mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 13:21:37 +00:00
Merge branch 'ethereum:master' into fix/get-logs-reverse-tags
This commit is contained in:
commit
e4e92eb30f
381 changed files with 20040 additions and 4285 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
|
@ -10,6 +10,7 @@ beacon/merkle/ @zsfelfoldi
|
|||
beacon/types/ @zsfelfoldi @fjl
|
||||
beacon/params/ @zsfelfoldi @fjl
|
||||
cmd/evm/ @MariusVanDerWijden @lightclient
|
||||
cmd/keeper/ @gballet
|
||||
core/state/ @rjl493456442
|
||||
crypto/ @gballet @jwasinger @fjl
|
||||
core/ @rjl493456442
|
||||
|
|
|
|||
4
.github/workflows/go.yml
vendored
4
.github/workflows/go.yml
vendored
|
|
@ -69,8 +69,8 @@ jobs:
|
|||
|
||||
- name: Install cross toolchain
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib
|
||||
sudo apt-get update
|
||||
sudo apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib
|
||||
|
||||
- name: Build
|
||||
run: go run build/ci.go test -arch 386 -short -p 8
|
||||
|
|
|
|||
102
AGENTS.md
Normal file
102
AGENTS.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# AGENTS
|
||||
|
||||
## Guidelines
|
||||
|
||||
- **Keep changes minimal and focused.** Only modify code directly related to the task at hand. Do not refactor unrelated code, rename existing variables or functions for style, or bundle unrelated fixes into the same commit or PR.
|
||||
- **Do not add, remove, or update dependencies** unless the task explicitly requires it.
|
||||
|
||||
## Pre-Commit Checklist
|
||||
|
||||
Before every commit, run **all** of the following checks and ensure they pass:
|
||||
|
||||
### 1. Formatting
|
||||
|
||||
Before committing, always run `gofmt` and `goimports` on all modified files:
|
||||
|
||||
```sh
|
||||
gofmt -w <modified files>
|
||||
goimports -w <modified files>
|
||||
```
|
||||
|
||||
### 2. Build All Commands
|
||||
|
||||
Verify that all tools compile successfully:
|
||||
|
||||
```sh
|
||||
make all
|
||||
```
|
||||
|
||||
This builds all executables under `cmd/`, including `keeper` which has special build requirements.
|
||||
|
||||
### 3. Tests
|
||||
|
||||
While iterating during development, use `-short` for faster feedback:
|
||||
|
||||
```sh
|
||||
go run ./build/ci.go test -short
|
||||
```
|
||||
|
||||
Before committing, run the full test suite **without** `-short` to ensure all tests pass, including the Ethereum execution-spec tests and all state/block test permutations:
|
||||
|
||||
```sh
|
||||
go run ./build/ci.go test
|
||||
```
|
||||
|
||||
### 4. Linting
|
||||
|
||||
```sh
|
||||
go run ./build/ci.go lint
|
||||
```
|
||||
|
||||
This runs additional style checks. Fix any issues before committing.
|
||||
|
||||
### 5. Generated Code
|
||||
|
||||
```sh
|
||||
go run ./build/ci.go check_generate
|
||||
```
|
||||
|
||||
Ensures that all generated files (e.g., `gen_*.go`) are up to date. If this fails, first install the required code generators by running `make devtools`, then run the appropriate `go generate` commands and include the updated files in your commit.
|
||||
|
||||
### 6. Dependency Hygiene
|
||||
|
||||
```sh
|
||||
go run ./build/ci.go check_baddeps
|
||||
```
|
||||
|
||||
Verifies that no forbidden dependencies have been introduced.
|
||||
|
||||
## What to include in commits
|
||||
|
||||
Do not commit binaries, whether they are produced by the main build or byproducts of investigations.
|
||||
|
||||
## Commit Message Format
|
||||
|
||||
Commit messages must be prefixed with the package(s) they modify, followed by a short lowercase description:
|
||||
|
||||
```
|
||||
<package(s)>: description
|
||||
```
|
||||
|
||||
Examples:
|
||||
- `core/vm: fix stack overflow in PUSH instruction`
|
||||
- `eth, rpc: make trace configs optional`
|
||||
- `cmd/geth: add new flag for sync mode`
|
||||
|
||||
Use comma-separated package names when multiple areas are affected. Keep the description concise.
|
||||
|
||||
## Pull Request Title Format
|
||||
|
||||
PR titles follow the same convention as commit messages:
|
||||
|
||||
```
|
||||
<list of modified paths>: description
|
||||
```
|
||||
|
||||
Examples:
|
||||
- `core/vm: fix stack overflow in PUSH instruction`
|
||||
- `core, eth: add arena allocator support`
|
||||
- `cmd/geth, internal/ethapi: refactor transaction args`
|
||||
- `trie/archiver: streaming subtree archival to fix OOM`
|
||||
|
||||
Use the top-level package paths, comma-separated if multiple areas are affected. Only mention the directories with functional changes, interface changes that trickle all over the codebase should not generate an exhaustive list. The description should be a short, lowercase summary of the change.
|
||||
|
|
@ -4,7 +4,7 @@ ARG VERSION=""
|
|||
ARG BUILDNUM=""
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.24-alpine AS builder
|
||||
FROM golang:1.26-alpine AS builder
|
||||
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ ARG VERSION=""
|
|||
ARG BUILDNUM=""
|
||||
|
||||
# Build Geth in a stock Go builder container
|
||||
FROM golang:1.24-alpine AS builder
|
||||
FROM golang:1.26-alpine AS builder
|
||||
|
||||
RUN apk add --no-cache gcc musl-dev linux-headers git
|
||||
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// Account represents an Ethereum account located at a specific location defined
|
||||
|
|
@ -196,7 +196,7 @@ func TextHash(data []byte) []byte {
|
|||
// This gives context to the signed message and prevents signing of transactions.
|
||||
func TextAndHash(data []byte) ([]byte, string) {
|
||||
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher := keccak.NewLegacyKeccak256()
|
||||
hasher.Write([]byte(msg))
|
||||
return hasher.Sum(nil), msg
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,14 @@ const refreshCycle = time.Second
|
|||
// trashing.
|
||||
const refreshThrottling = 500 * time.Millisecond
|
||||
|
||||
const (
|
||||
// deviceUsagePage identifies Ledger devices by HID usage page (0xffa0) on Windows and macOS.
|
||||
// See: https://github.com/LedgerHQ/ledger-live/blob/05a2980e838955a11a1418da638ef8ac3df4fb74/libs/ledgerjs/packages/hw-transport-node-hid-noevents/src/TransportNodeHid.ts
|
||||
deviceUsagePage = 0xffa0
|
||||
// deviceInterface identifies Ledger devices by USB interface number (0) on Linux.
|
||||
deviceInterface = 0
|
||||
)
|
||||
|
||||
// Hub is a accounts.Backend that can find and handle generic USB hardware wallets.
|
||||
type Hub struct {
|
||||
scheme string // Protocol scheme prefixing account and wallet URLs.
|
||||
|
|
@ -82,6 +90,7 @@ func NewLedgerHub() (*Hub, error) {
|
|||
0x0005, /* Ledger Nano S Plus */
|
||||
0x0006, /* Ledger Nano FTS */
|
||||
0x0007, /* Ledger Flex */
|
||||
0x0008, /* Ledger Nano Gen5 */
|
||||
|
||||
0x0000, /* WebUSB Ledger Blue */
|
||||
0x1000, /* WebUSB Ledger Nano S */
|
||||
|
|
@ -89,7 +98,8 @@ func NewLedgerHub() (*Hub, error) {
|
|||
0x5000, /* WebUSB Ledger Nano S Plus */
|
||||
0x6000, /* WebUSB Ledger Nano FTS */
|
||||
0x7000, /* WebUSB Ledger Flex */
|
||||
}, 0xffa0, 0, newLedgerDriver)
|
||||
0x8000, /* WebUSB Ledger Nano Gen5 */
|
||||
}, deviceUsagePage, deviceInterface, newLedgerDriver)
|
||||
}
|
||||
|
||||
// NewTrezorHubWithHID creates a new hardware wallet manager for Trezor devices.
|
||||
|
|
|
|||
|
|
@ -87,6 +87,10 @@ func (ec *engineClient) updateLoop(headCh <-chan types.ChainHeadEvent) {
|
|||
if status, err := ec.callForkchoiceUpdated(forkName, event); err == nil {
|
||||
log.Info("Successful ForkchoiceUpdated", "head", event.Block.Hash(), "status", status)
|
||||
} else {
|
||||
if err.Error() == "beacon syncer reorging" {
|
||||
log.Debug("Failed ForkchoiceUpdated", "head", event.Block.Hash(), "error", err)
|
||||
continue // ignore beacon syncer reorging errors, this error can occur if the blsync is skipping a block
|
||||
}
|
||||
log.Error("Failed ForkchoiceUpdated", "head", event.Block.Hash(), "error", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
|
|||
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
SlotNumber *hexutil.Uint64 `json:"slotNumber"`
|
||||
}
|
||||
var enc PayloadAttributes
|
||||
enc.Timestamp = hexutil.Uint64(p.Timestamp)
|
||||
|
|
@ -28,6 +29,7 @@ func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
|
|||
enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient
|
||||
enc.Withdrawals = p.Withdrawals
|
||||
enc.BeaconRoot = p.BeaconRoot
|
||||
enc.SlotNumber = (*hexutil.Uint64)(p.SlotNumber)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +41,7 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
|
|||
SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
SlotNumber *hexutil.Uint64 `json:"slotNumber"`
|
||||
}
|
||||
var dec PayloadAttributes
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -62,5 +65,8 @@ func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
|
|||
if dec.BeaconRoot != nil {
|
||||
p.BeaconRoot = dec.BeaconRoot
|
||||
}
|
||||
if dec.SlotNumber != nil {
|
||||
p.SlotNumber = (*uint64)(dec.SlotNumber)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
|||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||
SlotNumber *hexutil.Uint64 `json:"slotNumber"`
|
||||
}
|
||||
var enc ExecutableData
|
||||
enc.ParentHash = e.ParentHash
|
||||
|
|
@ -58,6 +59,7 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
|||
enc.Withdrawals = e.Withdrawals
|
||||
enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
|
||||
enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
|
||||
enc.SlotNumber = (*hexutil.Uint64)(e.SlotNumber)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
|
|
@ -81,6 +83,7 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
|||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||
SlotNumber *hexutil.Uint64 `json:"slotNumber"`
|
||||
}
|
||||
var dec ExecutableData
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -154,5 +157,8 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
|||
if dec.ExcessBlobGas != nil {
|
||||
e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
||||
}
|
||||
if dec.SlotNumber != nil {
|
||||
e.SlotNumber = (*uint64)(dec.SlotNumber)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,13 @@ var (
|
|||
// ExecutionPayloadV3 has the syntax of ExecutionPayloadV2 and appends the new
|
||||
// fields: blobGasUsed and excessBlobGas.
|
||||
PayloadV3 PayloadVersion = 0x3
|
||||
|
||||
// PayloadV4 is the identifier of ExecutionPayloadV4 introduced in amsterdam fork.
|
||||
//
|
||||
// https://github.com/ethereum/execution-apis/blob/main/src/engine/amsterdam.md#executionpayloadv4
|
||||
// ExecutionPayloadV4 has the syntax of ExecutionPayloadV3 and appends the new
|
||||
// field slotNumber.
|
||||
PayloadV4 PayloadVersion = 0x4
|
||||
)
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go
|
||||
|
|
@ -62,11 +69,13 @@ type PayloadAttributes struct {
|
|||
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BeaconRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
SlotNumber *uint64 `json:"slotNumber"`
|
||||
}
|
||||
|
||||
// JSON type overrides for PayloadAttributes.
|
||||
type payloadAttributesMarshaling struct {
|
||||
Timestamp hexutil.Uint64
|
||||
Timestamp hexutil.Uint64
|
||||
SlotNumber *hexutil.Uint64
|
||||
}
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go
|
||||
|
|
@ -90,6 +99,7 @@ type ExecutableData struct {
|
|||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||
SlotNumber *uint64 `json:"slotNumber"`
|
||||
}
|
||||
|
||||
// JSON type overrides for executableData.
|
||||
|
|
@ -104,6 +114,7 @@ type executableDataMarshaling struct {
|
|||
Transactions []hexutil.Bytes
|
||||
BlobGasUsed *hexutil.Uint64
|
||||
ExcessBlobGas *hexutil.Uint64
|
||||
SlotNumber *hexutil.Uint64
|
||||
}
|
||||
|
||||
// StatelessPayloadStatusV1 is the result of a stateless payload execution.
|
||||
|
|
@ -213,7 +224,7 @@ func encodeTransactions(txs []*types.Transaction) [][]byte {
|
|||
return enc
|
||||
}
|
||||
|
||||
func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
||||
func DecodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
||||
var txs = make([]*types.Transaction, len(enc))
|
||||
for i, encTx := range enc {
|
||||
var tx types.Transaction
|
||||
|
|
@ -251,7 +262,7 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b
|
|||
// for stateless execution, so it skips checking if the executable data hashes to
|
||||
// the requested hash (stateless has to *compute* the root hash, it's not given).
|
||||
func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash, requests [][]byte) (*types.Block, error) {
|
||||
txs, err := decodeTransactions(data.Transactions)
|
||||
txs, err := DecodeTransactions(data.Transactions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -313,6 +324,7 @@ func ExecutableDataToBlockNoHash(data ExecutableData, versionedHashes []common.H
|
|||
BlobGasUsed: data.BlobGasUsed,
|
||||
ParentBeaconRoot: beaconRoot,
|
||||
RequestsHash: requestsHash,
|
||||
SlotNumber: data.SlotNumber,
|
||||
}
|
||||
return types.NewBlockWithHeader(header).
|
||||
WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals}),
|
||||
|
|
@ -340,6 +352,7 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
|
|||
Withdrawals: block.Withdrawals(),
|
||||
BlobGasUsed: block.BlobGasUsed(),
|
||||
ExcessBlobGas: block.ExcessBlobGas(),
|
||||
SlotNumber: block.SlotNumber(),
|
||||
}
|
||||
|
||||
// Add blobs.
|
||||
|
|
|
|||
|
|
@ -5,81 +5,102 @@
|
|||
# https://github.com/ethereum/execution-spec-tests/releases/download/v5.1.0
|
||||
a3192784375acec7eaec492799d5c5d0c47a2909a3cc40178898e4ecd20cc416 fixtures_develop.tar.gz
|
||||
|
||||
# version:golang 1.25.1
|
||||
# version:golang 1.25.7
|
||||
# https://go.dev/dl/
|
||||
d010c109cee94d80efe681eab46bdea491ac906bf46583c32e9f0dbb0bd1a594 go1.25.1.src.tar.gz
|
||||
1d622468f767a1b9fe1e1e67bd6ce6744d04e0c68712adc689748bbeccb126bb go1.25.1.darwin-amd64.tar.gz
|
||||
68deebb214f39d542e518ebb0598a406ab1b5a22bba8ec9ade9f55fb4dd94a6c go1.25.1.darwin-arm64.tar.gz
|
||||
d03cdcbc9bd8baf5cf028de390478e9e2b3e4d0afe5a6582dedc19bfe6a263b2 go1.25.1.linux-386.tar.gz
|
||||
7716a0d940a0f6ae8e1f3b3f4f36299dc53e31b16840dbd171254312c41ca12e go1.25.1.linux-amd64.tar.gz
|
||||
65a3e34fb2126f55b34e1edfc709121660e1be2dee6bdf405fc399a63a95a87d go1.25.1.linux-arm64.tar.gz
|
||||
eb949be683e82a99e9861dafd7057e31ea40b161eae6c4cd18fdc0e8c4ae6225 go1.25.1.linux-armv6l.tar.gz
|
||||
be13d5479b8c75438f2efcaa8c191fba3af684b3228abc9c99c7aa8502f34424 go1.25.1.windows-386.zip
|
||||
4a974de310e7ee1d523d2fcedb114ba5fa75408c98eb3652023e55ccf3fa7cab go1.25.1.windows-amd64.zip
|
||||
45ab4290adbd6ee9e7f18f0d57eaa9008fdbef590882778ed93eac3c8cca06c5 go1.25.1.aix-ppc64.tar.gz
|
||||
2e3c1549bed3124763774d648f291ac42611232f48320ebbd23517c909c09b81 go1.25.1.dragonfly-amd64.tar.gz
|
||||
dc0198dd4ec520e13f26798def8750544edf6448d8e9c43fd2a814e4885932af go1.25.1.freebsd-386.tar.gz
|
||||
c4f1a7e7b258406e6f3b677ecdbd97bbb23ff9c0d44be4eb238a07d360f69ac8 go1.25.1.freebsd-amd64.tar.gz
|
||||
7772fc5ff71ed39297ec0c1599fc54e399642c9b848eac989601040923b0de9c go1.25.1.freebsd-arm.tar.gz
|
||||
5bb011d5d5b6218b12189f07aa0be618ab2002662fff1ca40afba7389735c207 go1.25.1.freebsd-arm64.tar.gz
|
||||
ccac716240cb049bebfafcb7eebc3758512178a4c51fc26da9cc032035d850c8 go1.25.1.freebsd-riscv64.tar.gz
|
||||
cc53910ffb9fcfdd988a9fa25b5423bae1cfa01b19616be646700e1f5453b466 go1.25.1.illumos-amd64.tar.gz
|
||||
efe809f923bcedab44bf7be2b3af8d182b512b1bf9c07d302e0c45d26c8f56f3 go1.25.1.linux-loong64.tar.gz
|
||||
c0de33679f6ed68991dc42dc4a602e74a666e3e166c1748ee1b5d1a7ea2ffbb2 go1.25.1.linux-mips.tar.gz
|
||||
c270f7b0c0bdfbcd54fef4481227c40d41bb518f9ae38ee930870f04a0a6a589 go1.25.1.linux-mips64.tar.gz
|
||||
80be871ba9c944f34d1868cdf5047e1cf2e1289fe08cdb90e2453d2f0d6965ae go1.25.1.linux-mips64le.tar.gz
|
||||
9f09defa9bb22ebf2cde76162f40958564e57ce5c2b3649bc063bebcbc9294c1 go1.25.1.linux-mipsle.tar.gz
|
||||
2c76b7d278c1d43ad19d478ad3f0f05e7b782b64b90870701b314fa48b5f43c6 go1.25.1.linux-ppc64.tar.gz
|
||||
8b0c8d3ee5b1b5c28b6bd63dc4438792012e01d03b4bf7a61d985c87edab7d1f go1.25.1.linux-ppc64le.tar.gz
|
||||
22fe934a9d0c9c57275716c55b92d46ebd887cec3177c9140705efa9f84ba1e2 go1.25.1.linux-riscv64.tar.gz
|
||||
9cfe517ba423f59f3738ca5c3d907c103253cffbbcc2987142f79c5de8c1bf93 go1.25.1.linux-s390x.tar.gz
|
||||
6af8a08353e76205d5b743dd7a3f0126684f96f62be0a31b75daf9837e512c46 go1.25.1.netbsd-386.tar.gz
|
||||
e5d534ff362edb1bd8c8e10892b6a027c4c1482454245d1529167676498684c7 go1.25.1.netbsd-amd64.tar.gz
|
||||
88bcf39254fdcea6a199c1c27d787831b652427ce60851ae9e41a3d7eb477f45 go1.25.1.netbsd-arm.tar.gz
|
||||
d7c2eabe1d04ee47bcaea2816fdd90dbd25d90d4dfa756faa9786c788e4f3a4e go1.25.1.netbsd-arm64.tar.gz
|
||||
14a2845977eb4dde11d929858c437a043467c427db87899935e90cee04a38d72 go1.25.1.openbsd-386.tar.gz
|
||||
d27ac54b38a13a09c81e67c82ac70d387037341c85c3399291c73e13e83fdd8c go1.25.1.openbsd-amd64.tar.gz
|
||||
0f4ab5f02500afa4befd51fed1e8b45e4d07ca050f641cc3acc76eaa4027b2c3 go1.25.1.openbsd-arm.tar.gz
|
||||
d46c3bd156843656f7f3cb0dec27ea51cd926ec3f7b80744bf8156e67c1c812f go1.25.1.openbsd-arm64.tar.gz
|
||||
c550514c67f22e409be10e40eace761e2e43069f4ef086ae6e60aac736c2b679 go1.25.1.openbsd-ppc64.tar.gz
|
||||
8a09a8714a2556eb13fc1f10b7ce2553fcea4971e3330fc3be0efd24aab45734 go1.25.1.openbsd-riscv64.tar.gz
|
||||
b0e1fefaf0c7abd71f139a54eee9767944aff5f0bc9d69c968234804884e552f go1.25.1.plan9-386.tar.gz
|
||||
e94732c94f149690aa0ab11c26090577211b4a988137cb2c03ec0b54e750402e go1.25.1.plan9-amd64.tar.gz
|
||||
7eb80e9de1e817d9089a54e8c7c5c8d8ed9e5fb4d4a012fc0f18fc422a484f0c go1.25.1.plan9-arm.tar.gz
|
||||
1261dfad7c4953c0ab90381bc1242dc54e394db7485c59349428d532b2273343 go1.25.1.solaris-amd64.tar.gz
|
||||
04bc3c078e9e904c4d58d6ac2532a5bdd402bd36a9ff0b5949b3c5e6006a05ee go1.25.1.windows-arm64.zip
|
||||
178f2832820274b43e177d32f06a3ebb0129e427dd20a5e4c88df2c1763cf10a go1.25.7.src.tar.gz
|
||||
81bf2a1f20633f62d55d826d82dde3b0570cf1408a91e15781b266037299285b go1.25.7.aix-ppc64.tar.gz
|
||||
bf5050a2152f4053837b886e8d9640c829dbacbc3370f913351eb0904cb706f5 go1.25.7.darwin-amd64.tar.gz
|
||||
ff18369ffad05c57d5bed888b660b31385f3c913670a83ef557cdfd98ea9ae1b go1.25.7.darwin-arm64.tar.gz
|
||||
c5dccd7f192dd7b305dc209fb316ac1917776d74bd8e4d532ef2772f305bf42a go1.25.7.dragonfly-amd64.tar.gz
|
||||
a2de97c8ac74bf64b0ae73fe9d379e61af530e061bc7f8f825044172ffe61a8b go1.25.7.freebsd-386.tar.gz
|
||||
055f9e138787dcafa81eb0314c8ff70c6dd0f6dba1e8a6957fef5d5efd1ab8fd go1.25.7.freebsd-amd64.tar.gz
|
||||
60e7f7a7c990f0b9539ac8ed668155746997d404643a4eecd47b3dee1b7e710b go1.25.7.freebsd-arm.tar.gz
|
||||
631e03d5fd4c526e2f499154d8c6bf4cb081afb2fff171c428722afc9539d53a go1.25.7.freebsd-arm64.tar.gz
|
||||
8a264fd685823808140672812e3ad9c43f6ad59444c0dc14cdd3a1351839ddd5 go1.25.7.freebsd-riscv64.tar.gz
|
||||
57c672447d906a1bcab98f2b11492d54521a791aacbb4994a25169e59cbe289a go1.25.7.illumos-amd64.tar.gz
|
||||
2866517e9ca81e6a2e85a930e9b11bc8a05cfeb2fc6dc6cb2765e7fb3c14b715 go1.25.7.linux-386.tar.gz
|
||||
12e6d6a191091ae27dc31f6efc630e3a3b8ba409baf3573d955b196fdf086005 go1.25.7.linux-amd64.tar.gz
|
||||
ba611a53534135a81067240eff9508cd7e256c560edd5d8c2fef54f083c07129 go1.25.7.linux-arm64.tar.gz
|
||||
1ba07e0eb86b839e72467f4b5c7a5597d07f30bcf5563c951410454f7cda5266 go1.25.7.linux-armv6l.tar.gz
|
||||
775753fc5952a334c415f08768df2f0b73a3228a16e8f5f63d545daacb4e3357 go1.25.7.linux-loong64.tar.gz
|
||||
1a023bb367c5fbb4c637a2f6dc23ff17c6591ad929ce16ea88c74d857153b307 go1.25.7.linux-mips.tar.gz
|
||||
a8e97223d8aa6fdfd45f132a4784d2f536bbac5f3d63a24b63d33b6bfe1549af go1.25.7.linux-mips64.tar.gz
|
||||
eb9edb6223330d5e20275667c65dea076b064c08e595fe4eba5d7d6055cfaccf go1.25.7.linux-mips64le.tar.gz
|
||||
9c1e693552a5f9bb9e0012d1c5e01456ecefbc59bef53a77305222ce10aba368 go1.25.7.linux-mipsle.tar.gz
|
||||
28a788798e7329acbbc0ac2caa5e4368b1e5ede646cc24429c991214cfb45c63 go1.25.7.linux-ppc64.tar.gz
|
||||
42124c0edc92464e2b37b2d7fcd3658f0c47ebd6a098732415a522be8cb88e3f go1.25.7.linux-ppc64le.tar.gz
|
||||
88d59c6893c8425875d6eef8e3434bc2fa2552e5ad4c058c6cd8cd710a0301c8 go1.25.7.linux-riscv64.tar.gz
|
||||
c6b77facf666dc68195ecab05dbf0ebb4e755b2a8b7734c759880557f1c29b0c go1.25.7.linux-s390x.tar.gz
|
||||
f14c184d9ade0ee04c7735d4071257b90896ecbde1b32adae84135f055e6399b go1.25.7.netbsd-386.tar.gz
|
||||
7e7389e404dca1088c31f0fc07f1dd60891d7182bcd621469c14f7e79eceb3ff go1.25.7.netbsd-amd64.tar.gz
|
||||
70388bb3ef2f03dbf1357e9056bd09034a67e018262557354f8cf549766b3f9d go1.25.7.netbsd-arm.tar.gz
|
||||
8c1cda9d25bfc9b18d24d5f95fc23949dd3ff99fa408a6cfa40e2cf12b07e362 go1.25.7.netbsd-arm64.tar.gz
|
||||
42f0d1bfbe39b8401cccb84dd66b30795b97bfc9620dfdc17c5cd4fcf6495cb0 go1.25.7.openbsd-386.tar.gz
|
||||
e514879c0a28bc32123cd52c4c093de912477fe83f36a6d07517d066ef55391a go1.25.7.openbsd-amd64.tar.gz
|
||||
8cd22530695a0218232bf7efea8f162df1697a3106942ac4129b8c3de39ce4ef go1.25.7.openbsd-arm.tar.gz
|
||||
938720f6ebc0d1c53d7840321d3a31f29fd02496e84a6538f442a9311dc1cc9a go1.25.7.openbsd-arm64.tar.gz
|
||||
a4c378b73b98f89a3596c2ef51aabbb28783d9ca29f7e317d8ca07939660ce6f go1.25.7.openbsd-ppc64.tar.gz
|
||||
937b58734fbeaa8c7941a0e4285e7e84b7885396e8d11c23f9ab1a8ff10ff20e go1.25.7.openbsd-riscv64.tar.gz
|
||||
61a093c8c5244916f25740316386bb9f141545dcf01b06a79d1c78ece488403e go1.25.7.plan9-386.tar.gz
|
||||
7fc8f6689c9de8ccb7689d2278035fa83c2d601409101840df6ddfe09ba58699 go1.25.7.plan9-amd64.tar.gz
|
||||
9661dff8eaeeb62f1c3aadbc5ff189a2e6744e1ec885e32dbcb438f58a34def5 go1.25.7.plan9-arm.tar.gz
|
||||
28ecba0e1d7950c8b29a4a04962dd49c3bf5221f55a44f17d98f369f82859cf4 go1.25.7.solaris-amd64.tar.gz
|
||||
baa6b488291801642fa620026169e38bec2da2ac187cd3ae2145721cf826bbc3 go1.25.7.windows-386.zip
|
||||
c75e5f4ff62d085cc0017be3ad19d5536f46825fa05db06ec468941f847e3228 go1.25.7.windows-amd64.zip
|
||||
807033f85931bc4a589ca8497535dcbeb1f30d506e47fa200f5f04c4a71c3d9f go1.25.7.windows-arm64.zip
|
||||
|
||||
# version:golangci 2.4.0
|
||||
# version:golangci 2.10.1
|
||||
# https://github.com/golangci/golangci-lint/releases/
|
||||
# https://github.com/golangci/golangci-lint/releases/download/v2.4.0/
|
||||
7904ce63f79db44934939cf7a063086ea0ea98e9b19eba0a9d52ccdd0d21951c golangci-lint-2.4.0-darwin-amd64.tar.gz
|
||||
cd4dd53fa09b6646baff5fd22b8c64d91db02c21c7496df27992d75d34feec59 golangci-lint-2.4.0-darwin-arm64.tar.gz
|
||||
d58f426ebe14cc257e81562b4bf37a488ffb4ffbbb3ec73041eb3b38bb25c0e1 golangci-lint-2.4.0-freebsd-386.tar.gz
|
||||
6ec4a6177fc6c0dd541fbcb3a7612845266d020d35cc6fa92959220cdf64ca39 golangci-lint-2.4.0-freebsd-amd64.tar.gz
|
||||
4d473e3e71c01feaa915a0604fb35758b41284fb976cdeac3f842118d9ee7e17 golangci-lint-2.4.0-freebsd-armv6.tar.gz
|
||||
58727746c6530801a3f9a702a5945556a5eb7e88809222536dd9f9d54cafaeff golangci-lint-2.4.0-freebsd-armv7.tar.gz
|
||||
fbf28c662760e24c32f82f8d16dffdb4a82de7726a52ba1fad94f890c22997ea golangci-lint-2.4.0-illumos-amd64.tar.gz
|
||||
a15a000a8981ef665e971e0f67e2acda9066a9e37a59344393b7351d8fb49c81 golangci-lint-2.4.0-linux-386.tar.gz
|
||||
fae792524c04424c0ac369f5b8076f04b45cf29fc945a370e55d369a8dc11840 golangci-lint-2.4.0-linux-amd64.tar.gz
|
||||
70ac11f55b80ec78fd3a879249cc9255121b8dfd7f7ed4fc46ed137f4abf17e7 golangci-lint-2.4.0-linux-arm64.tar.gz
|
||||
4acdc40e5cebe99e4e7ced358a05b2e71789f409b41cb4f39bbb86ccfa14b1dc golangci-lint-2.4.0-linux-armv6.tar.gz
|
||||
2a68749568fa22b4a97cb88dbea655595563c795076536aa6c087f7968784bf3 golangci-lint-2.4.0-linux-armv7.tar.gz
|
||||
9e3369afb023711036dcb0b4f45c9fe2792af962fa1df050c9f6ac101a6c5d73 golangci-lint-2.4.0-linux-loong64.tar.gz
|
||||
bb9143d6329be2c4dbfffef9564078e7da7d88e7dde6c829b6263d98e072229e golangci-lint-2.4.0-linux-mips64.tar.gz
|
||||
5ad1765b40d56cd04d4afd805b3ba6f4bfd9b36181da93c31e9b17e483d8608d golangci-lint-2.4.0-linux-mips64le.tar.gz
|
||||
918936fb9c0d5ba96bef03cf4348b03938634cfcced49be1e9bb29cb5094fa73 golangci-lint-2.4.0-linux-ppc64le.tar.gz
|
||||
f7474c638e1fb67ebbdc654b55ca0125377ea0bc88e8fee8d964a4f24eacf828 golangci-lint-2.4.0-linux-riscv64.tar.gz
|
||||
b617a9543997c8bfceaffa88a75d4e595030c6add69fba800c1e4d8f5fe253dd golangci-lint-2.4.0-linux-s390x.tar.gz
|
||||
7db027b03a9ba328f795215b04f594036837bc7dd0dd7cd16776b02a6167981c golangci-lint-2.4.0-netbsd-386.tar.gz
|
||||
52d8f9393f4313df0a62b752c37775e3af0b818e43e8dd28954351542d7c60bc golangci-lint-2.4.0-netbsd-amd64.tar.gz
|
||||
5c0086027fb5a4af3829e530c8115db4b35d11afe1914322eef528eb8cd38c69 golangci-lint-2.4.0-netbsd-arm64.tar.gz
|
||||
6b779d6ed1aed87cefe195cc11759902b97a76551b593312c6833f2635a3488f golangci-lint-2.4.0-netbsd-armv6.tar.gz
|
||||
f00d1f4b7ec3468a0f9fffd0d9ea036248b029b7621cbc9a59c449ef94356d09 golangci-lint-2.4.0-netbsd-armv7.tar.gz
|
||||
3ce671b0b42b58e35066493aab75a7e2826c9e079988f1ba5d814a4029faaf87 golangci-lint-2.4.0-windows-386.zip
|
||||
003112f7a56746feaabf20b744054bf9acdf900c9e77176383623c4b1d76aaa9 golangci-lint-2.4.0-windows-amd64.zip
|
||||
dc0c2092af5d47fc2cd31a1dfe7b4c7e765fab22de98bd21ef2ffcc53ad9f54f golangci-lint-2.4.0-windows-arm64.zip
|
||||
0263d23e20a260cb1592d35e12a388f99efe2c51b3611fdc66fbd9db1fce664d golangci-lint-2.4.0-windows-armv6.zip
|
||||
9403c03bf648e6313036e0273149d44bad1b9ad53889b6d00e4ccb842ba3c058 golangci-lint-2.4.0-windows-armv7.zip
|
||||
# https://github.com/golangci/golangci-lint/releases/download/v2.10.1
|
||||
66fb0da81b8033b477f97eea420d4b46b230ca172b8bb87c6610109f3772b6b6 golangci-lint-2.10.1-darwin-amd64.tar.gz
|
||||
03bfadf67e52b441b7ec21305e501c717df93c959836d66c7f97312654acb297 golangci-lint-2.10.1-darwin-arm64.tar.gz
|
||||
c9a44658ccc8f7b8dbbd4ae6020ba91c1a5d3987f4d91ced0f7d2bea013e57ca golangci-lint-2.10.1-freebsd-386.tar.gz
|
||||
a513c5cb4e0f5bd5767001af9d5e97e7868cfc2d9c46739a4df93e713cfb24af golangci-lint-2.10.1-freebsd-amd64.tar.gz
|
||||
2ef38eefc4b5cee2febacb75a30579526e5656c16338a921d80e59a8e87d4425 golangci-lint-2.10.1-freebsd-arm64.tar.gz
|
||||
8fea6766318b4829e766bbe325f10191d75297dcc44ae35bf374816037878e38 golangci-lint-2.10.1-freebsd-armv6.tar.gz
|
||||
30b629870574d6254f3e8804e5a74b34f98e1263c9d55465830d739c88b862ed golangci-lint-2.10.1-freebsd-armv7.tar.gz
|
||||
c0db839f866ce80b1b6c96167aa101cfe50d9c936f42d942a3c1cbdc1801af68 golangci-lint-2.10.1-illumos-amd64.tar.gz
|
||||
280eb56636e9175f671cd7b755d7d67f628ae2ed00a164d1e443c43c112034e5 golangci-lint-2.10.1-linux-386.deb
|
||||
065a7d99da61dc7dfbfef2e2d7053dd3fa6672598f2747117aa4bb5f45e7df7f golangci-lint-2.10.1-linux-386.rpm
|
||||
a55918c03bb413b2662287653ab2ae2fef4e37428b247dad6348724adde9d770 golangci-lint-2.10.1-linux-386.tar.gz
|
||||
8aa9b3aa14f39745eeb7fc7ff50bcac683e785397d1e4bc9afd2184b12c4ce86 golangci-lint-2.10.1-linux-amd64.deb
|
||||
62a111688e9e305032334a2cbc84f4d971b64bb3bffc99d3f80081d57fb25e32 golangci-lint-2.10.1-linux-amd64.rpm
|
||||
dfa775874cf0561b404a02a8f4481fc69b28091da95aa697259820d429b09c99 golangci-lint-2.10.1-linux-amd64.tar.gz
|
||||
b3f36937e8ea1660739dc0f5c892ea59c9c21ed4e75a91a25957c561f7f79a55 golangci-lint-2.10.1-linux-arm64.deb
|
||||
36d50314d53683b1f1a2a6cedfb5a9468451b481c64ab9e97a8e843ea088074d golangci-lint-2.10.1-linux-arm64.rpm
|
||||
6652b42ae02915eb2f9cb2a2e0cac99514c8eded8388d88ae3e06e1a52c00de8 golangci-lint-2.10.1-linux-arm64.tar.gz
|
||||
a32d8d318e803496812dd3461f250e52ccc7f53c47b95ce404a9cf55778ceb6a golangci-lint-2.10.1-linux-armv6.deb
|
||||
41d065f4c8ea165a1531abea644988ee2e973e4f0b49f9725ed3b979dac45112 golangci-lint-2.10.1-linux-armv6.rpm
|
||||
59159a4df03aabbde69d15c7b7b3df143363cbb41f4bd4b200caffb8e34fb734 golangci-lint-2.10.1-linux-armv6.tar.gz
|
||||
b2e8ec0e050a1e2251dfe1561434999d202f5a3f9fa47ce94378b0fd1662ea5a golangci-lint-2.10.1-linux-armv7.deb
|
||||
28c9331429a497da27e9c77846063bd0e8275e878ffedb4eb9e9f21d24771cc0 golangci-lint-2.10.1-linux-armv7.rpm
|
||||
818f33e95b273e3769284b25563b51ef6a294e9e25acf140fda5830c075a1a59 golangci-lint-2.10.1-linux-armv7.tar.gz
|
||||
6b6b85ed4b7c27f51097dd681523000409dde835e86e6e314e87be4bb013e2ab golangci-lint-2.10.1-linux-loong64.deb
|
||||
94050a0cf06169e2ae44afb307dcaafa7d7c3b38c0c23b5652cf9cb60f0c337f golangci-lint-2.10.1-linux-loong64.rpm
|
||||
25820300fccb8c961c1cdcb1f77928040c079e04c43a3a5ceb34b1cb4a1c5c8d golangci-lint-2.10.1-linux-loong64.tar.gz
|
||||
98bf39d10139fdcaa37f94950e9bbb8888660ae468847ae0bf1cb5bf67c1f68b golangci-lint-2.10.1-linux-mips64.deb
|
||||
df3ce5f03808dcceaa8b683d1d06e95c885f09b59dc8e15deb840fbe2b3e3299 golangci-lint-2.10.1-linux-mips64.rpm
|
||||
972508dda523067e6e6a1c8e6609d63bc7c4153819c11b947d439235cf17bac2 golangci-lint-2.10.1-linux-mips64.tar.gz
|
||||
1d37f2919e183b5bf8b1777ed8c4b163d3b491d0158355a7999d647655cbbeb6 golangci-lint-2.10.1-linux-mips64le.deb
|
||||
e341d031002cd09a416329ed40f674231051a38544b8f94deb2d1708ce1f4a6f golangci-lint-2.10.1-linux-mips64le.rpm
|
||||
393560122b9cb5538df0c357d30eb27b6ee563533fbb9b138c8db4fd264002af golangci-lint-2.10.1-linux-mips64le.tar.gz
|
||||
21ca46b6a96442e8957677a3ca059c6b93674a68a01b1c71f4e5df0ea2e96d19 golangci-lint-2.10.1-linux-ppc64le.deb
|
||||
57fe0cbca0a9bbdf1547c5e8aa7d278e6896b438d72a541bae6bc62c38b43d1e golangci-lint-2.10.1-linux-ppc64le.rpm
|
||||
e2883db9fa51584e5e203c64456f29993550a7faadc84e3faccdb48f0669992e golangci-lint-2.10.1-linux-ppc64le.tar.gz
|
||||
aa6da0e98ab0ba3bb7582e112174c349907d5edfeff90a551dca3c6eecf92fc0 golangci-lint-2.10.1-linux-riscv64.deb
|
||||
3c68d76cd884a7aad206223a980b9c20bb9ea74b560fa27ed02baf2389189234 golangci-lint-2.10.1-linux-riscv64.rpm
|
||||
3bca11bfac4197205639cbd4676a5415054e629ac6c12ea10fcbe33ef852d9c3 golangci-lint-2.10.1-linux-riscv64.tar.gz
|
||||
0c6aed2ce49db2586adbac72c80d871f06feb1caf4c0763a5ca98fec809a8f0b golangci-lint-2.10.1-linux-s390x.deb
|
||||
16c285adfe1061d69dd8e503be69f87c7202857c6f4add74ac02e3571158fbec golangci-lint-2.10.1-linux-s390x.rpm
|
||||
21011ad368eb04f024201b832095c6b5f96d0888de194cca5bfe4d9307d6364b golangci-lint-2.10.1-linux-s390x.tar.gz
|
||||
7b5191e77a70485918712e31ed55159956323e4911bab1b67569c9d86e1b75eb golangci-lint-2.10.1-netbsd-386.tar.gz
|
||||
07801fd38d293ebad10826f8285525a39ea91ce5ddad77d05bfa90bda9c884a9 golangci-lint-2.10.1-netbsd-amd64.tar.gz
|
||||
7e7219d71c1bf33b98c328c93dc0560706dd896a1c43c44696e5222fc9d7446e golangci-lint-2.10.1-netbsd-arm64.tar.gz
|
||||
92fbc90b9eec0e572269b0f5492a2895c426b086a68372fde49b7e4d4020863e golangci-lint-2.10.1-netbsd-armv6.tar.gz
|
||||
f67b3ae1f47caeefa507a4ebb0c8336958a19011fe48766443212030f75d004b golangci-lint-2.10.1-netbsd-armv7.tar.gz
|
||||
a40bc091c10cea84eaee1a90b84b65f5e8652113b0a600bb099e4e4d9d7caddb golangci-lint-2.10.1-windows-386.zip
|
||||
c60c87695e79db8e320f0e5be885059859de52bb5ee5f11be5577828570bc2a3 golangci-lint-2.10.1-windows-amd64.zip
|
||||
636ab790c8dcea8034aa34aba6031ca3893d68f7eda000460ab534341fadbab1 golangci-lint-2.10.1-windows-arm64.zip
|
||||
|
||||
# This is the builder on PPA that will build Go itself (inception-y), don't modify!
|
||||
#
|
||||
|
|
|
|||
|
|
@ -168,8 +168,6 @@ var (
|
|||
"focal", // 20.04, EOL: 04/2030
|
||||
"jammy", // 22.04, EOL: 04/2032
|
||||
"noble", // 24.04, EOL: 04/2034
|
||||
"oracular", // 24.10, EOL: 07/2025
|
||||
"plucky", // 25.04, EOL: 01/2026
|
||||
}
|
||||
|
||||
// This is where the tests should be unpacked.
|
||||
|
|
@ -1198,7 +1196,7 @@ func doWindowsInstaller(cmdline []string) {
|
|||
var (
|
||||
arch = flag.String("arch", runtime.GOARCH, "Architecture for cross build packaging")
|
||||
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. WINDOWS_SIGNING_KEY)`)
|
||||
signify = flag.String("signify key", "", `Environment variable holding the signify signing key (e.g. WINDOWS_SIGNIFY_KEY)`)
|
||||
signify = flag.String("signify", "", `Environment variable holding the signify signing key (e.g. WINDOWS_SIGNIFY_KEY)`)
|
||||
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
|
||||
workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ func (c *Conn) ReadEth() (any, error) {
|
|||
var msg any
|
||||
switch int(code) {
|
||||
case eth.StatusMsg:
|
||||
msg = new(eth.StatusPacket69)
|
||||
msg = new(eth.StatusPacket)
|
||||
case eth.GetBlockHeadersMsg:
|
||||
msg = new(eth.GetBlockHeadersPacket)
|
||||
case eth.BlockHeadersMsg:
|
||||
|
|
@ -164,10 +164,6 @@ func (c *Conn) ReadEth() (any, error) {
|
|||
msg = new(eth.GetBlockBodiesPacket)
|
||||
case eth.BlockBodiesMsg:
|
||||
msg = new(eth.BlockBodiesPacket)
|
||||
case eth.NewBlockMsg:
|
||||
msg = new(eth.NewBlockPacket)
|
||||
case eth.NewBlockHashesMsg:
|
||||
msg = new(eth.NewBlockHashesPacket)
|
||||
case eth.TransactionsMsg:
|
||||
msg = new(eth.TransactionsPacket)
|
||||
case eth.NewPooledTransactionHashesMsg:
|
||||
|
|
@ -229,7 +225,7 @@ func (c *Conn) ReadSnap() (any, error) {
|
|||
}
|
||||
|
||||
// dialAndPeer creates a peer connection and runs the handshake.
|
||||
func (s *Suite) dialAndPeer(status *eth.StatusPacket69) (*Conn, error) {
|
||||
func (s *Suite) dialAndPeer(status *eth.StatusPacket) (*Conn, error) {
|
||||
c, err := s.dial()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -242,7 +238,7 @@ func (s *Suite) dialAndPeer(status *eth.StatusPacket69) (*Conn, error) {
|
|||
|
||||
// peer performs both the protocol handshake and the status message
|
||||
// exchange with the node in order to peer with it.
|
||||
func (c *Conn) peer(chain *Chain, status *eth.StatusPacket69) error {
|
||||
func (c *Conn) peer(chain *Chain, status *eth.StatusPacket) error {
|
||||
if err := c.handshake(); err != nil {
|
||||
return fmt.Errorf("handshake failed: %v", err)
|
||||
}
|
||||
|
|
@ -315,7 +311,7 @@ func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) {
|
|||
}
|
||||
|
||||
// statusExchange performs a `Status` message exchange with the given node.
|
||||
func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket69) error {
|
||||
func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket) error {
|
||||
loop:
|
||||
for {
|
||||
code, data, err := c.Read()
|
||||
|
|
@ -324,7 +320,7 @@ loop:
|
|||
}
|
||||
switch code {
|
||||
case eth.StatusMsg + protoOffset(ethProto):
|
||||
msg := new(eth.StatusPacket69)
|
||||
msg := new(eth.StatusPacket)
|
||||
if err := rlp.DecodeBytes(data, &msg); err != nil {
|
||||
return fmt.Errorf("error decoding status packet: %w", err)
|
||||
}
|
||||
|
|
@ -363,7 +359,7 @@ loop:
|
|||
}
|
||||
if status == nil {
|
||||
// default status message
|
||||
status = ð.StatusPacket69{
|
||||
status = ð.StatusPacket{
|
||||
ProtocolVersion: uint32(c.negotiatedProtoVersion),
|
||||
NetworkID: chain.config.ChainID.Uint64(),
|
||||
Genesis: chain.blocks[0].Hash(),
|
||||
|
|
|
|||
|
|
@ -86,9 +86,3 @@ func protoOffset(proto Proto) uint64 {
|
|||
panic("unhandled protocol")
|
||||
}
|
||||
}
|
||||
|
||||
// msgTypePtr is the constraint for protocol message types.
|
||||
type msgTypePtr[U any] interface {
|
||||
*U
|
||||
Kind() byte
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/snap"
|
||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
)
|
||||
|
|
@ -937,10 +938,14 @@ func (s *Suite) snapGetTrieNodes(t *utesting.T, tc *trieNodesTest) error {
|
|||
}
|
||||
|
||||
// write0 request
|
||||
paths, err := rlp.EncodeToRawList(tc.paths)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
req := &snap.GetTrieNodesPacket{
|
||||
ID: uint64(rand.Int63()),
|
||||
Root: tc.root,
|
||||
Paths: tc.paths,
|
||||
Paths: paths,
|
||||
Bytes: tc.nBytes,
|
||||
}
|
||||
msg, err := conn.snapRequest(snap.GetTrieNodesMsg, req)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
|
|
@ -151,7 +152,11 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to get headers for given request: %v", err)
|
||||
}
|
||||
if !headersMatch(expected, headers.BlockHeadersRequest) {
|
||||
received, err := headers.List.Items()
|
||||
if err != nil {
|
||||
t.Fatalf("invalid headers received: %v", err)
|
||||
}
|
||||
if !headersMatch(expected, received) {
|
||||
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers)
|
||||
}
|
||||
}
|
||||
|
|
@ -237,7 +242,7 @@ concurrently, with different request IDs.`)
|
|||
|
||||
// Wait for responses.
|
||||
// Note they can arrive in either order.
|
||||
resp, err := collectResponses(conn, 2, func(msg *eth.BlockHeadersPacket) uint64 {
|
||||
resp, err := collectHeaderResponses(conn, 2, func(msg *eth.BlockHeadersPacket) uint64 {
|
||||
if msg.RequestId != 111 && msg.RequestId != 222 {
|
||||
t.Fatalf("response with unknown request ID: %v", msg.RequestId)
|
||||
}
|
||||
|
|
@ -248,17 +253,11 @@ concurrently, with different request IDs.`)
|
|||
}
|
||||
|
||||
// Check if headers match.
|
||||
resp1 := resp[111]
|
||||
if expected, err := s.chain.GetHeaders(req1); err != nil {
|
||||
t.Fatalf("failed to get expected headers for request 1: %v", err)
|
||||
} else if !headersMatch(expected, resp1.BlockHeadersRequest) {
|
||||
t.Fatalf("header mismatch for request ID %v: \nexpected %v \ngot %v", 111, expected, resp1)
|
||||
if err := s.checkHeadersAgainstChain(req1, resp[111]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp2 := resp[222]
|
||||
if expected, err := s.chain.GetHeaders(req2); err != nil {
|
||||
t.Fatalf("failed to get expected headers for request 2: %v", err)
|
||||
} else if !headersMatch(expected, resp2.BlockHeadersRequest) {
|
||||
t.Fatalf("header mismatch for request ID %v: \nexpected %v \ngot %v", 222, expected, resp2)
|
||||
if err := s.checkHeadersAgainstChain(req2, resp[222]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -303,8 +302,8 @@ same request ID. The node should handle the request by responding to both reques
|
|||
|
||||
// Wait for the responses. They can arrive in either order, and we can't tell them
|
||||
// apart by their request ID, so use the number of headers instead.
|
||||
resp, err := collectResponses(conn, 2, func(msg *eth.BlockHeadersPacket) uint64 {
|
||||
id := uint64(len(msg.BlockHeadersRequest))
|
||||
resp, err := collectHeaderResponses(conn, 2, func(msg *eth.BlockHeadersPacket) uint64 {
|
||||
id := uint64(msg.List.Len())
|
||||
if id != 2 && id != 3 {
|
||||
t.Fatalf("invalid number of headers in response: %d", id)
|
||||
}
|
||||
|
|
@ -315,26 +314,35 @@ same request ID. The node should handle the request by responding to both reques
|
|||
}
|
||||
|
||||
// Check if headers match.
|
||||
resp1 := resp[2]
|
||||
if expected, err := s.chain.GetHeaders(request1); err != nil {
|
||||
t.Fatalf("failed to get expected headers for request 1: %v", err)
|
||||
} else if !headersMatch(expected, resp1.BlockHeadersRequest) {
|
||||
t.Fatalf("headers mismatch: \nexpected %v \ngot %v", expected, resp1)
|
||||
if err := s.checkHeadersAgainstChain(request1, resp[2]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp2 := resp[3]
|
||||
if expected, err := s.chain.GetHeaders(request2); err != nil {
|
||||
t.Fatalf("failed to get expected headers for request 2: %v", err)
|
||||
} else if !headersMatch(expected, resp2.BlockHeadersRequest) {
|
||||
t.Fatalf("headers mismatch: \nexpected %v \ngot %v", expected, resp2)
|
||||
if err := s.checkHeadersAgainstChain(request2, resp[3]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Suite) checkHeadersAgainstChain(req *eth.GetBlockHeadersPacket, resp *eth.BlockHeadersPacket) error {
|
||||
received2, err := resp.List.Items()
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid headers in response with request ID %v (%d items): %v", resp.RequestId, resp.List.Len(), err)
|
||||
}
|
||||
if expected, err := s.chain.GetHeaders(req); err != nil {
|
||||
return fmt.Errorf("test chain failed to get expected headers for request: %v", err)
|
||||
} else if !headersMatch(expected, received2) {
|
||||
return fmt.Errorf("header mismatch for request ID %v (%d items): \nexpected %v \ngot %v", resp.RequestId, resp.List.Len(), expected, resp)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// collectResponses waits for n messages of type T on the given connection.
|
||||
// The messsages are collected according to the 'identity' function.
|
||||
func collectResponses[T any, P msgTypePtr[T]](conn *Conn, n int, identity func(P) uint64) (map[uint64]P, error) {
|
||||
resp := make(map[uint64]P, n)
|
||||
//
|
||||
// This function is written in a generic way to handle
|
||||
func collectHeaderResponses(conn *Conn, n int, identity func(*eth.BlockHeadersPacket) uint64) (map[uint64]*eth.BlockHeadersPacket, error) {
|
||||
resp := make(map[uint64]*eth.BlockHeadersPacket, n)
|
||||
for range n {
|
||||
r := new(T)
|
||||
r := new(eth.BlockHeadersPacket)
|
||||
if err := conn.ReadMsg(ethProto, eth.BlockHeadersMsg, r); err != nil {
|
||||
return resp, fmt.Errorf("read error: %v", err)
|
||||
}
|
||||
|
|
@ -373,10 +381,8 @@ and expects a response.`)
|
|||
if got, want := headers.RequestId, req.RequestId; got != want {
|
||||
t.Fatalf("unexpected request id")
|
||||
}
|
||||
if expected, err := s.chain.GetHeaders(req); err != nil {
|
||||
t.Fatalf("failed to get expected block headers: %v", err)
|
||||
} else if !headersMatch(expected, headers.BlockHeadersRequest) {
|
||||
t.Fatalf("header mismatch: \nexpected %v \ngot %v", expected, headers)
|
||||
if err := s.checkHeadersAgainstChain(req, headers); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -407,9 +413,8 @@ func (s *Suite) TestGetBlockBodies(t *utesting.T) {
|
|||
if got, want := resp.RequestId, req.RequestId; got != want {
|
||||
t.Fatalf("unexpected request id in respond", got, want)
|
||||
}
|
||||
bodies := resp.BlockBodiesResponse
|
||||
if len(bodies) != len(req.GetBlockBodiesRequest) {
|
||||
t.Fatalf("wrong bodies in response: expected %d bodies, got %d", len(req.GetBlockBodiesRequest), len(bodies))
|
||||
if resp.List.Len() != len(req.GetBlockBodiesRequest) {
|
||||
t.Fatalf("wrong bodies in response: expected %d bodies, got %d", len(req.GetBlockBodiesRequest), resp.List.Len())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -433,7 +438,7 @@ func (s *Suite) TestGetReceipts(t *utesting.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Create block bodies request.
|
||||
// Create receipts request.
|
||||
req := ð.GetReceiptsPacket{
|
||||
RequestId: 66,
|
||||
GetReceiptsRequest: (eth.GetReceiptsRequest)(hashes),
|
||||
|
|
@ -442,15 +447,15 @@ func (s *Suite) TestGetReceipts(t *utesting.T) {
|
|||
t.Fatalf("could not write to connection: %v", err)
|
||||
}
|
||||
// Wait for response.
|
||||
resp := new(eth.ReceiptsPacket[*eth.ReceiptList69])
|
||||
resp := new(eth.ReceiptsPacket)
|
||||
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
|
||||
t.Fatalf("error reading block bodies msg: %v", err)
|
||||
}
|
||||
if got, want := resp.RequestId, req.RequestId; got != want {
|
||||
t.Fatalf("unexpected request id in respond", got, want)
|
||||
}
|
||||
if len(resp.List) != len(req.GetReceiptsRequest) {
|
||||
t.Fatalf("wrong bodies in response: expected %d bodies, got %d", len(req.GetReceiptsRequest), len(resp.List))
|
||||
if resp.List.Len() != len(req.GetReceiptsRequest) {
|
||||
t.Fatalf("wrong receipts in response: expected %d receipts, got %d", len(req.GetReceiptsRequest), resp.List.Len())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -804,7 +809,11 @@ on another peer connection using GetPooledTransactions.`)
|
|||
if got, want := msg.RequestId, req.RequestId; got != want {
|
||||
t.Fatalf("unexpected request id in response: got %d, want %d", got, want)
|
||||
}
|
||||
for _, got := range msg.PooledTransactionsResponse {
|
||||
responseTxs, err := msg.List.Items()
|
||||
if err != nil {
|
||||
t.Fatalf("invalid transactions in response: %v", err)
|
||||
}
|
||||
for _, got := range responseTxs {
|
||||
if _, exists := set[got.Hash()]; !exists {
|
||||
t.Fatalf("unexpected tx received: %v", got.Hash())
|
||||
}
|
||||
|
|
@ -976,7 +985,9 @@ func (s *Suite) TestBlobViolations(t *utesting.T) {
|
|||
if err := conn.ReadMsg(ethProto, eth.GetPooledTransactionsMsg, req); err != nil {
|
||||
t.Fatalf("reading pooled tx request failed: %v", err)
|
||||
}
|
||||
resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, PooledTransactionsResponse: test.resp}
|
||||
|
||||
encTxs, _ := rlp.EncodeToRawList(test.resp)
|
||||
resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, List: encTxs}
|
||||
if err := conn.Write(ethProto, eth.PooledTransactionsMsg, resp); err != nil {
|
||||
t.Fatalf("writing pooled tx response failed: %v", err)
|
||||
}
|
||||
|
|
@ -1104,7 +1115,8 @@ func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types
|
|||
// the good peer is connected, and has announced the tx.
|
||||
// proceed to send the incorrect one from the bad peer.
|
||||
|
||||
resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, PooledTransactionsResponse: eth.PooledTransactionsResponse(types.Transactions{badTx})}
|
||||
encTxs, _ := rlp.EncodeToRawList([]*types.Transaction{badTx})
|
||||
resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, List: encTxs}
|
||||
if err := conn.Write(ethProto, eth.PooledTransactionsMsg, resp); err != nil {
|
||||
errc <- fmt.Errorf("writing pooled tx response failed: %v", err)
|
||||
return
|
||||
|
|
@ -1164,7 +1176,8 @@ func (s *Suite) testBadBlobTx(t *utesting.T, tx *types.Transaction, badTx *types
|
|||
return
|
||||
}
|
||||
|
||||
resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, PooledTransactionsResponse: eth.PooledTransactionsResponse(types.Transactions{tx})}
|
||||
encTxs, _ := rlp.EncodeToRawList([]*types.Transaction{tx})
|
||||
resp := eth.PooledTransactionsPacket{RequestId: req.RequestId, List: encTxs}
|
||||
if err := conn.Write(ethProto, eth.PooledTransactionsMsg, resp); err != nil {
|
||||
errc <- fmt.Errorf("writing pooled tx response failed: %v", err)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
// sendTxs sends the given transactions to the node and
|
||||
|
|
@ -51,7 +52,8 @@ func (s *Suite) sendTxs(t *utesting.T, txs []*types.Transaction) error {
|
|||
return fmt.Errorf("peering failed: %v", err)
|
||||
}
|
||||
|
||||
if err = sendConn.Write(ethProto, eth.TransactionsMsg, eth.TransactionsPacket(txs)); err != nil {
|
||||
encTxs, _ := rlp.EncodeToRawList(txs)
|
||||
if err = sendConn.Write(ethProto, eth.TransactionsMsg, eth.TransactionsPacket{RawList: encTxs}); err != nil {
|
||||
return fmt.Errorf("failed to write message to connection: %v", err)
|
||||
}
|
||||
|
||||
|
|
@ -68,7 +70,8 @@ func (s *Suite) sendTxs(t *utesting.T, txs []*types.Transaction) error {
|
|||
}
|
||||
switch msg := msg.(type) {
|
||||
case *eth.TransactionsPacket:
|
||||
for _, tx := range *msg {
|
||||
txs, _ := msg.Items()
|
||||
for _, tx := range txs {
|
||||
got[tx.Hash()] = true
|
||||
}
|
||||
case *eth.NewPooledTransactionHashesPacket:
|
||||
|
|
@ -80,9 +83,10 @@ func (s *Suite) sendTxs(t *utesting.T, txs []*types.Transaction) error {
|
|||
if err != nil {
|
||||
t.Logf("invalid GetBlockHeaders request: %v", err)
|
||||
}
|
||||
encHeaders, _ := rlp.EncodeToRawList(headers)
|
||||
recvConn.Write(ethProto, eth.BlockHeadersMsg, ð.BlockHeadersPacket{
|
||||
RequestId: msg.RequestId,
|
||||
BlockHeadersRequest: headers,
|
||||
RequestId: msg.RequestId,
|
||||
List: encHeaders,
|
||||
})
|
||||
default:
|
||||
return fmt.Errorf("unexpected eth wire msg: %s", pretty.Sdump(msg))
|
||||
|
|
@ -167,9 +171,10 @@ func (s *Suite) sendInvalidTxs(t *utesting.T, txs []*types.Transaction) error {
|
|||
if err != nil {
|
||||
t.Logf("invalid GetBlockHeaders request: %v", err)
|
||||
}
|
||||
encHeaders, _ := rlp.EncodeToRawList(headers)
|
||||
recvConn.Write(ethProto, eth.BlockHeadersMsg, ð.BlockHeadersPacket{
|
||||
RequestId: msg.RequestId,
|
||||
BlockHeadersRequest: headers,
|
||||
RequestId: msg.RequestId,
|
||||
List: encHeaders,
|
||||
})
|
||||
default:
|
||||
return fmt.Errorf("unexpected eth message: %v", pretty.Sdump(msg))
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ func (s *Suite) AllTests() []utesting.Test {
|
|||
{Name: "Ping", Fn: s.TestPing},
|
||||
{Name: "PingLargeRequestID", Fn: s.TestPingLargeRequestID},
|
||||
{Name: "PingMultiIP", Fn: s.TestPingMultiIP},
|
||||
{Name: "PingHandshakeInterrupted", Fn: s.TestPingHandshakeInterrupted},
|
||||
{Name: "HandshakeResend", Fn: s.TestHandshakeResend},
|
||||
{Name: "TalkRequest", Fn: s.TestTalkRequest},
|
||||
{Name: "FindnodeZeroDistance", Fn: s.TestFindnodeZeroDistance},
|
||||
{Name: "FindnodeResults", Fn: s.TestFindnodeResults},
|
||||
|
|
@ -158,22 +158,20 @@ the attempt from a different IP.`)
|
|||
}
|
||||
}
|
||||
|
||||
// TestPingHandshakeInterrupted starts a handshake, but doesn't finish it and sends a second ordinary message
|
||||
// packet instead of a handshake message packet. The remote node should respond with
|
||||
// another WHOAREYOU challenge for the second packet.
|
||||
func (s *Suite) TestPingHandshakeInterrupted(t *utesting.T) {
|
||||
t.Log(`TestPingHandshakeInterrupted starts a handshake, but doesn't finish it and sends a second ordinary message
|
||||
packet instead of a handshake message packet. The remote node should respond with
|
||||
another WHOAREYOU challenge for the second packet.`)
|
||||
|
||||
// TestHandshakeResend starts a handshake, but doesn't finish it and sends a second ordinary message
|
||||
// packet instead of a handshake message packet. The remote node should repeat the previous WHOAREYOU
|
||||
// challenge for the first PING.
|
||||
func (s *Suite) TestHandshakeResend(t *utesting.T) {
|
||||
conn, l1 := s.listen1(t)
|
||||
defer conn.close()
|
||||
|
||||
// First PING triggers challenge.
|
||||
ping := &v5wire.Ping{ReqID: conn.nextReqID()}
|
||||
conn.write(l1, ping, nil)
|
||||
var challenge1 *v5wire.Whoareyou
|
||||
switch resp := conn.read(l1).(type) {
|
||||
case *v5wire.Whoareyou:
|
||||
challenge1 = resp
|
||||
t.Logf("got WHOAREYOU for PING")
|
||||
default:
|
||||
t.Fatal("expected WHOAREYOU, got", resp)
|
||||
|
|
@ -181,9 +179,16 @@ another WHOAREYOU challenge for the second packet.`)
|
|||
|
||||
// Send second PING.
|
||||
ping2 := &v5wire.Ping{ReqID: conn.nextReqID()}
|
||||
switch resp := conn.reqresp(l1, ping2).(type) {
|
||||
case *v5wire.Pong:
|
||||
checkPong(t, resp, ping2, l1)
|
||||
conn.write(l1, ping2, nil)
|
||||
switch resp := conn.read(l1).(type) {
|
||||
case *v5wire.Whoareyou:
|
||||
if resp.Nonce != challenge1.Nonce {
|
||||
t.Fatalf("wrong nonce %x in WHOAREYOU (want %x)", resp.Nonce[:], challenge1.Nonce[:])
|
||||
}
|
||||
if !bytes.Equal(resp.ChallengeData, challenge1.ChallengeData) {
|
||||
t.Fatalf("wrong ChallengeData in resent WHOAREYOU (want %x)", resp.ChallengeData, challenge1.ChallengeData)
|
||||
}
|
||||
resp.Node = conn.remote
|
||||
default:
|
||||
t.Fatal("expected WHOAREYOU, got", resp)
|
||||
}
|
||||
|
|
|
|||
128
cmd/era/main.go
128
cmd/era/main.go
|
|
@ -30,6 +30,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/internal/era"
|
||||
"github.com/ethereum/go-ethereum/internal/era/execdb"
|
||||
"github.com/ethereum/go-ethereum/internal/era/onedb"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -53,7 +55,7 @@ var (
|
|||
eraSizeFlag = &cli.IntFlag{
|
||||
Name: "size",
|
||||
Usage: "number of blocks per era",
|
||||
Value: era.MaxEra1Size,
|
||||
Value: era.MaxSize,
|
||||
}
|
||||
txsFlag = &cli.BoolFlag{
|
||||
Name: "txs",
|
||||
|
|
@ -131,7 +133,7 @@ func block(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// info prints some high-level information about the era1 file.
|
||||
// info prints some high-level information about the era file.
|
||||
func info(ctx *cli.Context) error {
|
||||
epoch, err := strconv.ParseUint(ctx.Args().First(), 10, 64)
|
||||
if err != nil {
|
||||
|
|
@ -142,33 +144,34 @@ func info(ctx *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
defer e.Close()
|
||||
acc, err := e.Accumulator()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading accumulator: %w", err)
|
||||
var (
|
||||
accHex string
|
||||
tdStr string
|
||||
)
|
||||
if acc, err := e.Accumulator(); err == nil {
|
||||
accHex = acc.Hex()
|
||||
}
|
||||
td, err := e.InitialTD()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading total difficulty: %w", err)
|
||||
if td, err := e.InitialTD(); err == nil {
|
||||
tdStr = td.String()
|
||||
}
|
||||
info := struct {
|
||||
Accumulator common.Hash `json:"accumulator"`
|
||||
TotalDifficulty *big.Int `json:"totalDifficulty"`
|
||||
StartBlock uint64 `json:"startBlock"`
|
||||
Count uint64 `json:"count"`
|
||||
Accumulator string `json:"accumulator,omitempty"`
|
||||
TotalDifficulty string `json:"totalDifficulty,omitempty"`
|
||||
StartBlock uint64 `json:"startBlock"`
|
||||
Count uint64 `json:"count"`
|
||||
}{
|
||||
acc, td, e.Start(), e.Count(),
|
||||
accHex, tdStr, e.Start(), e.Count(),
|
||||
}
|
||||
b, _ := json.MarshalIndent(info, "", " ")
|
||||
fmt.Println(string(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
// open opens an era1 file at a certain epoch.
|
||||
func open(ctx *cli.Context, epoch uint64) (*era.Era, error) {
|
||||
var (
|
||||
dir = ctx.String(dirFlag.Name)
|
||||
network = ctx.String(networkFlag.Name)
|
||||
)
|
||||
// open opens an era file at a certain epoch.
|
||||
func open(ctx *cli.Context, epoch uint64) (era.Era, error) {
|
||||
dir := ctx.String(dirFlag.Name)
|
||||
network := ctx.String(networkFlag.Name)
|
||||
|
||||
entries, err := era.ReadDir(dir, network)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading era dir: %w", err)
|
||||
|
|
@ -176,7 +179,28 @@ func open(ctx *cli.Context, epoch uint64) (*era.Era, error) {
|
|||
if epoch >= uint64(len(entries)) {
|
||||
return nil, fmt.Errorf("epoch out-of-bounds: last %d, want %d", len(entries)-1, epoch)
|
||||
}
|
||||
return era.Open(filepath.Join(dir, entries[epoch]))
|
||||
path := filepath.Join(dir, entries[epoch])
|
||||
return openByPath(path)
|
||||
}
|
||||
|
||||
// openByPath tries to open a single file as either eraE or era1 based on extension,
|
||||
// falling back to the other reader if needed.
|
||||
func openByPath(path string) (era.Era, error) {
|
||||
switch strings.ToLower(filepath.Ext(path)) {
|
||||
case ".erae":
|
||||
if e, err := execdb.Open(path); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return e, nil
|
||||
}
|
||||
case ".era1":
|
||||
if e, err := onedb.Open(path); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return e, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported or unreadable era file: %s", path)
|
||||
}
|
||||
|
||||
// verify checks each era1 file in a directory to ensure it is well-formed and
|
||||
|
|
@ -203,18 +227,58 @@ func verify(ctx *cli.Context) error {
|
|||
return fmt.Errorf("error reading %s: %w", dir, err)
|
||||
}
|
||||
|
||||
if len(entries) != len(roots) {
|
||||
return errors.New("number of era1 files should match the number of accumulator hashes")
|
||||
// Build the verification list respecting the rule:
|
||||
// era1: must have accumulator, always verify
|
||||
// erae: verify only if accumulator exists (pre-merge)
|
||||
|
||||
// Build list of files to verify.
|
||||
verify := make([]string, 0, len(entries))
|
||||
|
||||
for _, name := range entries {
|
||||
path := filepath.Join(dir, name)
|
||||
ext := strings.ToLower(filepath.Ext(name))
|
||||
|
||||
switch ext {
|
||||
case ".era1":
|
||||
e, err := onedb.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening era1 file %s: %w", name, err)
|
||||
}
|
||||
_, accErr := e.Accumulator()
|
||||
e.Close()
|
||||
if accErr != nil {
|
||||
return fmt.Errorf("era1 file %s missing accumulator: %w", name, accErr)
|
||||
}
|
||||
verify = append(verify, path)
|
||||
|
||||
case ".erae":
|
||||
e, err := execdb.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening erae file %s: %w", name, err)
|
||||
}
|
||||
_, accErr := e.Accumulator()
|
||||
e.Close()
|
||||
if accErr == nil {
|
||||
verify = append(verify, path) // pre-merge only
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported era file: %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(verify) != len(roots) {
|
||||
return fmt.Errorf("mismatch between eras to verify (%d) and provided roots (%d)", len(verify), len(roots))
|
||||
}
|
||||
|
||||
// Verify each epoch matches the expected root.
|
||||
for i, want := range roots {
|
||||
// Wrap in function so defers don't stack.
|
||||
err := func() error {
|
||||
name := entries[i]
|
||||
e, err := era.Open(filepath.Join(dir, name))
|
||||
path := verify[i]
|
||||
name := filepath.Base(path)
|
||||
e, err := openByPath(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening era1 file %s: %w", name, err)
|
||||
return fmt.Errorf("error opening era file %s: %w", name, err)
|
||||
}
|
||||
defer e.Close()
|
||||
// Read accumulator and check against expected.
|
||||
|
|
@ -243,7 +307,7 @@ func verify(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
// checkAccumulator verifies the accumulator matches the data in the Era.
|
||||
func checkAccumulator(e *era.Era) error {
|
||||
func checkAccumulator(e era.Era) error {
|
||||
var (
|
||||
err error
|
||||
want common.Hash
|
||||
|
|
@ -257,7 +321,7 @@ func checkAccumulator(e *era.Era) error {
|
|||
if td, err = e.InitialTD(); err != nil {
|
||||
return fmt.Errorf("error reading total difficulty: %w", err)
|
||||
}
|
||||
it, err := era.NewIterator(e)
|
||||
it, err := e.Iterator()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making era iterator: %w", err)
|
||||
}
|
||||
|
|
@ -290,9 +354,13 @@ func checkAccumulator(e *era.Era) error {
|
|||
if rr != block.ReceiptHash() {
|
||||
return fmt.Errorf("receipt root in block %d mismatch: want %s, got %s", block.NumberU64(), block.ReceiptHash(), rr)
|
||||
}
|
||||
hashes = append(hashes, block.Hash())
|
||||
td.Add(td, block.Difficulty())
|
||||
tds = append(tds, new(big.Int).Set(td))
|
||||
// Only include pre-merge blocks in accumulator calculation.
|
||||
// Post-merge blocks have difficulty == 0.
|
||||
if block.Difficulty().Sign() > 0 {
|
||||
hashes = append(hashes, block.Hash())
|
||||
td.Add(td, block.Difficulty())
|
||||
tds = append(tds, new(big.Int).Set(td))
|
||||
}
|
||||
}
|
||||
if it.Error() != nil {
|
||||
return fmt.Errorf("error reading block %d: %w", it.Number(), it.Error())
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ type header struct {
|
|||
BlobGasUsed *uint64 `json:"blobGasUsed" rlp:"optional"`
|
||||
ExcessBlobGas *uint64 `json:"excessBlobGas" rlp:"optional"`
|
||||
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||
SlotNumber *uint64 `json:"slotNumber" rlp:"optional"`
|
||||
}
|
||||
|
||||
type headerMarshaling struct {
|
||||
|
|
@ -68,6 +69,7 @@ type headerMarshaling struct {
|
|||
BaseFee *math.HexOrDecimal256
|
||||
BlobGasUsed *math.HexOrDecimal64
|
||||
ExcessBlobGas *math.HexOrDecimal64
|
||||
SlotNumber *math.HexOrDecimal64
|
||||
}
|
||||
|
||||
type bbInput struct {
|
||||
|
|
@ -136,6 +138,7 @@ func (i *bbInput) ToBlock() *types.Block {
|
|||
BlobGasUsed: i.Header.BlobGasUsed,
|
||||
ExcessBlobGas: i.Header.ExcessBlobGas,
|
||||
ParentBeaconRoot: i.Header.ParentBeaconBlockRoot,
|
||||
SlotNumber: i.Header.SlotNumber,
|
||||
}
|
||||
|
||||
// Fill optional values.
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -40,7 +41,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/holiman/uint256"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
type Prestate struct {
|
||||
|
|
@ -102,6 +102,7 @@ type stEnv struct {
|
|||
ParentExcessBlobGas *uint64 `json:"parentExcessBlobGas,omitempty"`
|
||||
ParentBlobGasUsed *uint64 `json:"parentBlobGasUsed,omitempty"`
|
||||
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
SlotNumber *uint64 `json:"slotNumber"`
|
||||
}
|
||||
|
||||
type stEnvMarshaling struct {
|
||||
|
|
@ -120,6 +121,7 @@ type stEnvMarshaling struct {
|
|||
ExcessBlobGas *math.HexOrDecimal64
|
||||
ParentExcessBlobGas *math.HexOrDecimal64
|
||||
ParentBlobGasUsed *math.HexOrDecimal64
|
||||
SlotNumber *math.HexOrDecimal64
|
||||
}
|
||||
|
||||
type rejectedTx struct {
|
||||
|
|
@ -147,15 +149,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
isEIP4762 = chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)
|
||||
statedb = MakePreState(rawdb.NewMemoryDatabase(), pre.Pre, isEIP4762)
|
||||
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp)
|
||||
gaspool = new(core.GasPool)
|
||||
gaspool = core.NewGasPool(pre.Env.GasLimit)
|
||||
blockHash = common.Hash{0x13, 0x37}
|
||||
rejectedTxs []*rejectedTx
|
||||
includedTxs types.Transactions
|
||||
gasUsed = uint64(0)
|
||||
blobGasUsed = uint64(0)
|
||||
receipts = make(types.Receipts, 0)
|
||||
)
|
||||
gaspool.AddGas(pre.Env.GasLimit)
|
||||
vmContext := vm.BlockContext{
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
|
|
@ -195,6 +195,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
ExcessBlobGas: pre.Env.ParentExcessBlobGas,
|
||||
BlobGasUsed: pre.Env.ParentBlobGasUsed,
|
||||
BaseFee: pre.Env.ParentBaseFee,
|
||||
SlotNumber: pre.Env.SlotNumber,
|
||||
}
|
||||
header := &types.Header{
|
||||
Time: pre.Env.Timestamp,
|
||||
|
|
@ -255,16 +256,19 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
statedb.SetTxContext(tx.Hash(), len(receipts))
|
||||
var (
|
||||
snapshot = statedb.Snapshot()
|
||||
prevGas = gaspool.Gas()
|
||||
gp = gaspool.Snapshot()
|
||||
)
|
||||
receipt, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, &gasUsed, evm)
|
||||
receipt, err := core.ApplyTransactionWithEVM(msg, gaspool, statedb, vmContext.BlockNumber, blockHash, pre.Env.Timestamp, tx, evm)
|
||||
if err != nil {
|
||||
statedb.RevertToSnapshot(snapshot)
|
||||
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
||||
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
||||
gaspool.SetGas(prevGas)
|
||||
gaspool.Set(gp)
|
||||
continue
|
||||
}
|
||||
if receipt.Logs == nil {
|
||||
receipt.Logs = []*types.Log{}
|
||||
}
|
||||
includedTxs = append(includedTxs, tx)
|
||||
if hashError != nil {
|
||||
return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError)
|
||||
|
|
@ -346,7 +350,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
Receipts: receipts,
|
||||
Rejected: rejectedTxs,
|
||||
Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty),
|
||||
GasUsed: (math.HexOrDecimal64)(gasUsed),
|
||||
GasUsed: (math.HexOrDecimal64)(gaspool.Used()),
|
||||
BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee),
|
||||
}
|
||||
if pre.Env.Withdrawals != nil {
|
||||
|
|
@ -361,10 +365,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
|||
// Set requestsHash on block.
|
||||
h := types.CalcRequestsHash(requests)
|
||||
execRs.RequestsHash = &h
|
||||
for i := range requests {
|
||||
// remove prefix
|
||||
requests[i] = requests[i][1:]
|
||||
}
|
||||
execRs.Requests = requests
|
||||
}
|
||||
|
||||
|
|
@ -415,7 +415,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, isBintrie bool
|
|||
}
|
||||
|
||||
func rlpHash(x any) (h common.Hash) {
|
||||
hw := sha3.NewLegacyKeccak256()
|
||||
hw := keccak.NewLegacyKeccak256()
|
||||
rlp.Encode(hw, x)
|
||||
hw.Sum(h[:0])
|
||||
return h
|
||||
|
|
|
|||
|
|
@ -56,27 +56,35 @@ func (l *fileWritingTracer) Write(p []byte) (n int, err error) {
|
|||
return n, nil
|
||||
}
|
||||
|
||||
// newFileWriter creates a set of hooks which wraps inner hooks (typically a logger),
|
||||
// newFileWriter creates a tracer which wraps inner hooks (typically a logger),
|
||||
// and writes the output to a file, one file per transaction.
|
||||
func newFileWriter(baseDir string, innerFn func(out io.Writer) *tracing.Hooks) *tracing.Hooks {
|
||||
func newFileWriter(baseDir string, innerFn func(out io.Writer) *tracing.Hooks) *tracers.Tracer {
|
||||
t := &fileWritingTracer{
|
||||
baseDir: baseDir,
|
||||
suffix: "jsonl",
|
||||
}
|
||||
t.inner = innerFn(t) // instantiate the inner tracer
|
||||
return t.hooks()
|
||||
return &tracers.Tracer{
|
||||
Hooks: t.hooks(),
|
||||
GetResult: func() (json.RawMessage, error) { return json.RawMessage("{}"), nil },
|
||||
Stop: func(err error) {},
|
||||
}
|
||||
}
|
||||
|
||||
// newResultWriter creates a set of hooks wraps and invokes an underlying tracer,
|
||||
// newResultWriter creates a tracer that wraps and invokes an underlying tracer,
|
||||
// and writes the result (getResult-output) to file, one per transaction.
|
||||
func newResultWriter(baseDir string, tracer *tracers.Tracer) *tracing.Hooks {
|
||||
func newResultWriter(baseDir string, tracer *tracers.Tracer) *tracers.Tracer {
|
||||
t := &fileWritingTracer{
|
||||
baseDir: baseDir,
|
||||
getResult: tracer.GetResult,
|
||||
inner: tracer.Hooks,
|
||||
suffix: "json",
|
||||
}
|
||||
return t.hooks()
|
||||
return &tracers.Tracer{
|
||||
Hooks: t.hooks(),
|
||||
GetResult: func() (json.RawMessage, error) { return json.RawMessage("{}"), nil },
|
||||
Stop: func(err error) {},
|
||||
}
|
||||
}
|
||||
|
||||
// OnTxStart creates a new output-file specific for this transaction, and invokes
|
||||
|
|
|
|||
|
|
@ -162,6 +162,11 @@ var (
|
|||
strings.Join(vm.ActivateableEips(), ", ")),
|
||||
Value: "GrayGlacier",
|
||||
}
|
||||
OpcodeCountFlag = &cli.StringFlag{
|
||||
Name: "opcode.count",
|
||||
Usage: "If set, opcode execution counts will be written to this file (relative to output.basedir).",
|
||||
Value: "",
|
||||
}
|
||||
VerbosityFlag = &cli.IntFlag{
|
||||
Name: "verbosity",
|
||||
Usage: "sets the verbosity level",
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ func (h header) MarshalJSON() ([]byte, error) {
|
|||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed" rlp:"optional"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas" rlp:"optional"`
|
||||
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||
SlotNumber *math.HexOrDecimal64 `json:"slotNumber" rlp:"optional"`
|
||||
}
|
||||
var enc header
|
||||
enc.ParentHash = h.ParentHash
|
||||
|
|
@ -60,6 +61,7 @@ func (h header) MarshalJSON() ([]byte, error) {
|
|||
enc.BlobGasUsed = (*math.HexOrDecimal64)(h.BlobGasUsed)
|
||||
enc.ExcessBlobGas = (*math.HexOrDecimal64)(h.ExcessBlobGas)
|
||||
enc.ParentBeaconBlockRoot = h.ParentBeaconBlockRoot
|
||||
enc.SlotNumber = (*math.HexOrDecimal64)(h.SlotNumber)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
|
|
@ -86,6 +88,7 @@ func (h *header) UnmarshalJSON(input []byte) error {
|
|||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed" rlp:"optional"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas" rlp:"optional"`
|
||||
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`
|
||||
SlotNumber *math.HexOrDecimal64 `json:"slotNumber" rlp:"optional"`
|
||||
}
|
||||
var dec header
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -155,5 +158,8 @@ func (h *header) UnmarshalJSON(input []byte) error {
|
|||
if dec.ParentBeaconBlockRoot != nil {
|
||||
h.ParentBeaconBlockRoot = dec.ParentBeaconBlockRoot
|
||||
}
|
||||
if dec.SlotNumber != nil {
|
||||
h.SlotNumber = (*uint64)(dec.SlotNumber)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
|||
ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"`
|
||||
ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"`
|
||||
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
SlotNumber *math.HexOrDecimal64 `json:"slotNumber"`
|
||||
}
|
||||
var enc stEnv
|
||||
enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
|
||||
|
|
@ -59,6 +60,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
|||
enc.ParentExcessBlobGas = (*math.HexOrDecimal64)(s.ParentExcessBlobGas)
|
||||
enc.ParentBlobGasUsed = (*math.HexOrDecimal64)(s.ParentBlobGasUsed)
|
||||
enc.ParentBeaconBlockRoot = s.ParentBeaconBlockRoot
|
||||
enc.SlotNumber = (*math.HexOrDecimal64)(s.SlotNumber)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
|
|
@ -85,6 +87,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
|||
ParentExcessBlobGas *math.HexOrDecimal64 `json:"parentExcessBlobGas,omitempty"`
|
||||
ParentBlobGasUsed *math.HexOrDecimal64 `json:"parentBlobGasUsed,omitempty"`
|
||||
ParentBeaconBlockRoot *common.Hash `json:"parentBeaconBlockRoot"`
|
||||
SlotNumber *math.HexOrDecimal64 `json:"slotNumber"`
|
||||
}
|
||||
var dec stEnv
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -154,5 +157,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
|||
if dec.ParentBeaconBlockRoot != nil {
|
||||
s.ParentBeaconBlockRoot = dec.ParentBeaconBlockRoot
|
||||
}
|
||||
if dec.SlotNumber != nil {
|
||||
s.SlotNumber = (*uint64)(dec.SlotNumber)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/tests"
|
||||
|
|
@ -115,9 +117,6 @@ func Transaction(ctx *cli.Context) error {
|
|||
}
|
||||
var results []result
|
||||
for it.Next() {
|
||||
if err := it.Err(); err != nil {
|
||||
return NewError(ErrorIO, err)
|
||||
}
|
||||
var tx types.Transaction
|
||||
err := rlp.DecodeBytes(it.Value(), &tx)
|
||||
if err != nil {
|
||||
|
|
@ -180,14 +179,21 @@ func Transaction(ctx *cli.Context) error {
|
|||
r.Error = errors.New("gas * maxFeePerGas exceeds 256 bits")
|
||||
}
|
||||
// Check whether the init code size has been exceeded.
|
||||
if chainConfig.IsShanghai(new(big.Int), 0) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize {
|
||||
r.Error = errors.New("max initcode size exceeded")
|
||||
if tx.To() == nil {
|
||||
if err := vm.CheckMaxInitCodeSize(&rules, uint64(len(tx.Data()))); err != nil {
|
||||
r.Error = err
|
||||
}
|
||||
}
|
||||
|
||||
if chainConfig.IsOsaka(new(big.Int), 0) && tx.Gas() > params.MaxTxGas {
|
||||
r.Error = errors.New("gas limit exceeds maximum")
|
||||
}
|
||||
results = append(results, r)
|
||||
}
|
||||
if err := it.Err(); err != nil {
|
||||
return NewError(ErrorIO, err)
|
||||
}
|
||||
|
||||
out, err := json.MarshalIndent(results, "", " ")
|
||||
fmt.Println(string(out))
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/native"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/tests"
|
||||
|
|
@ -167,14 +168,15 @@ func Transition(ctx *cli.Context) error {
|
|||
}
|
||||
|
||||
// Configure tracer
|
||||
var tracer *tracers.Tracer
|
||||
if ctx.IsSet(TraceTracerFlag.Name) { // Custom tracing
|
||||
config := json.RawMessage(ctx.String(TraceTracerConfigFlag.Name))
|
||||
tracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name),
|
||||
innerTracer, err := tracers.DefaultDirectory.New(ctx.String(TraceTracerFlag.Name),
|
||||
nil, config, chainConfig)
|
||||
if err != nil {
|
||||
return NewError(ErrorConfig, fmt.Errorf("failed instantiating tracer: %v", err))
|
||||
}
|
||||
vmConfig.Tracer = newResultWriter(baseDir, tracer)
|
||||
tracer = newResultWriter(baseDir, innerTracer)
|
||||
} else if ctx.Bool(TraceFlag.Name) { // JSON opcode tracing
|
||||
logConfig := &logger.Config{
|
||||
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
|
||||
|
|
@ -182,20 +184,45 @@ func Transition(ctx *cli.Context) error {
|
|||
EnableReturnData: ctx.Bool(TraceEnableReturnDataFlag.Name),
|
||||
}
|
||||
if ctx.Bool(TraceEnableCallFramesFlag.Name) {
|
||||
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
|
||||
tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
|
||||
return logger.NewJSONLoggerWithCallFrames(logConfig, out)
|
||||
})
|
||||
} else {
|
||||
vmConfig.Tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
|
||||
tracer = newFileWriter(baseDir, func(out io.Writer) *tracing.Hooks {
|
||||
return logger.NewJSONLogger(logConfig, out)
|
||||
})
|
||||
}
|
||||
}
|
||||
// Configure opcode counter
|
||||
var opcodeTracer *tracers.Tracer
|
||||
if ctx.IsSet(OpcodeCountFlag.Name) && ctx.String(OpcodeCountFlag.Name) != "" {
|
||||
opcodeTracer = native.NewOpcodeCounter()
|
||||
if tracer != nil {
|
||||
// If we have an existing tracer, multiplex with the opcode tracer
|
||||
mux, _ := native.NewMuxTracer([]string{"trace", "opcode"}, []*tracers.Tracer{tracer, opcodeTracer})
|
||||
vmConfig.Tracer = mux.Hooks
|
||||
} else {
|
||||
vmConfig.Tracer = opcodeTracer.Hooks
|
||||
}
|
||||
} else if tracer != nil {
|
||||
vmConfig.Tracer = tracer.Hooks
|
||||
}
|
||||
// Run the test and aggregate the result
|
||||
s, result, body, err := prestate.Apply(vmConfig, chainConfig, txIt, ctx.Int64(RewardFlag.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Write opcode counts if enabled
|
||||
if opcodeTracer != nil {
|
||||
fname := ctx.String(OpcodeCountFlag.Name)
|
||||
result, err := opcodeTracer.GetResult()
|
||||
if err != nil {
|
||||
return NewError(ErrorJson, fmt.Errorf("failed getting opcode counts: %v", err))
|
||||
}
|
||||
if err := saveFile(baseDir, fname, result); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Dump the execution result
|
||||
var (
|
||||
collector = make(Alloc)
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ var (
|
|||
t8ntool.ForknameFlag,
|
||||
t8ntool.ChainIDFlag,
|
||||
t8ntool.RewardFlag,
|
||||
t8ntool.OpcodeCountFlag,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
|||
2
cmd/evm/testdata/1/exp.json
vendored
2
cmd/evm/testdata/1/exp.json
vendored
|
|
@ -24,7 +24,7 @@
|
|||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x5208",
|
||||
|
|
|
|||
4
cmd/evm/testdata/13/exp2.json
vendored
4
cmd/evm/testdata/13/exp2.json
vendored
|
|
@ -12,7 +12,7 @@
|
|||
"status": "0x0",
|
||||
"cumulativeGasUsed": "0x84d0",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x84d0",
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
"status": "0x0",
|
||||
"cumulativeGasUsed": "0x109a0",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x84d0",
|
||||
|
|
|
|||
2
cmd/evm/testdata/23/exp.json
vendored
2
cmd/evm/testdata/23/exp.json
vendored
|
|
@ -11,7 +11,7 @@
|
|||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0x520b",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x520b",
|
||||
|
|
|
|||
4
cmd/evm/testdata/24/exp.json
vendored
4
cmd/evm/testdata/24/exp.json
vendored
|
|
@ -27,7 +27,7 @@
|
|||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0xa861",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0x92ea4a28224d033afb20e0cc2b290d4c7c2d61f6a4800a680e4e19ac962ee941",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0xa861",
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0x10306",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0x16b1d912f1d664f3f60f4e1b5f296f3c82a64a1a253117b4851d18bc03c4f1da",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x5aa5",
|
||||
|
|
|
|||
2
cmd/evm/testdata/25/exp.json
vendored
2
cmd/evm/testdata/25/exp.json
vendored
|
|
@ -23,7 +23,7 @@
|
|||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0x92ea4a28224d033afb20e0cc2b290d4c7c2d61f6a4800a680e4e19ac962ee941",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x5208",
|
||||
|
|
|
|||
2
cmd/evm/testdata/28/exp.json
vendored
2
cmd/evm/testdata/28/exp.json
vendored
|
|
@ -28,7 +28,7 @@
|
|||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0xa865",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0x7508d7139d002a4b3a26a4f12dec0d87cb46075c78bf77a38b569a133b509262",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0xa865",
|
||||
|
|
|
|||
2
cmd/evm/testdata/29/exp.json
vendored
2
cmd/evm/testdata/29/exp.json
vendored
|
|
@ -26,7 +26,7 @@
|
|||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0x84f70aba406a55628a0620f26d260f90aeb6ccc55fed6ec2ac13dd4f727032ed",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x5208",
|
||||
|
|
|
|||
2
cmd/evm/testdata/3/exp.json
vendored
2
cmd/evm/testdata/3/exp.json
vendored
|
|
@ -24,7 +24,7 @@
|
|||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0x521f",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x521f",
|
||||
|
|
|
|||
4
cmd/evm/testdata/30/exp.json
vendored
4
cmd/evm/testdata/30/exp.json
vendored
|
|
@ -25,7 +25,7 @@
|
|||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0x5208",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x5208",
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0xa410",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"logs": null,
|
||||
"logs": [],
|
||||
"transactionHash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x5208",
|
||||
|
|
|
|||
2
cmd/evm/testdata/33/exp.json
vendored
2
cmd/evm/testdata/33/exp.json
vendored
|
|
@ -44,7 +44,7 @@
|
|||
"root": "0x",
|
||||
"status": "0x1",
|
||||
"cumulativeGasUsed": "0x15fa9",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","logs": null,"transactionHash": "0x0417aab7c1d8a3989190c3167c132876ce9b8afd99262c5a0f9d06802de3d7ef",
|
||||
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","logs": [],"transactionHash": "0x0417aab7c1d8a3989190c3167c132876ce9b8afd99262c5a0f9d06802de3d7ef",
|
||||
"contractAddress": "0x0000000000000000000000000000000000000000",
|
||||
"gasUsed": "0x15fa9",
|
||||
"effectiveGasPrice": null,
|
||||
|
|
|
|||
177
cmd/fetchpayload/main.go
Normal file
177
cmd/fetchpayload/main.go
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
// Copyright 2026 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// fetchpayload queries an Ethereum node over RPC, fetches a block and its
|
||||
// execution witness, and writes the combined Payload (ChainID + Block +
|
||||
// Witness) to disk in the format consumed by cmd/keeper.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/stateless"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// Payload is duplicated from cmd/keeper/main.go (package main, not importable).
|
||||
type Payload struct {
|
||||
ChainID uint64
|
||||
Block *types.Block
|
||||
Witness *stateless.Witness
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
rpcURL = flag.String("rpc", "http://localhost:8545", "RPC endpoint URL")
|
||||
blockArg = flag.String("block", "latest", `Block number: decimal, 0x-hex, or "latest"`)
|
||||
format = flag.String("format", "rlp", "Comma-separated output formats: rlp, hex, json")
|
||||
outDir = flag.String("out", "", "Output directory (default: current directory)")
|
||||
)
|
||||
flag.Parse()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Parse block number (nil means "latest" in ethclient).
|
||||
blockNum, err := parseBlockNumber(*blockArg)
|
||||
if err != nil {
|
||||
fatal("invalid block number %q: %v", *blockArg, err)
|
||||
}
|
||||
|
||||
// Connect to the node.
|
||||
client, err := ethclient.DialContext(ctx, *rpcURL)
|
||||
if err != nil {
|
||||
fatal("failed to connect to %s: %v", *rpcURL, err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
chainID, err := client.ChainID(ctx)
|
||||
if err != nil {
|
||||
fatal("failed to get chain ID: %v", err)
|
||||
}
|
||||
|
||||
// Fetch the block first so we have a concrete number for the witness call,
|
||||
// avoiding a race where "latest" advances between the two RPCs.
|
||||
block, err := client.BlockByNumber(ctx, blockNum)
|
||||
if err != nil {
|
||||
fatal("failed to fetch block: %v", err)
|
||||
}
|
||||
fmt.Printf("Fetched block %d (%#x)\n", block.NumberU64(), block.Hash())
|
||||
|
||||
// Fetch the execution witness via the debug namespace.
|
||||
var extWitness stateless.ExtWitness
|
||||
err = client.Client().CallContext(ctx, &extWitness, "debug_executionWitness", rpc.BlockNumber(block.NumberU64()))
|
||||
if err != nil {
|
||||
fatal("failed to fetch execution witness: %v", err)
|
||||
}
|
||||
|
||||
witness := new(stateless.Witness)
|
||||
err = witness.FromExtWitness(&extWitness)
|
||||
if err != nil {
|
||||
fatal("failed to convert witness: %v", err)
|
||||
}
|
||||
|
||||
payload := Payload{
|
||||
ChainID: chainID.Uint64(),
|
||||
Block: block,
|
||||
Witness: witness,
|
||||
}
|
||||
|
||||
// Encode payload as RLP (shared by "rlp" and "hex" formats).
|
||||
rlpBytes, err := rlp.EncodeToBytes(payload)
|
||||
if err != nil {
|
||||
fatal("failed to RLP-encode payload: %v", err)
|
||||
}
|
||||
|
||||
// Write one output file per requested format.
|
||||
blockHex := fmt.Sprintf("%x", block.NumberU64())
|
||||
for f := range strings.SplitSeq(*format, ",") {
|
||||
f = strings.TrimSpace(f)
|
||||
outPath := filepath.Join(*outDir, fmt.Sprintf("%s_payload.%s", blockHex, f))
|
||||
|
||||
var data []byte
|
||||
switch f {
|
||||
case "rlp":
|
||||
data = rlpBytes
|
||||
case "hex":
|
||||
data = []byte(hexutil.Encode(rlpBytes))
|
||||
case "json":
|
||||
data, err = marshalJSONPayload(chainID, block, &extWitness)
|
||||
if err != nil {
|
||||
fatal("failed to JSON-encode payload: %v", err)
|
||||
}
|
||||
default:
|
||||
fatal("unknown format %q (valid: rlp, hex, json)", f)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(outPath, data, 0644); err != nil {
|
||||
fatal("failed to write %s: %v", outPath, err)
|
||||
}
|
||||
fmt.Printf("Wrote %s (%d bytes)\n", outPath, len(data))
|
||||
}
|
||||
}
|
||||
|
||||
// parseBlockNumber converts a CLI string to *big.Int.
|
||||
// Returns nil for "latest" (ethclient convention for the head block).
|
||||
func parseBlockNumber(s string) (*big.Int, error) {
|
||||
if strings.EqualFold(s, "latest") {
|
||||
return nil, nil
|
||||
}
|
||||
n := new(big.Int)
|
||||
if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") {
|
||||
if _, ok := n.SetString(s[2:], 16); !ok {
|
||||
return nil, fmt.Errorf("invalid hex number")
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
if _, ok := n.SetString(s, 10); !ok {
|
||||
return nil, fmt.Errorf("invalid decimal number")
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// jsonPayload is a JSON-friendly representation of Payload. It uses ExtWitness
|
||||
// instead of the internal Witness (which has no JSON marshaling).
|
||||
type jsonPayload struct {
|
||||
ChainID uint64 `json:"chainId"`
|
||||
Block *types.Block `json:"block"`
|
||||
Witness *stateless.ExtWitness `json:"witness"`
|
||||
}
|
||||
|
||||
func marshalJSONPayload(chainID *big.Int, block *types.Block, ext *stateless.ExtWitness) ([]byte, error) {
|
||||
return json.MarshalIndent(jsonPayload{
|
||||
ChainID: chainID.Uint64(),
|
||||
Block: block,
|
||||
Witness: ext,
|
||||
}, "", " ")
|
||||
}
|
||||
|
||||
func fatal(format string, args ...any) {
|
||||
fmt.Fprintf(os.Stderr, format+"\n", args...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
|
@ -43,6 +44,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/internal/debug"
|
||||
"github.com/ethereum/go-ethereum/internal/era"
|
||||
"github.com/ethereum/go-ethereum/internal/era/eradl"
|
||||
"github.com/ethereum/go-ethereum/internal/era/execdb"
|
||||
"github.com/ethereum/go-ethereum/internal/era/onedb"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
|
|
@ -108,6 +111,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`,
|
|||
utils.MetricsInfluxDBUsernameFlag,
|
||||
utils.MetricsInfluxDBPasswordFlag,
|
||||
utils.MetricsInfluxDBTagsFlag,
|
||||
utils.MetricsInfluxDBIntervalFlag,
|
||||
utils.MetricsInfluxDBTokenFlag,
|
||||
utils.MetricsInfluxDBBucketFlag,
|
||||
utils.MetricsInfluxDBOrganizationFlag,
|
||||
|
|
@ -121,6 +125,7 @@ if one is set. Otherwise it prints the genesis from the datadir.`,
|
|||
utils.LogExportCheckpointsFlag,
|
||||
utils.StateHistoryFlag,
|
||||
utils.TrienodeHistoryFlag,
|
||||
utils.TrienodeHistoryFullValueCheckpointFlag,
|
||||
}, utils.DatabaseFlags, debug.Flags),
|
||||
Before: func(ctx *cli.Context) error {
|
||||
flags.MigrateGlobalFlags(ctx)
|
||||
|
|
@ -152,7 +157,7 @@ be gzipped.`,
|
|||
Name: "import-history",
|
||||
Usage: "Import an Era archive",
|
||||
ArgsUsage: "<dir>",
|
||||
Flags: slices.Concat([]cli.Flag{utils.TxLookupLimitFlag, utils.TransactionHistoryFlag}, utils.DatabaseFlags, utils.NetworkFlags),
|
||||
Flags: slices.Concat([]cli.Flag{utils.TxLookupLimitFlag, utils.TransactionHistoryFlag, utils.EraFormatFlag}, utils.DatabaseFlags, utils.NetworkFlags),
|
||||
Description: `
|
||||
The import-history command will import blocks and their corresponding receipts
|
||||
from Era archives.
|
||||
|
|
@ -163,7 +168,7 @@ from Era archives.
|
|||
Name: "export-history",
|
||||
Usage: "Export blockchain history to Era archives",
|
||||
ArgsUsage: "<dir> <first> <last>",
|
||||
Flags: utils.DatabaseFlags,
|
||||
Flags: slices.Concat([]cli.Flag{utils.EraFormatFlag}, utils.DatabaseFlags),
|
||||
Description: `
|
||||
The export-history command will export blocks and their corresponding receipts
|
||||
into Era archives. Eras are typically packaged in steps of 8192 blocks.
|
||||
|
|
@ -203,13 +208,19 @@ This command dumps out the state for a given block (or latest, if none provided)
|
|||
pruneHistoryCommand = &cli.Command{
|
||||
Action: pruneHistory,
|
||||
Name: "prune-history",
|
||||
Usage: "Prune blockchain history (block bodies and receipts) up to the merge block",
|
||||
Usage: "Prune blockchain history (block bodies and receipts) up to a specified point",
|
||||
ArgsUsage: "",
|
||||
Flags: utils.DatabaseFlags,
|
||||
Flags: slices.Concat(utils.DatabaseFlags, []cli.Flag{
|
||||
utils.ChainHistoryFlag,
|
||||
}),
|
||||
Description: `
|
||||
The prune-history command removes historical block bodies and receipts from the
|
||||
blockchain database up to the merge block, while preserving block headers. This
|
||||
helps reduce storage requirements for nodes that don't need full historical data.`,
|
||||
blockchain database up to a specified point, while preserving block headers. This
|
||||
helps reduce storage requirements for nodes that don't need full historical data.
|
||||
|
||||
The --history.chain flag is required to specify the pruning target:
|
||||
- postmerge: Prune up to the merge block. The node will keep the merge block and everything thereafter.
|
||||
- postprague: Prune up to the Prague (Pectra) upgrade block. The node will keep the prague block and everything thereafter.`,
|
||||
}
|
||||
|
||||
downloadEraCommand = &cli.Command{
|
||||
|
|
@ -515,15 +526,27 @@ func importHistory(ctx *cli.Context) error {
|
|||
network = networks[0]
|
||||
}
|
||||
|
||||
if err := utils.ImportHistory(chain, dir, network); err != nil {
|
||||
var (
|
||||
format = ctx.String(utils.EraFormatFlag.Name)
|
||||
from func(era.ReadAtSeekCloser) (era.Era, error)
|
||||
)
|
||||
switch format {
|
||||
case "era1", "era":
|
||||
from = onedb.From
|
||||
case "erae":
|
||||
from = execdb.From
|
||||
default:
|
||||
return fmt.Errorf("unknown --era.format %q (expected 'era1' or 'erae')", format)
|
||||
}
|
||||
if err := utils.ImportHistory(chain, dir, network, from); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Import done in %v\n", time.Since(start))
|
||||
return nil
|
||||
}
|
||||
|
||||
// exportHistory exports chain history in Era archives at a specified
|
||||
// directory.
|
||||
// exportHistory exports chain history in Era archives at a specified directory.
|
||||
func exportHistory(ctx *cli.Context) error {
|
||||
if ctx.Args().Len() != 3 {
|
||||
utils.Fatalf("usage: %s", ctx.Command.ArgsUsage)
|
||||
|
|
@ -549,10 +572,26 @@ func exportHistory(ctx *cli.Context) error {
|
|||
if head := chain.CurrentSnapBlock(); uint64(last) > head.Number.Uint64() {
|
||||
utils.Fatalf("Export error: block number %d larger than head block %d\n", uint64(last), head.Number.Uint64())
|
||||
}
|
||||
err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), uint64(era.MaxEra1Size))
|
||||
if err != nil {
|
||||
|
||||
var (
|
||||
format = ctx.String(utils.EraFormatFlag.Name)
|
||||
filename func(network string, epoch int, root common.Hash) string
|
||||
newBuilder func(w io.Writer) era.Builder
|
||||
)
|
||||
switch format {
|
||||
case "era1", "era":
|
||||
newBuilder = func(w io.Writer) era.Builder { return onedb.NewBuilder(w) }
|
||||
filename = func(network string, epoch int, root common.Hash) string { return onedb.Filename(network, epoch, root) }
|
||||
case "erae":
|
||||
newBuilder = func(w io.Writer) era.Builder { return execdb.NewBuilder(w) }
|
||||
filename = func(network string, epoch int, root common.Hash) string { return execdb.Filename(network, epoch, root) }
|
||||
default:
|
||||
return fmt.Errorf("unknown archive format %q (use 'era1' or 'erae')", format)
|
||||
}
|
||||
if err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), newBuilder, filename); err != nil {
|
||||
utils.Fatalf("Export error: %v\n", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Export done in %v\n", time.Since(start))
|
||||
return nil
|
||||
}
|
||||
|
|
@ -670,47 +709,77 @@ func hashish(x string) bool {
|
|||
}
|
||||
|
||||
func pruneHistory(ctx *cli.Context) error {
|
||||
// Parse and validate the history mode flag.
|
||||
if !ctx.IsSet(utils.ChainHistoryFlag.Name) {
|
||||
return errors.New("--history.chain flag is required")
|
||||
}
|
||||
var mode history.HistoryMode
|
||||
if err := mode.UnmarshalText([]byte(ctx.String(utils.ChainHistoryFlag.Name))); err != nil {
|
||||
return err
|
||||
}
|
||||
if mode == history.KeepAll {
|
||||
return errors.New("--history.chain=all is not valid for pruning. To restore history, use 'geth import-history'")
|
||||
}
|
||||
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
// Open the chain database
|
||||
// Open the chain database.
|
||||
chain, chaindb := utils.MakeChain(ctx, stack, false)
|
||||
defer chaindb.Close()
|
||||
defer chain.Stop()
|
||||
|
||||
// Determine the prune point. This will be the first PoS block.
|
||||
prunePoint, ok := history.PrunePoints[chain.Genesis().Hash()]
|
||||
if !ok || prunePoint == nil {
|
||||
return errors.New("prune point not found")
|
||||
// Determine the prune point based on the history mode.
|
||||
genesisHash := chain.Genesis().Hash()
|
||||
policy, err := history.NewPolicy(mode, genesisHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if policy.Target == nil {
|
||||
return fmt.Errorf("prune point for %q not found for this network", mode.String())
|
||||
}
|
||||
var (
|
||||
mergeBlock = prunePoint.BlockNumber
|
||||
mergeBlockHash = prunePoint.BlockHash.Hex()
|
||||
targetBlock = policy.Target.BlockNumber
|
||||
targetBlockHash = policy.Target.BlockHash
|
||||
)
|
||||
|
||||
// Check we're far enough past merge to ensure all data is in freezer
|
||||
// Check the current freezer tail to see if pruning is needed/possible.
|
||||
freezerTail, _ := chaindb.Tail()
|
||||
if freezerTail > 0 {
|
||||
if freezerTail == targetBlock {
|
||||
log.Info("Database already pruned to target block", "tail", freezerTail)
|
||||
return nil
|
||||
}
|
||||
if freezerTail > targetBlock {
|
||||
// Database is pruned beyond the target - can't unprune.
|
||||
return fmt.Errorf("database is already pruned to block %d, which is beyond target %d. Cannot unprune. To restore history, use 'geth import-history'", freezerTail, targetBlock)
|
||||
}
|
||||
// freezerTail < targetBlock: we can prune further, continue below.
|
||||
}
|
||||
|
||||
// Check we're far enough past the target to ensure all data is in freezer.
|
||||
currentHeader := chain.CurrentHeader()
|
||||
if currentHeader == nil {
|
||||
return errors.New("current header not found")
|
||||
}
|
||||
if currentHeader.Number.Uint64() < mergeBlock+params.FullImmutabilityThreshold {
|
||||
return fmt.Errorf("chain not far enough past merge block, need %d more blocks",
|
||||
mergeBlock+params.FullImmutabilityThreshold-currentHeader.Number.Uint64())
|
||||
if currentHeader.Number.Uint64() < targetBlock+params.FullImmutabilityThreshold {
|
||||
return fmt.Errorf("chain not far enough past target block %d, need %d more blocks",
|
||||
targetBlock, targetBlock+params.FullImmutabilityThreshold-currentHeader.Number.Uint64())
|
||||
}
|
||||
|
||||
// Double-check the prune block in db has the expected hash.
|
||||
hash := rawdb.ReadCanonicalHash(chaindb, mergeBlock)
|
||||
if hash != common.HexToHash(mergeBlockHash) {
|
||||
return fmt.Errorf("merge block hash mismatch: got %s, want %s", hash.Hex(), mergeBlockHash)
|
||||
// Double-check the target block in db has the expected hash.
|
||||
hash := rawdb.ReadCanonicalHash(chaindb, targetBlock)
|
||||
if hash != targetBlockHash {
|
||||
return fmt.Errorf("target block hash mismatch: got %s, want %s", hash.Hex(), targetBlockHash.Hex())
|
||||
}
|
||||
|
||||
log.Info("Starting history pruning", "head", currentHeader.Number, "tail", mergeBlock, "tailHash", mergeBlockHash)
|
||||
log.Info("Starting history pruning", "head", currentHeader.Number, "target", targetBlock, "targetHash", targetBlockHash.Hex())
|
||||
start := time.Now()
|
||||
rawdb.PruneTransactionIndex(chaindb, mergeBlock)
|
||||
if _, err := chaindb.TruncateTail(mergeBlock); err != nil {
|
||||
rawdb.PruneTransactionIndex(chaindb, targetBlock)
|
||||
if _, err := chaindb.TruncateTail(targetBlock); err != nil {
|
||||
return fmt.Errorf("failed to truncate ancient data: %v", err)
|
||||
}
|
||||
log.Info("History pruning completed", "tail", mergeBlock, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
log.Info("History pruning completed", "tail", targetBlock, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
|
||||
// TODO(s1na): what if there is a crash between the two prune operations?
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/eth/catalyst"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/internal/telemetry/tracesetup"
|
||||
"github.com/ethereum/go-ethereum/internal/version"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
|
|
@ -239,9 +240,15 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
|||
cfg.Eth.OverrideVerkle = &v
|
||||
}
|
||||
|
||||
// Start metrics export if enabled
|
||||
// Start metrics export if enabled.
|
||||
utils.SetupMetrics(&cfg.Metrics)
|
||||
|
||||
// Setup OpenTelemetry reporting if enabled.
|
||||
if err := tracesetup.SetupTelemetry(cfg.Node.OpenTelemetry, stack); err != nil {
|
||||
utils.Fatalf("failed to setup OpenTelemetry: %v", err)
|
||||
}
|
||||
|
||||
// Add Ethereum service.
|
||||
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
|
||||
|
||||
// Create gauge with geth system and build information
|
||||
|
|
@ -370,6 +377,9 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
|
|||
if ctx.IsSet(utils.MetricsInfluxDBTagsFlag.Name) {
|
||||
cfg.Metrics.InfluxDBTags = ctx.String(utils.MetricsInfluxDBTagsFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(utils.MetricsInfluxDBIntervalFlag.Name) {
|
||||
cfg.Metrics.InfluxDBInterval = ctx.Duration(utils.MetricsInfluxDBIntervalFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(utils.MetricsEnableInfluxDBV2Flag.Name) {
|
||||
cfg.Metrics.EnableInfluxDBV2 = ctx.Bool(utils.MetricsEnableInfluxDBV2Flag.Name)
|
||||
}
|
||||
|
|
@ -398,9 +408,9 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
|
|||
ctx.IsSet(utils.MetricsInfluxDBBucketFlag.Name)
|
||||
|
||||
if enableExport && v2FlagIsSet {
|
||||
utils.Fatalf("Flags --influxdb.metrics.organization, --influxdb.metrics.token, --influxdb.metrics.bucket are only available for influxdb-v2")
|
||||
utils.Fatalf("Flags --%s, --%s, --%s are only available for influxdb-v2", utils.MetricsInfluxDBOrganizationFlag.Name, utils.MetricsInfluxDBTokenFlag.Name, utils.MetricsInfluxDBBucketFlag.Name)
|
||||
} else if enableExportV2 && v1FlagIsSet {
|
||||
utils.Fatalf("Flags --influxdb.metrics.username, --influxdb.metrics.password are only available for influxdb-v1")
|
||||
utils.Fatalf("Flags --%s, --%s are only available for influxdb-v1", utils.MetricsInfluxDBUsernameFlag.Name, utils.MetricsInfluxDBPasswordFlag.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
ipcAPIs = "admin:1.0 debug:1.0 engine:1.0 eth:1.0 miner:1.0 net:1.0 rpc:1.0 txpool:1.0 web3:1.0"
|
||||
ipcAPIs = "admin:1.0 debug:1.0 engine:1.0 eth:1.0 miner:1.0 net:1.0 rpc:1.0 testing:1.0 txpool:1.0 web3:1.0"
|
||||
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package main
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
|
|
@ -37,6 +38,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/internal/tablewriter"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
|
|
@ -51,7 +53,24 @@ var (
|
|||
}
|
||||
removeChainDataFlag = &cli.BoolFlag{
|
||||
Name: "remove.chain",
|
||||
Usage: "If set, selects the state data for removal",
|
||||
Usage: "If set, selects the chain data for removal",
|
||||
}
|
||||
inspectTrieTopFlag = &cli.IntFlag{
|
||||
Name: "top",
|
||||
Usage: "Print the top N results per ranking category",
|
||||
Value: 10,
|
||||
}
|
||||
inspectTrieDumpPathFlag = &cli.StringFlag{
|
||||
Name: "dump-path",
|
||||
Usage: "Path for the trie statistics dump file",
|
||||
}
|
||||
inspectTrieSummarizeFlag = &cli.StringFlag{
|
||||
Name: "summarize",
|
||||
Usage: "Summarize an existing trie dump file (skip trie traversal)",
|
||||
}
|
||||
inspectTrieContractFlag = &cli.StringFlag{
|
||||
Name: "contract",
|
||||
Usage: "Inspect only the storage of the given contract address (skips full account trie walk)",
|
||||
}
|
||||
|
||||
removedbCommand = &cli.Command{
|
||||
|
|
@ -74,6 +93,7 @@ Remove blockchain and state databases`,
|
|||
dbCompactCmd,
|
||||
dbGetCmd,
|
||||
dbDeleteCmd,
|
||||
dbInspectTrieCmd,
|
||||
dbPutCmd,
|
||||
dbGetSlotsCmd,
|
||||
dbDumpFreezerIndex,
|
||||
|
|
@ -92,6 +112,22 @@ Remove blockchain and state databases`,
|
|||
Usage: "Inspect the storage size for each type of data in the database",
|
||||
Description: `This commands iterates the entire database. If the optional 'prefix' and 'start' arguments are provided, then the iteration is limited to the given subset of data.`,
|
||||
}
|
||||
dbInspectTrieCmd = &cli.Command{
|
||||
Action: inspectTrie,
|
||||
Name: "inspect-trie",
|
||||
ArgsUsage: "<blocknum>",
|
||||
Flags: slices.Concat([]cli.Flag{
|
||||
utils.ExcludeStorageFlag,
|
||||
inspectTrieTopFlag,
|
||||
utils.OutputFileFlag,
|
||||
inspectTrieDumpPathFlag,
|
||||
inspectTrieSummarizeFlag,
|
||||
inspectTrieContractFlag,
|
||||
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||
Usage: "Print detailed trie information about the structure of account trie and storage tries.",
|
||||
Description: `This commands iterates the entrie trie-backed state. If the 'blocknum' is not specified,
|
||||
the latest block number will be used by default.`,
|
||||
}
|
||||
dbCheckStateContentCmd = &cli.Command{
|
||||
Action: checkStateContent,
|
||||
Name: "check-state-content",
|
||||
|
|
@ -385,6 +421,88 @@ func checkStateContent(ctx *cli.Context) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func inspectTrie(ctx *cli.Context) error {
|
||||
topN := ctx.Int(inspectTrieTopFlag.Name)
|
||||
if topN <= 0 {
|
||||
return fmt.Errorf("invalid --%s value %d (must be > 0)", inspectTrieTopFlag.Name, topN)
|
||||
}
|
||||
config := &trie.InspectConfig{
|
||||
NoStorage: ctx.Bool(utils.ExcludeStorageFlag.Name),
|
||||
TopN: topN,
|
||||
Path: ctx.String(utils.OutputFileFlag.Name),
|
||||
}
|
||||
|
||||
if summarizePath := ctx.String(inspectTrieSummarizeFlag.Name); summarizePath != "" {
|
||||
if ctx.NArg() > 0 {
|
||||
return fmt.Errorf("block number argument is not supported with --%s", inspectTrieSummarizeFlag.Name)
|
||||
}
|
||||
config.DumpPath = summarizePath
|
||||
log.Info("Summarizing trie dump", "path", summarizePath, "top", topN)
|
||||
return trie.Summarize(summarizePath, config)
|
||||
}
|
||||
if ctx.NArg() > 1 {
|
||||
return fmt.Errorf("excessive number of arguments: %v", ctx.Command.ArgsUsage)
|
||||
}
|
||||
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
db := utils.MakeChainDatabase(ctx, stack, false)
|
||||
defer stack.Close()
|
||||
defer db.Close()
|
||||
|
||||
var (
|
||||
trieRoot common.Hash
|
||||
hash common.Hash
|
||||
number uint64
|
||||
)
|
||||
switch {
|
||||
case ctx.NArg() == 0 || ctx.Args().Get(0) == "latest":
|
||||
head := rawdb.ReadHeadHeaderHash(db)
|
||||
n, ok := rawdb.ReadHeaderNumber(db, head)
|
||||
if !ok {
|
||||
return fmt.Errorf("could not load head block hash")
|
||||
}
|
||||
number = n
|
||||
case ctx.Args().Get(0) == "snapshot":
|
||||
trieRoot = rawdb.ReadSnapshotRoot(db)
|
||||
number = math.MaxUint64
|
||||
default:
|
||||
var err error
|
||||
number, err = strconv.ParseUint(ctx.Args().Get(0), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse blocknum, Args[0]: %v, err: %v", ctx.Args().Get(0), err)
|
||||
}
|
||||
}
|
||||
|
||||
if number != math.MaxUint64 {
|
||||
hash = rawdb.ReadCanonicalHash(db, number)
|
||||
if hash == (common.Hash{}) {
|
||||
return fmt.Errorf("canonical hash for block %d not found", number)
|
||||
}
|
||||
blockHeader := rawdb.ReadHeader(db, hash, number)
|
||||
trieRoot = blockHeader.Root
|
||||
}
|
||||
if trieRoot == (common.Hash{}) {
|
||||
log.Error("Empty root hash")
|
||||
}
|
||||
|
||||
config.DumpPath = ctx.String(inspectTrieDumpPathFlag.Name)
|
||||
if config.DumpPath == "" {
|
||||
config.DumpPath = stack.ResolvePath("trie-dump.bin")
|
||||
}
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
if contractAddr := ctx.String(inspectTrieContractFlag.Name); contractAddr != "" {
|
||||
address := common.HexToAddress(contractAddr)
|
||||
log.Info("Inspecting contract", "address", address, "root", trieRoot, "block", number)
|
||||
return trie.InspectContract(triedb, db, trieRoot, address)
|
||||
}
|
||||
|
||||
log.Info("Inspecting trie", "root", trieRoot, "block", number, "dump", config.DumpPath, "top", topN)
|
||||
return trie.Inspect(triedb, trieRoot, config)
|
||||
}
|
||||
|
||||
func showDBStats(db ethdb.KeyValueStater) {
|
||||
stats, err := db.Stat()
|
||||
if err != nil {
|
||||
|
|
@ -759,7 +877,7 @@ func showMetaData(ctx *cli.Context) error {
|
|||
data = append(data, []string{"headHeader.Root", fmt.Sprintf("%v", h.Root)})
|
||||
data = append(data, []string{"headHeader.Number", fmt.Sprintf("%d (%#x)", h.Number, h.Number)})
|
||||
}
|
||||
table := rawdb.NewTableWriter(os.Stdout)
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Field", "Value"})
|
||||
table.AppendBulk(data)
|
||||
table.Render()
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import (
|
|||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
|
|
@ -95,6 +94,7 @@ var (
|
|||
utils.LogExportCheckpointsFlag,
|
||||
utils.StateHistoryFlag,
|
||||
utils.TrienodeHistoryFlag,
|
||||
utils.TrienodeHistoryFullValueCheckpointFlag,
|
||||
utils.LightKDFFlag,
|
||||
utils.EthRequiredBlocksFlag,
|
||||
utils.LegacyWhitelistFlag, // deprecated
|
||||
|
|
@ -195,6 +195,13 @@ var (
|
|||
utils.RPCTxSyncDefaultTimeoutFlag,
|
||||
utils.RPCTxSyncMaxTimeoutFlag,
|
||||
utils.RPCGlobalRangeLimitFlag,
|
||||
utils.RPCTelemetryFlag,
|
||||
utils.RPCTelemetryEndpointFlag,
|
||||
utils.RPCTelemetryUserFlag,
|
||||
utils.RPCTelemetryPasswordFlag,
|
||||
utils.RPCTelemetryInstanceIDFlag,
|
||||
utils.RPCTelemetryTagsFlag,
|
||||
utils.RPCTelemetrySampleRatioFlag,
|
||||
}
|
||||
|
||||
metricsFlags = []cli.Flag{
|
||||
|
|
@ -208,6 +215,7 @@ var (
|
|||
utils.MetricsInfluxDBUsernameFlag,
|
||||
utils.MetricsInfluxDBPasswordFlag,
|
||||
utils.MetricsInfluxDBTagsFlag,
|
||||
utils.MetricsInfluxDBIntervalFlag,
|
||||
utils.MetricsEnableInfluxDBV2Flag,
|
||||
utils.MetricsInfluxDBTokenFlag,
|
||||
utils.MetricsInfluxDBBucketFlag,
|
||||
|
|
@ -307,18 +315,6 @@ func prepare(ctx *cli.Context) {
|
|||
case !ctx.IsSet(utils.NetworkIdFlag.Name):
|
||||
log.Info("Starting Geth on Ethereum mainnet...")
|
||||
}
|
||||
// If we're a full node on mainnet without --cache specified, bump default cache allowance
|
||||
if !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) {
|
||||
// Make sure we're not on any supported preconfigured testnet either
|
||||
if !ctx.IsSet(utils.HoleskyFlag.Name) &&
|
||||
!ctx.IsSet(utils.SepoliaFlag.Name) &&
|
||||
!ctx.IsSet(utils.HoodiFlag.Name) &&
|
||||
!ctx.IsSet(utils.DeveloperFlag.Name) {
|
||||
// Nope, we're really on mainnet. Bump that cache up!
|
||||
log.Info("Bumping default cache on mainnet", "provided", ctx.Int(utils.CacheFlag.Name), "updated", 4096)
|
||||
ctx.Set(utils.CacheFlag.Name, strconv.Itoa(4096))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// geth is the main entry point into the system if no special subcommand is run.
|
||||
|
|
|
|||
36
cmd/keeper/getpayload_wasm.go
Normal file
36
cmd/keeper/getpayload_wasm.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2026 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/>.
|
||||
|
||||
//go:build wasm
|
||||
// +build wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:wasmimport geth_io len
|
||||
func hintLen() uint32
|
||||
|
||||
//go:wasmimport geth_io read
|
||||
func hintRead(data unsafe.Pointer)
|
||||
|
||||
func getInput() []byte {
|
||||
data := make([]byte, hintLen())
|
||||
hintRead(unsafe.Pointer(&data[0]))
|
||||
return data
|
||||
}
|
||||
|
|
@ -13,13 +13,15 @@ require (
|
|||
github.com/bits-and-blooms/bitset v1.20.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/consensys/gnark-crypto v0.18.1 // indirect
|
||||
github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
|
||||
github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect
|
||||
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/emicklei/dot v1.6.2 // indirect
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5 // indirect
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect
|
||||
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect
|
||||
github.com/ferranbt/fastssz v0.1.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
|
|
@ -29,13 +31,16 @@ require (
|
|||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
|
||||
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe // indirect
|
||||
github.com/supranational/blst v0.3.16 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect
|
||||
golang.org/x/sync v0.12.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.40.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.40.0 // indirect
|
||||
golang.org/x/crypto v0.44.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAK
|
|||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
|
||||
github.com/consensys/gnark-crypto v0.18.1 h1:RyLV6UhPRoYYzaFnPQA4qK3DyuDgkTgskDdoGqFt3fI=
|
||||
github.com/consensys/gnark-crypto v0.18.1/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
|
||||
github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=
|
||||
github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
|
||||
github.com/crate-crypto/go-eth-kzg v1.5.0 h1:FYRiJMJG2iv+2Dy3fi14SVGjcPteZ5HAAUe4YWlJygc=
|
||||
github.com/crate-crypto/go-eth-kzg v1.5.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
|
||||
|
|
@ -40,14 +40,19 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1
|
|||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
|
||||
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
|
||||
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.6 h1:xQymkKCT5E2Jiaoqf3v4wsNgjZLY0lRSkZn27fRjSls=
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.6/go.mod h1:8HMkUZ5JRv4hpw/XUrYWSQNAUzhHMg2UDb/U+5m+XNw=
|
||||
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
|
||||
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
|
||||
github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
|
||||
github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
|
||||
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
|
||||
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
|
|
@ -59,6 +64,10 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
|||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
|
||||
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
|
||||
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
|
||||
|
|
@ -102,28 +111,38 @@ github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1
|
|||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe h1:nbdqkIGOGfUAD54q1s2YBcBz/WcsxCO9HUQ4aGV5hUw=
|
||||
github.com/supranational/blst v0.3.16-0.20250831170142-f48500c1fdbe/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE=
|
||||
github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
|
||||
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME=
|
||||
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
|
@ -52,7 +53,7 @@ func main() {
|
|||
}
|
||||
vmConfig := vm.Config{}
|
||||
|
||||
crossStateRoot, crossReceiptRoot, err := core.ExecuteStateless(chainConfig, vmConfig, payload.Block, payload.Witness)
|
||||
crossStateRoot, crossReceiptRoot, err := core.ExecuteStateless(context.Background(), chainConfig, vmConfig, payload.Block, payload.Witness)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "stateless self-validation failed: %v\n", err)
|
||||
os.Exit(10)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
// 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/>.
|
||||
|
||||
//go:build !example && !ziren
|
||||
//go:build !example && !ziren && !wasm
|
||||
// +build !example,!ziren,!wasm
|
||||
|
||||
package main
|
||||
|
||||
|
|
|
|||
158
cmd/utils/cmd.go
158
cmd/utils/cmd.go
|
|
@ -57,6 +57,8 @@ const (
|
|||
importBatchSize = 2500
|
||||
)
|
||||
|
||||
type EraFileFormat int
|
||||
|
||||
// ErrImportInterrupted is returned when the user interrupts the import process.
|
||||
var ErrImportInterrupted = errors.New("interrupted")
|
||||
|
||||
|
|
@ -250,7 +252,7 @@ func readList(filename string) ([]string, error) {
|
|||
// ImportHistory imports Era1 files containing historical block information,
|
||||
// starting from genesis. The assumption is held that the provided chain
|
||||
// segment in Era1 file should all be canonical and verified.
|
||||
func ImportHistory(chain *core.BlockChain, dir string, network string) error {
|
||||
func ImportHistory(chain *core.BlockChain, dir string, network string, from func(f era.ReadAtSeekCloser) (era.Era, error)) error {
|
||||
if chain.CurrentSnapBlock().Number.BitLen() != 0 {
|
||||
return errors.New("history import only supported when starting from genesis")
|
||||
}
|
||||
|
|
@ -263,42 +265,49 @@ func ImportHistory(chain *core.BlockChain, dir string, network string) error {
|
|||
return fmt.Errorf("unable to read checksums.txt: %w", err)
|
||||
}
|
||||
if len(checksums) != len(entries) {
|
||||
return fmt.Errorf("expected equal number of checksums and entries, have: %d checksums, %d entries", len(checksums), len(entries))
|
||||
return fmt.Errorf("expected equal number of checksums and entries, have: %d checksums, %d entries",
|
||||
len(checksums), len(entries))
|
||||
}
|
||||
|
||||
var (
|
||||
start = time.Now()
|
||||
reported = time.Now()
|
||||
imported = 0
|
||||
h = sha256.New()
|
||||
buf = bytes.NewBuffer(nil)
|
||||
scratch = bytes.NewBuffer(nil)
|
||||
)
|
||||
for i, filename := range entries {
|
||||
|
||||
for i, file := range entries {
|
||||
err := func() error {
|
||||
f, err := os.Open(filepath.Join(dir, filename))
|
||||
path := filepath.Join(dir, file)
|
||||
|
||||
// validate against checksum file in directory
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to open era: %w", err)
|
||||
return fmt.Errorf("open %s: %w", path, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Validate checksum.
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return fmt.Errorf("unable to recalculate checksum: %w", err)
|
||||
}
|
||||
if have, want := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex(), checksums[i]; have != want {
|
||||
return fmt.Errorf("checksum mismatch: have %s, want %s", have, want)
|
||||
return fmt.Errorf("checksum %s: %w", path, err)
|
||||
}
|
||||
got := common.BytesToHash(h.Sum(scratch.Bytes()[:])).Hex()
|
||||
want := checksums[i]
|
||||
h.Reset()
|
||||
buf.Reset()
|
||||
scratch.Reset()
|
||||
|
||||
if got != want {
|
||||
return fmt.Errorf("%s checksum mismatch: have %s want %s", file, got, want)
|
||||
}
|
||||
// Import all block data from Era1.
|
||||
e, err := era.From(f)
|
||||
e, err := from(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening era: %w", err)
|
||||
}
|
||||
it, err := era.NewIterator(e)
|
||||
it, err := e.Iterator()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making era reader: %w", err)
|
||||
return fmt.Errorf("error creating iterator: %w", err)
|
||||
}
|
||||
|
||||
for it.Next() {
|
||||
block, err := it.Block()
|
||||
if err != nil {
|
||||
|
|
@ -311,26 +320,28 @@ func ImportHistory(chain *core.BlockChain, dir string, network string) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
||||
}
|
||||
encReceipts := types.EncodeBlockReceiptLists([]types.Receipts{receipts})
|
||||
if _, err := chain.InsertReceiptChain([]*types.Block{block}, encReceipts, math.MaxUint64); err != nil {
|
||||
enc := types.EncodeBlockReceiptLists([]types.Receipts{receipts})
|
||||
if _, err := chain.InsertReceiptChain([]*types.Block{block}, enc, math.MaxUint64); err != nil {
|
||||
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
||||
}
|
||||
imported += 1
|
||||
imported++
|
||||
|
||||
// Give the user some feedback that something is happening.
|
||||
if time.Since(reported) >= 8*time.Second {
|
||||
log.Info("Importing Era files", "head", it.Number(), "imported", imported, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
log.Info("Importing Era files", "head", it.Number(), "imported", imported,
|
||||
"elapsed", common.PrettyDuration(time.Since(start)))
|
||||
imported = 0
|
||||
reported = time.Now()
|
||||
}
|
||||
}
|
||||
if err := it.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -389,7 +400,6 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
|
|||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
var writer io.Writer = fh
|
||||
if strings.HasSuffix(fn, ".gz") {
|
||||
writer = gzip.NewWriter(writer)
|
||||
|
|
@ -405,7 +415,7 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
|
|||
|
||||
// ExportHistory exports blockchain history into the specified directory,
|
||||
// following the Era format.
|
||||
func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64) error {
|
||||
func ExportHistory(bc *core.BlockChain, dir string, first, last uint64, newBuilder func(io.Writer) era.Builder, filename func(network string, epoch int, lastBlockHash common.Hash) string) error {
|
||||
log.Info("Exporting blockchain history", "dir", dir)
|
||||
if head := bc.CurrentBlock().Number.Uint64(); head < last {
|
||||
log.Warn("Last block beyond head, setting last = head", "head", head, "last", last)
|
||||
|
|
@ -418,76 +428,100 @@ func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64) er
|
|||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("error creating output directory: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
start = time.Now()
|
||||
reported = time.Now()
|
||||
h = sha256.New()
|
||||
buf = bytes.NewBuffer(nil)
|
||||
td = new(big.Int)
|
||||
checksums []string
|
||||
)
|
||||
td := new(big.Int)
|
||||
for i := uint64(0); i < first; i++ {
|
||||
td.Add(td, bc.GetHeaderByNumber(i).Difficulty)
|
||||
|
||||
// Compute initial TD by accumulating difficulty from genesis to first-1.
|
||||
// This is necessary because TD is no longer stored in the database. Only
|
||||
// compute if a segment of the export is pre-merge.
|
||||
b := bc.GetBlockByNumber(first)
|
||||
if b == nil {
|
||||
return fmt.Errorf("block #%d not found", first)
|
||||
}
|
||||
for i := first; i <= last; i += step {
|
||||
err := func() error {
|
||||
filename := filepath.Join(dir, era.Filename(network, int(i/step), common.Hash{}))
|
||||
f, err := os.Create(filename)
|
||||
if first > 0 && b.Difficulty().Sign() != 0 {
|
||||
log.Info("Computing initial total difficulty", "from", 0, "to", first-1)
|
||||
for i := uint64(0); i < first; i++ {
|
||||
b := bc.GetBlockByNumber(i)
|
||||
if b == nil {
|
||||
return fmt.Errorf("block #%d not found while computing initial TD", i)
|
||||
}
|
||||
td.Add(td, b.Difficulty())
|
||||
}
|
||||
log.Info("Initial total difficulty computed", "td", td)
|
||||
}
|
||||
|
||||
for batch := first; batch <= last; batch += uint64(era.MaxSize) {
|
||||
idx := int(batch / uint64(era.MaxSize))
|
||||
tmpPath := filepath.Join(dir, filename(network, idx, common.Hash{}))
|
||||
|
||||
if err := func() error {
|
||||
f, err := os.Create(tmpPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create era file: %w", err)
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
w := era.NewBuilder(f)
|
||||
for j := uint64(0); j < step && j <= last-i; j++ {
|
||||
var (
|
||||
n = i + j
|
||||
block = bc.GetBlockByNumber(n)
|
||||
)
|
||||
builder := newBuilder(f)
|
||||
|
||||
for j := uint64(0); j < uint64(era.MaxSize) && batch+j <= last; j++ {
|
||||
n := batch + j
|
||||
block := bc.GetBlockByNumber(n)
|
||||
if block == nil {
|
||||
return fmt.Errorf("export failed on #%d: not found", n)
|
||||
return fmt.Errorf("block #%d not found", n)
|
||||
}
|
||||
receipts := bc.GetReceiptsByHash(block.Hash())
|
||||
if receipts == nil {
|
||||
return fmt.Errorf("export failed on #%d: receipts not found", n)
|
||||
receipt := bc.GetReceiptsByHash(block.Hash())
|
||||
if receipt == nil {
|
||||
return fmt.Errorf("receipts for #%d missing", n)
|
||||
}
|
||||
td.Add(td, block.Difficulty())
|
||||
if err := w.Add(block, receipts, new(big.Int).Set(td)); err != nil {
|
||||
|
||||
// For pre-merge blocks, pass accumulated TD.
|
||||
// For post-merge blocks (difficulty == 0), pass nil TD.
|
||||
var blockTD *big.Int
|
||||
if block.Difficulty().Sign() != 0 {
|
||||
td.Add(td, block.Difficulty())
|
||||
blockTD = new(big.Int).Set(td)
|
||||
}
|
||||
|
||||
if err := builder.Add(block, receipt, blockTD); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
root, err := w.Finalize()
|
||||
id, err := builder.Finalize()
|
||||
if err != nil {
|
||||
return fmt.Errorf("export failed to finalize %d: %w", step/i, err)
|
||||
return err
|
||||
}
|
||||
// Set correct filename with root.
|
||||
os.Rename(filename, filepath.Join(dir, era.Filename(network, int(i/step), root)))
|
||||
|
||||
// Compute checksum of entire Era1.
|
||||
if _, err := f.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return fmt.Errorf("unable to calculate checksum: %w", err)
|
||||
}
|
||||
checksums = append(checksums, common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex())
|
||||
h.Reset()
|
||||
buf.Reset()
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return err
|
||||
}
|
||||
checksums = append(checksums, common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex())
|
||||
|
||||
// Close before rename. It's required on Windows.
|
||||
f.Close()
|
||||
final := filepath.Join(dir, filename(network, idx, id))
|
||||
return os.Rename(tmpPath, final)
|
||||
}(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if time.Since(reported) >= 8*time.Second {
|
||||
log.Info("Exporting blocks", "exported", i, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
log.Info("export progress", "exported", batch, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
reported = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
os.WriteFile(filepath.Join(dir, "checksums.txt"), []byte(strings.Join(checksums, "\n")), os.ModePerm)
|
||||
|
||||
log.Info("Exported blockchain to", "dir", dir)
|
||||
|
||||
_ = os.WriteFile(filepath.Join(dir, "checksums.txt"), []byte(strings.Join(checksums, "\n")), os.ModePerm)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -218,7 +218,11 @@ var (
|
|||
Usage: "Max number of elements (0 = no limit)",
|
||||
Value: 0,
|
||||
}
|
||||
|
||||
OutputFileFlag = &cli.StringFlag{
|
||||
Name: "output",
|
||||
Usage: "Writes the result in json to the output",
|
||||
Value: "",
|
||||
}
|
||||
SnapshotFlag = &cli.BoolFlag{
|
||||
Name: "snapshot",
|
||||
Usage: `Enables snapshot-database mode (default = enable)`,
|
||||
|
|
@ -301,6 +305,12 @@ var (
|
|||
Value: ethconfig.Defaults.TrienodeHistory,
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
TrienodeHistoryFullValueCheckpointFlag = &cli.UintFlag{
|
||||
Name: "history.trienode.full-value-checkpoint",
|
||||
Usage: "The frequency of full-value encoding. Every n-th node is stored in full-value format; all other nodes are stored as diffs relative to their predecessor",
|
||||
Value: uint(ethconfig.Defaults.NodeFullValueCheckpoint),
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
TransactionHistoryFlag = &cli.Uint64Flag{
|
||||
Name: "history.transactions",
|
||||
Usage: "Number of recent blocks to maintain transactions index for (default = about one year, 0 = entire chain)",
|
||||
|
|
@ -309,7 +319,7 @@ var (
|
|||
}
|
||||
ChainHistoryFlag = &cli.StringFlag{
|
||||
Name: "history.chain",
|
||||
Usage: `Blockchain history retention ("all" or "postmerge")`,
|
||||
Usage: `Blockchain history retention ("all", "postmerge", or "postprague")`,
|
||||
Value: ethconfig.Defaults.HistoryMode.String(),
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
|
|
@ -474,8 +484,8 @@ var (
|
|||
// Performance tuning settings
|
||||
CacheFlag = &cli.IntFlag{
|
||||
Name: "cache",
|
||||
Usage: "Megabytes of memory allocated to internal caching (default = 4096 mainnet full node, 128 light mode)",
|
||||
Value: 1024,
|
||||
Usage: "Megabytes of memory allocated to internal caching",
|
||||
Value: 4096,
|
||||
Category: flags.PerfCategory,
|
||||
}
|
||||
CacheDatabaseFlag = &cli.IntFlag{
|
||||
|
|
@ -686,7 +696,7 @@ var (
|
|||
}
|
||||
LogSlowBlockFlag = &cli.DurationFlag{
|
||||
Name: "debug.logslowblock",
|
||||
Usage: "Block execution time threshold beyond which detailed statistics will be logged (0 means disable)",
|
||||
Usage: "Block execution time threshold beyond which detailed statistics will be logged (0 logs all blocks, negative means disable)",
|
||||
Value: ethconfig.Defaults.SlowBlockThreshold,
|
||||
Category: flags.LoggingCategory,
|
||||
}
|
||||
|
|
@ -1010,6 +1020,13 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
|||
Category: flags.MetricsCategory,
|
||||
}
|
||||
|
||||
MetricsInfluxDBIntervalFlag = &cli.DurationFlag{
|
||||
Name: "metrics.influxdb.interval",
|
||||
Usage: "Interval between metrics reports to InfluxDB (with time unit, e.g. 10s)",
|
||||
Value: metrics.DefaultConfig.InfluxDBInterval,
|
||||
Category: flags.MetricsCategory,
|
||||
}
|
||||
|
||||
MetricsEnableInfluxDBV2Flag = &cli.BoolFlag{
|
||||
Name: "metrics.influxdbv2",
|
||||
Usage: "Enable metrics export/push to an external InfluxDB v2 database",
|
||||
|
|
@ -1036,6 +1053,55 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
|||
Value: metrics.DefaultConfig.InfluxDBOrganization,
|
||||
Category: flags.MetricsCategory,
|
||||
}
|
||||
|
||||
// RPC Telemetry
|
||||
RPCTelemetryFlag = &cli.BoolFlag{
|
||||
Name: "rpc.telemetry",
|
||||
Usage: "Enable RPC telemetry",
|
||||
Category: flags.APICategory,
|
||||
}
|
||||
|
||||
RPCTelemetryEndpointFlag = &cli.StringFlag{
|
||||
Name: "rpc.telemetry.endpoint",
|
||||
Usage: "Defines where RPC telemetry is sent (e.g., http://localhost:4318)",
|
||||
Category: flags.APICategory,
|
||||
}
|
||||
|
||||
RPCTelemetryUserFlag = &cli.StringFlag{
|
||||
Name: "rpc.telemetry.username",
|
||||
Usage: "HTTP Basic Auth username for OpenTelemetry",
|
||||
Category: flags.APICategory,
|
||||
}
|
||||
|
||||
RPCTelemetryPasswordFlag = &cli.StringFlag{
|
||||
Name: "rpc.telemetry.password",
|
||||
Usage: "HTTP Basic Auth password for OpenTelemetry",
|
||||
Category: flags.APICategory,
|
||||
}
|
||||
|
||||
RPCTelemetryInstanceIDFlag = &cli.StringFlag{
|
||||
Name: "rpc.telemetry.instance-id",
|
||||
Usage: "OpenTelemetry instance ID",
|
||||
Category: flags.APICategory,
|
||||
}
|
||||
|
||||
RPCTelemetryTagsFlag = &cli.StringFlag{
|
||||
Name: "rpc.telemetry.tags",
|
||||
Usage: "Comma-separated tags (key/values) added as attributes to the OpenTelemetry resource struct",
|
||||
Category: flags.APICategory,
|
||||
}
|
||||
|
||||
RPCTelemetrySampleRatioFlag = &cli.Float64Flag{
|
||||
Name: "rpc.telemetry.sample-ratio",
|
||||
Usage: "Defines the sampling ratio for RPC telemetry (0.0 to 1.0)",
|
||||
Value: 1.0,
|
||||
Category: flags.APICategory,
|
||||
}
|
||||
// Era flags are a group of flags related to the era archive format.
|
||||
EraFormatFlag = &cli.StringFlag{
|
||||
Name: "era.format",
|
||||
Usage: "Archive format: 'era1' or 'erae'",
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -1426,6 +1492,7 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
|||
setNodeUserIdent(ctx, cfg)
|
||||
SetDataDir(ctx, cfg)
|
||||
setSmartCard(ctx, cfg)
|
||||
setOpenTelemetry(ctx, cfg)
|
||||
|
||||
if ctx.IsSet(JWTSecretFlag.Name) {
|
||||
cfg.JWTSecret = ctx.String(JWTSecretFlag.Name)
|
||||
|
|
@ -1493,6 +1560,33 @@ func setSmartCard(ctx *cli.Context, cfg *node.Config) {
|
|||
cfg.SmartCardDaemonPath = path
|
||||
}
|
||||
|
||||
func setOpenTelemetry(ctx *cli.Context, cfg *node.Config) {
|
||||
tcfg := &cfg.OpenTelemetry
|
||||
if ctx.IsSet(RPCTelemetryFlag.Name) {
|
||||
tcfg.Enabled = ctx.Bool(RPCTelemetryFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(RPCTelemetryEndpointFlag.Name) {
|
||||
tcfg.Endpoint = ctx.String(RPCTelemetryEndpointFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(RPCTelemetryUserFlag.Name) {
|
||||
tcfg.AuthUser = ctx.String(RPCTelemetryUserFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(RPCTelemetryPasswordFlag.Name) {
|
||||
tcfg.AuthPassword = ctx.String(RPCTelemetryPasswordFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(RPCTelemetryInstanceIDFlag.Name) {
|
||||
tcfg.InstanceID = ctx.String(RPCTelemetryInstanceIDFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(RPCTelemetryTagsFlag.Name) {
|
||||
tcfg.Tags = ctx.String(RPCTelemetryTagsFlag.Name)
|
||||
}
|
||||
tcfg.SampleRatio = ctx.Float64(RPCTelemetrySampleRatioFlag.Name)
|
||||
|
||||
if tcfg.Endpoint != "" && !tcfg.Enabled {
|
||||
log.Warn(fmt.Sprintf("OpenTelemetry endpoint configured but telemetry is not enabled, use --%s to enable.", RPCTelemetryFlag.Name))
|
||||
}
|
||||
}
|
||||
|
||||
func SetDataDir(ctx *cli.Context, cfg *node.Config) {
|
||||
switch {
|
||||
case ctx.IsSet(DataDirFlag.Name):
|
||||
|
|
@ -1714,6 +1808,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
if ctx.IsSet(TrienodeHistoryFlag.Name) {
|
||||
cfg.TrienodeHistory = ctx.Int64(TrienodeHistoryFlag.Name)
|
||||
}
|
||||
if ctx.IsSet(TrienodeHistoryFullValueCheckpointFlag.Name) {
|
||||
cfg.NodeFullValueCheckpoint = uint32(ctx.Uint(TrienodeHistoryFullValueCheckpointFlag.Name))
|
||||
}
|
||||
if ctx.IsSet(StateSchemeFlag.Name) {
|
||||
cfg.StateScheme = ctx.String(StateSchemeFlag.Name)
|
||||
}
|
||||
|
|
@ -2160,13 +2257,14 @@ func SetupMetrics(cfg *metrics.Config) {
|
|||
bucket = cfg.InfluxDBBucket
|
||||
organization = cfg.InfluxDBOrganization
|
||||
tagsMap = SplitTagsFlag(cfg.InfluxDBTags)
|
||||
interval = cfg.InfluxDBInterval
|
||||
)
|
||||
if enableExport {
|
||||
log.Info("Enabling metrics export to InfluxDB")
|
||||
go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap)
|
||||
log.Info("Enabling metrics export to InfluxDB", "interval", interval)
|
||||
go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, interval, endpoint, database, username, password, "geth.", tagsMap)
|
||||
} else if enableExportV2 {
|
||||
log.Info("Enabling metrics export to InfluxDB (v2)")
|
||||
go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, token, bucket, organization, "geth.", tagsMap)
|
||||
log.Info("Enabling metrics export to InfluxDB (v2)", "interval", interval)
|
||||
go influxdb.InfluxDBV2WithTags(metrics.DefaultRegistry, interval, endpoint, token, bucket, organization, "geth.", tagsMap)
|
||||
}
|
||||
|
||||
// Expvar exporter.
|
||||
|
|
@ -2318,16 +2416,17 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
|||
Fatalf("%v", err)
|
||||
}
|
||||
options := &core.BlockChainConfig{
|
||||
TrieCleanLimit: ethconfig.Defaults.TrieCleanCache,
|
||||
NoPrefetch: ctx.Bool(CacheNoPrefetchFlag.Name),
|
||||
TrieDirtyLimit: ethconfig.Defaults.TrieDirtyCache,
|
||||
ArchiveMode: ctx.String(GCModeFlag.Name) == "archive",
|
||||
TrieTimeLimit: ethconfig.Defaults.TrieTimeout,
|
||||
SnapshotLimit: ethconfig.Defaults.SnapshotCache,
|
||||
Preimages: ctx.Bool(CachePreimagesFlag.Name),
|
||||
StateScheme: scheme,
|
||||
StateHistory: ctx.Uint64(StateHistoryFlag.Name),
|
||||
TrienodeHistory: ctx.Int64(TrienodeHistoryFlag.Name),
|
||||
TrieCleanLimit: ethconfig.Defaults.TrieCleanCache,
|
||||
NoPrefetch: ctx.Bool(CacheNoPrefetchFlag.Name),
|
||||
TrieDirtyLimit: ethconfig.Defaults.TrieDirtyCache,
|
||||
ArchiveMode: ctx.String(GCModeFlag.Name) == "archive",
|
||||
TrieTimeLimit: ethconfig.Defaults.TrieTimeout,
|
||||
SnapshotLimit: ethconfig.Defaults.SnapshotCache,
|
||||
Preimages: ctx.Bool(CachePreimagesFlag.Name),
|
||||
StateScheme: scheme,
|
||||
StateHistory: ctx.Uint64(StateHistoryFlag.Name),
|
||||
TrienodeHistory: ctx.Int64(TrienodeHistoryFlag.Name),
|
||||
NodeFullValueCheckpoint: uint32(ctx.Uint(TrienodeHistoryFullValueCheckpointFlag.Name)),
|
||||
|
||||
// Disable transaction indexing/unindexing.
|
||||
TxLookupLimit: -1,
|
||||
|
|
@ -2341,8 +2440,12 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
|||
// Enable state size tracking if enabled
|
||||
StateSizeTracking: ctx.Bool(StateSizeTrackingFlag.Name),
|
||||
|
||||
// Configure the slow block statistic logger
|
||||
SlowBlockThreshold: ctx.Duration(LogSlowBlockFlag.Name),
|
||||
// Configure the slow block statistic logger (disabled by default)
|
||||
SlowBlockThreshold: ethconfig.Defaults.SlowBlockThreshold,
|
||||
}
|
||||
// Only enable slow block logging if the flag was explicitly set
|
||||
if ctx.IsSet(LogSlowBlockFlag.Name) {
|
||||
options.SlowBlockThreshold = ctx.Duration(LogSlowBlockFlag.Name)
|
||||
}
|
||||
if options.ArchiveMode && !options.Preimages {
|
||||
options.Preimages = true
|
||||
|
|
@ -2366,8 +2469,6 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
|||
}
|
||||
vmcfg := vm.Config{
|
||||
EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name),
|
||||
EnableWitnessStats: ctx.Bool(VMWitnessStatsFlag.Name),
|
||||
StatelessSelfValidation: ctx.Bool(VMStatelessSelfValidationFlag.Name) || ctx.Bool(VMWitnessStatsFlag.Name),
|
||||
}
|
||||
if ctx.IsSet(VMTraceFlag.Name) {
|
||||
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||
|
|
@ -2381,6 +2482,9 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
|||
}
|
||||
options.VmConfig = vmcfg
|
||||
|
||||
options.StatelessSelfValidation = ctx.Bool(VMStatelessSelfValidationFlag.Name) || ctx.Bool(VMWitnessStatsFlag.Name)
|
||||
options.EnableWitnessStats = ctx.Bool(VMWitnessStatsFlag.Name)
|
||||
|
||||
chain, err := core.NewBlockChain(chainDb, gspec, engine, options)
|
||||
if err != nil {
|
||||
Fatalf("Can't create BlockChain: %v", err)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/internal/era"
|
||||
"github.com/ethereum/go-ethereum/internal/era/execdb"
|
||||
"github.com/ethereum/go-ethereum/internal/era/onedb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
|
|
@ -44,136 +46,148 @@ var (
|
|||
)
|
||||
|
||||
func TestHistoryImportAndExport(t *testing.T) {
|
||||
var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
genesis = &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: types.GenesisAlloc{address: {Balance: big.NewInt(1000000000000000000)}},
|
||||
}
|
||||
signer = types.LatestSigner(genesis.Config)
|
||||
)
|
||||
|
||||
// Generate chain.
|
||||
db, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), int(count), func(i int, g *core.BlockGen) {
|
||||
if i == 0 {
|
||||
return
|
||||
}
|
||||
tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{
|
||||
ChainID: genesis.Config.ChainID,
|
||||
Nonce: uint64(i - 1),
|
||||
GasTipCap: common.Big0,
|
||||
GasFeeCap: g.PrevBlock(0).BaseFee(),
|
||||
Gas: 50000,
|
||||
To: &common.Address{0xaa},
|
||||
Value: big.NewInt(int64(i)),
|
||||
Data: nil,
|
||||
AccessList: nil,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tx: %v", err)
|
||||
}
|
||||
g.AddTx(tx)
|
||||
})
|
||||
|
||||
// Initialize BlockChain.
|
||||
chain, err := core.NewBlockChain(db, genesis, ethash.NewFaker(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize chain: %v", err)
|
||||
}
|
||||
if _, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("error inserting chain: %v", err)
|
||||
}
|
||||
|
||||
// Make temp directory for era files.
|
||||
dir := t.TempDir()
|
||||
|
||||
// Export history to temp directory.
|
||||
if err := ExportHistory(chain, dir, 0, count, step); err != nil {
|
||||
t.Fatalf("error exporting history: %v", err)
|
||||
}
|
||||
|
||||
// Read checksums.
|
||||
b, err := os.ReadFile(filepath.Join(dir, "checksums.txt"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read checksums: %v", err)
|
||||
}
|
||||
checksums := strings.Split(string(b), "\n")
|
||||
|
||||
// Verify each Era.
|
||||
entries, _ := era.ReadDir(dir, "mainnet")
|
||||
for i, filename := range entries {
|
||||
func() {
|
||||
f, err := os.Open(filepath.Join(dir, filename))
|
||||
if err != nil {
|
||||
t.Fatalf("error opening era file: %v", err)
|
||||
}
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
builder func(io.Writer) era.Builder
|
||||
filename func(network string, epoch int, root common.Hash) string
|
||||
from func(f era.ReadAtSeekCloser) (era.Era, error)
|
||||
}{
|
||||
{"era1", onedb.NewBuilder, onedb.Filename, onedb.From},
|
||||
{"erae", execdb.NewBuilder, execdb.Filename, execdb.From},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var (
|
||||
h = sha256.New()
|
||||
buf = bytes.NewBuffer(nil)
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
genesis = &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: types.GenesisAlloc{address: {Balance: big.NewInt(1000000000000000000)}},
|
||||
}
|
||||
signer = types.LatestSigner(genesis.Config)
|
||||
)
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
t.Fatalf("unable to recalculate checksum: %v", err)
|
||||
}
|
||||
if got, want := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex(), checksums[i]; got != want {
|
||||
t.Fatalf("checksum %d does not match: got %s, want %s", i, got, want)
|
||||
}
|
||||
e, err := era.From(f)
|
||||
if err != nil {
|
||||
t.Fatalf("error opening era: %v", err)
|
||||
}
|
||||
defer e.Close()
|
||||
it, err := era.NewIterator(e)
|
||||
if err != nil {
|
||||
t.Fatalf("error making era reader: %v", err)
|
||||
}
|
||||
for j := 0; it.Next(); j++ {
|
||||
n := i*int(step) + j
|
||||
if it.Error() != nil {
|
||||
t.Fatalf("error reading block entry %d: %v", n, it.Error())
|
||||
|
||||
// Generate chain.
|
||||
db, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), int(count), func(i int, g *core.BlockGen) {
|
||||
if i == 0 {
|
||||
return
|
||||
}
|
||||
block, receipts, err := it.BlockAndReceipts()
|
||||
tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{
|
||||
ChainID: genesis.Config.ChainID,
|
||||
Nonce: uint64(i - 1),
|
||||
GasTipCap: common.Big0,
|
||||
GasFeeCap: g.PrevBlock(0).BaseFee(),
|
||||
Gas: 50000,
|
||||
To: &common.Address{0xaa},
|
||||
Value: big.NewInt(int64(i)),
|
||||
Data: nil,
|
||||
AccessList: nil,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("error reading block entry %d: %v", n, err)
|
||||
}
|
||||
want := chain.GetBlockByNumber(uint64(n))
|
||||
if want, got := uint64(n), block.NumberU64(); want != got {
|
||||
t.Fatalf("blocks out of order: want %d, got %d", want, got)
|
||||
}
|
||||
if want.Hash() != block.Hash() {
|
||||
t.Fatalf("block hash mismatch %d: want %s, got %s", n, want.Hash().Hex(), block.Hash().Hex())
|
||||
}
|
||||
if got := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); got != want.TxHash() {
|
||||
t.Fatalf("tx hash %d mismatch: want %s, got %s", n, want.TxHash(), got)
|
||||
}
|
||||
if got := types.CalcUncleHash(block.Uncles()); got != want.UncleHash() {
|
||||
t.Fatalf("uncle hash %d mismatch: want %s, got %s", n, want.UncleHash(), got)
|
||||
}
|
||||
if got := types.DeriveSha(receipts, trie.NewStackTrie(nil)); got != want.ReceiptHash() {
|
||||
t.Fatalf("receipt root %d mismatch: want %s, got %s", n, want.ReceiptHash(), got)
|
||||
t.Fatalf("error creating tx: %v", err)
|
||||
}
|
||||
g.AddTx(tx)
|
||||
})
|
||||
|
||||
// Initialize BlockChain.
|
||||
chain, err := core.NewBlockChain(db, genesis, ethash.NewFaker(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize chain: %v", err)
|
||||
}
|
||||
if _, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("error inserting chain: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Now import Era.
|
||||
db2, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
db2.Close()
|
||||
})
|
||||
// Make temp directory for era files.
|
||||
dir := t.TempDir()
|
||||
|
||||
genesis.MustCommit(db2, triedb.NewDatabase(db2, triedb.HashDefaults))
|
||||
imported, err := core.NewBlockChain(db2, genesis, ethash.NewFaker(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize chain: %v", err)
|
||||
}
|
||||
if err := ImportHistory(imported, dir, "mainnet"); err != nil {
|
||||
t.Fatalf("failed to import chain: %v", err)
|
||||
}
|
||||
if have, want := imported.CurrentHeader(), chain.CurrentHeader(); have.Hash() != want.Hash() {
|
||||
t.Fatalf("imported chain does not match expected, have (%d, %s) want (%d, %s)", have.Number, have.Hash(), want.Number, want.Hash())
|
||||
// Export history to temp directory.
|
||||
if err := ExportHistory(chain, dir, 0, count, tt.builder, tt.filename); err != nil {
|
||||
t.Fatalf("error exporting history: %v", err)
|
||||
}
|
||||
|
||||
// Read checksums.
|
||||
b, err := os.ReadFile(filepath.Join(dir, "checksums.txt"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read checksums: %v", err)
|
||||
}
|
||||
checksums := strings.Split(string(b), "\n")
|
||||
|
||||
// Verify each Era.
|
||||
entries, _ := era.ReadDir(dir, "mainnet")
|
||||
for i, filename := range entries {
|
||||
func() {
|
||||
f, err := os.Open(filepath.Join(dir, filename))
|
||||
if err != nil {
|
||||
t.Fatalf("error opening era file: %v", err)
|
||||
}
|
||||
var (
|
||||
h = sha256.New()
|
||||
buf = bytes.NewBuffer(nil)
|
||||
)
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
t.Fatalf("unable to recalculate checksum: %v", err)
|
||||
}
|
||||
if got, want := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex(), checksums[i]; got != want {
|
||||
t.Fatalf("checksum %d does not match: got %s, want %s", i, got, want)
|
||||
}
|
||||
e, err := tt.from(f)
|
||||
if err != nil {
|
||||
t.Fatalf("error opening era: %v", err)
|
||||
}
|
||||
defer e.Close()
|
||||
it, err := e.Iterator()
|
||||
if err != nil {
|
||||
t.Fatalf("error making era reader: %v", err)
|
||||
}
|
||||
for j := 0; it.Next(); j++ {
|
||||
n := i*int(step) + j
|
||||
if it.Error() != nil {
|
||||
t.Fatalf("error reading block entry %d: %v", n, it.Error())
|
||||
}
|
||||
block, receipts, err := it.BlockAndReceipts()
|
||||
if err != nil {
|
||||
t.Fatalf("error reading block entry %d: %v", n, err)
|
||||
}
|
||||
want := chain.GetBlockByNumber(uint64(n))
|
||||
if want, got := uint64(n), block.NumberU64(); want != got {
|
||||
t.Fatalf("blocks out of order: want %d, got %d", want, got)
|
||||
}
|
||||
if want.Hash() != block.Hash() {
|
||||
t.Fatalf("block hash mismatch %d: want %s, got %s", n, want.Hash().Hex(), block.Hash().Hex())
|
||||
}
|
||||
if got := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); got != want.TxHash() {
|
||||
t.Fatalf("tx hash %d mismatch: want %s, got %s", n, want.TxHash(), got)
|
||||
}
|
||||
if got := types.CalcUncleHash(block.Uncles()); got != want.UncleHash() {
|
||||
t.Fatalf("uncle hash %d mismatch: want %s, got %s", n, want.UncleHash(), got)
|
||||
}
|
||||
if got := types.DeriveSha(receipts, trie.NewStackTrie(nil)); got != want.ReceiptHash() {
|
||||
t.Fatalf("receipt root %d mismatch: want %s, got %s", n, want.ReceiptHash(), got)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Now import Era.
|
||||
db2, err := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
db2.Close()
|
||||
})
|
||||
|
||||
genesis.MustCommit(db2, triedb.NewDatabase(db2, triedb.HashDefaults))
|
||||
imported, err := core.NewBlockChain(db2, genesis, ethash.NewFaker(), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize chain: %v", err)
|
||||
}
|
||||
if err := ImportHistory(imported, dir, "mainnet", tt.from); err != nil {
|
||||
t.Fatalf("failed to import chain: %v", err)
|
||||
}
|
||||
if have, want := imported.CurrentHeader(), chain.CurrentHeader(); have.Hash() != want.Hash() {
|
||||
t.Fatalf("imported chain does not match expected, have (%d, %s) want (%d, %s)", have.Number, have.Hash(), want.Number, want.Hash())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,7 +155,9 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
|
|||
}
|
||||
|
||||
cfg.historyPruneBlock = new(uint64)
|
||||
*cfg.historyPruneBlock = history.PrunePoints[params.MainnetGenesisHash].BlockNumber
|
||||
if p, err := history.NewPolicy(history.KeepPostMerge, params.MainnetGenesisHash); err == nil {
|
||||
*cfg.historyPruneBlock = p.Target.BlockNumber
|
||||
}
|
||||
case ctx.Bool(testSepoliaFlag.Name):
|
||||
cfg.fsys = builtinTestFiles
|
||||
if ctx.IsSet(filterQueryFileFlag.Name) {
|
||||
|
|
@ -180,7 +182,9 @@ func testConfigFromCLI(ctx *cli.Context) (cfg testConfig) {
|
|||
}
|
||||
|
||||
cfg.historyPruneBlock = new(uint64)
|
||||
*cfg.historyPruneBlock = history.PrunePoints[params.SepoliaGenesisHash].BlockNumber
|
||||
if p, err := history.NewPolicy(history.KeepPostMerge, params.SepoliaGenesisHash); err == nil {
|
||||
*cfg.historyPruneBlock = p.Target.BlockNumber
|
||||
}
|
||||
default:
|
||||
cfg.fsys = os.DirFS(".")
|
||||
cfg.filterQueryFile = ctx.String(filterQueryFileFlag.Name)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||
)
|
||||
|
||||
// Lengths of hashes and addresses in bytes.
|
||||
|
|
@ -271,7 +271,7 @@ func (a *Address) checksumHex() []byte {
|
|||
buf := a.hex()
|
||||
|
||||
// compute checksum
|
||||
sha := sha3.NewLegacyKeccak256()
|
||||
sha := keccak.NewLegacyKeccak256()
|
||||
sha.Write(buf[2:])
|
||||
hash := sha.Sum(nil)
|
||||
for i := 2; i < len(buf); i++ {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package beacon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
|
@ -29,6 +30,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/internal/telemetry"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/holiman/uint256"
|
||||
|
|
@ -272,6 +274,14 @@ func (beacon *Beacon) verifyHeader(chain consensus.ChainHeaderReader, header, pa
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
amsterdam := chain.Config().IsAmsterdam(header.Number, header.Time)
|
||||
if amsterdam && header.SlotNumber == nil {
|
||||
return errors.New("header is missing slotNumber")
|
||||
}
|
||||
if !amsterdam && header.SlotNumber != nil {
|
||||
return fmt.Errorf("invalid slotNumber: have %d, expected nil", *header.SlotNumber)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -343,9 +353,17 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
|
|||
|
||||
// FinalizeAndAssemble implements consensus.Engine, setting the final state and
|
||||
// assembling the block.
|
||||
func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
func (beacon *Beacon) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (result *types.Block, err error) {
|
||||
ctx, _, spanEnd := telemetry.StartSpan(ctx, "consensus.beacon.FinalizeAndAssemble",
|
||||
telemetry.Int64Attribute("block.number", int64(header.Number.Uint64())),
|
||||
telemetry.Int64Attribute("txs.count", int64(len(body.Transactions))),
|
||||
telemetry.Int64Attribute("withdrawals.count", int64(len(body.Withdrawals))),
|
||||
)
|
||||
defer spanEnd(&err)
|
||||
|
||||
if !beacon.IsPoSHeader(header) {
|
||||
return beacon.ethone.FinalizeAndAssemble(chain, header, state, body, receipts)
|
||||
block, delegateErr := beacon.ethone.FinalizeAndAssemble(ctx, chain, header, state, body, receipts)
|
||||
return block, delegateErr
|
||||
}
|
||||
shanghai := chain.Config().IsShanghai(header.Number, header.Time)
|
||||
if shanghai {
|
||||
|
|
@ -359,13 +377,20 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
|
|||
}
|
||||
}
|
||||
// Finalize and assemble the block.
|
||||
_, _, finalizeSpanEnd := telemetry.StartSpan(ctx, "consensus.beacon.Finalize")
|
||||
beacon.Finalize(chain, header, state, body)
|
||||
finalizeSpanEnd(nil)
|
||||
|
||||
// Assign the final state root to header.
|
||||
_, _, rootSpanEnd := telemetry.StartSpan(ctx, "consensus.beacon.IntermediateRoot")
|
||||
header.Root = state.IntermediateRoot(true)
|
||||
rootSpanEnd(nil)
|
||||
|
||||
// Assemble the final block.
|
||||
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)), nil
|
||||
_, _, blockSpanEnd := telemetry.StartSpan(ctx, "consensus.beacon.NewBlock")
|
||||
block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))
|
||||
blockSpanEnd(nil)
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// Seal generates a new sealing request for the given input block and pushes
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package clique
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
|
@ -37,12 +38,12 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -310,6 +311,8 @@ func (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
|||
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", *header.BlobGasUsed)
|
||||
case header.ParentBeaconRoot != nil:
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", *header.ParentBeaconRoot)
|
||||
case header.SlotNumber != nil:
|
||||
return fmt.Errorf("invalid slotNumber, have %#x, expected nil", *header.SlotNumber)
|
||||
}
|
||||
// All basic checks passed, verify cascading fields
|
||||
return c.verifyCascadingFields(chain, header, parents)
|
||||
|
|
@ -579,7 +582,7 @@ func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Heade
|
|||
|
||||
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
|
||||
// nor block rewards given, and returns the final block.
|
||||
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
func (c *Clique) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
if len(body.Withdrawals) > 0 {
|
||||
return nil, errors.New("clique does not support withdrawals")
|
||||
}
|
||||
|
|
@ -642,7 +645,7 @@ func (c *Clique) Close() error {
|
|||
|
||||
// SealHash returns the hash of a block prior to it being sealed.
|
||||
func SealHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher := keccak.NewLegacyKeccak256()
|
||||
encodeSigHeader(hasher, header)
|
||||
hasher.(crypto.KeccakState).Read(hash[:])
|
||||
return hash
|
||||
|
|
@ -694,6 +697,9 @@ func encodeSigHeader(w io.Writer, header *types.Header) {
|
|||
if header.ParentBeaconRoot != nil {
|
||||
panic("unexpected parent beacon root value in clique")
|
||||
}
|
||||
if header.SlotNumber != nil {
|
||||
panic("unexpected slot number value in clique")
|
||||
}
|
||||
if err := rlp.Encode(w, enc); err != nil {
|
||||
panic("can't encode: " + err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package consensus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -92,7 +93,7 @@ type Engine interface {
|
|||
//
|
||||
// Note: The block header and state database might be updated to reflect any
|
||||
// consensus rules that happen at finalization (e.g. block rewards).
|
||||
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error)
|
||||
FinalizeAndAssemble(ctx context.Context, chain ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error)
|
||||
|
||||
// Seal generates a new sealing request for the given input block and pushes
|
||||
// the result into the given channel.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package ethash
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
|
@ -31,11 +32,11 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/tracing"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/holiman/uint256"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// Ethash proof-of-work protocol constants.
|
||||
|
|
@ -283,6 +284,8 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, pa
|
|||
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", *header.BlobGasUsed)
|
||||
case header.ParentBeaconRoot != nil:
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", *header.ParentBeaconRoot)
|
||||
case header.SlotNumber != nil:
|
||||
return fmt.Errorf("invalid slotNumber, have %#x, expected nil", *header.SlotNumber)
|
||||
}
|
||||
// Add some fake checks for tests
|
||||
if ethash.fakeDelay != nil {
|
||||
|
|
@ -511,7 +514,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.
|
|||
|
||||
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
|
||||
// uncle rewards, setting the final state and assembling the block.
|
||||
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
func (ethash *Ethash) FinalizeAndAssemble(ctx context.Context, chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, body *types.Body, receipts []*types.Receipt) (*types.Block, error) {
|
||||
if len(body.Withdrawals) > 0 {
|
||||
return nil, errors.New("ethash does not support withdrawals")
|
||||
}
|
||||
|
|
@ -527,7 +530,7 @@ func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
|
|||
|
||||
// SealHash returns the hash of a block prior to it being sealed.
|
||||
func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher := keccak.NewLegacyKeccak256()
|
||||
|
||||
enc := []interface{}{
|
||||
header.ParentHash,
|
||||
|
|
@ -559,6 +562,9 @@ func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
|
|||
if header.ParentBeaconRoot != nil {
|
||||
panic("parent beacon root set on ethash")
|
||||
}
|
||||
if header.SlotNumber != nil {
|
||||
panic("slot number set on ethash")
|
||||
}
|
||||
rlp.Encode(hasher, enc)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ func VerifyEIP1559Header(config *params.ChainConfig, parent, header *types.Heade
|
|||
if header.BaseFee == nil {
|
||||
return errors.New("header is missing baseFee")
|
||||
}
|
||||
// Verify the parent header is not malformed
|
||||
if config.IsLondon(parent.Number) && parent.BaseFee == nil {
|
||||
return errors.New("parent header is missing baseFee")
|
||||
}
|
||||
// Verify the baseFee is correct based on the parent header.
|
||||
expectedBaseFee := CalcBaseFee(config, parent)
|
||||
if header.BaseFee.Cmp(expectedBaseFee) != 0 {
|
||||
|
|
|
|||
|
|
@ -53,9 +53,9 @@ func (bc *BlobConfig) blobPrice(excessBlobGas uint64) *big.Int {
|
|||
return new(big.Int).Mul(f, big.NewInt(params.BlobTxBlobGasPerBlob))
|
||||
}
|
||||
|
||||
func latestBlobConfig(cfg *params.ChainConfig, time uint64) *BlobConfig {
|
||||
func latestBlobConfig(cfg *params.ChainConfig, time uint64) (BlobConfig, error) {
|
||||
if cfg.BlobScheduleConfig == nil {
|
||||
return nil
|
||||
return BlobConfig{}, errors.New("no blob config")
|
||||
}
|
||||
var (
|
||||
london = cfg.LondonBlock
|
||||
|
|
@ -80,14 +80,14 @@ func latestBlobConfig(cfg *params.ChainConfig, time uint64) *BlobConfig {
|
|||
case cfg.IsCancun(london, time) && s.Cancun != nil:
|
||||
bc = s.Cancun
|
||||
default:
|
||||
return nil
|
||||
return BlobConfig{}, errors.New("no blob config")
|
||||
}
|
||||
|
||||
return &BlobConfig{
|
||||
return BlobConfig{
|
||||
Target: bc.Target,
|
||||
Max: bc.Max,
|
||||
UpdateFraction: bc.UpdateFraction,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
// VerifyEIP4844Header verifies the presence of the excessBlobGas field and that
|
||||
|
|
@ -98,8 +98,8 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade
|
|||
panic("bad header pair")
|
||||
}
|
||||
|
||||
bcfg := latestBlobConfig(config, header.Time)
|
||||
if bcfg == nil {
|
||||
bcfg, err := latestBlobConfig(config, header.Time)
|
||||
if err != nil {
|
||||
panic("called before EIP-4844 is active")
|
||||
}
|
||||
|
||||
|
|
@ -130,11 +130,14 @@ func VerifyEIP4844Header(config *params.ChainConfig, parent, header *types.Heade
|
|||
// blobs on top of the excess blob gas.
|
||||
func CalcExcessBlobGas(config *params.ChainConfig, parent *types.Header, headTimestamp uint64) uint64 {
|
||||
isOsaka := config.IsOsaka(config.LondonBlock, headTimestamp)
|
||||
bcfg := latestBlobConfig(config, headTimestamp)
|
||||
bcfg, err := latestBlobConfig(config, headTimestamp)
|
||||
if err != nil {
|
||||
panic("calculating excess blob gas on nil blob config")
|
||||
}
|
||||
return calcExcessBlobGas(isOsaka, bcfg, parent)
|
||||
}
|
||||
|
||||
func calcExcessBlobGas(isOsaka bool, bcfg *BlobConfig, parent *types.Header) uint64 {
|
||||
func calcExcessBlobGas(isOsaka bool, bcfg BlobConfig, parent *types.Header) uint64 {
|
||||
var parentExcessBlobGas, parentBlobGasUsed uint64
|
||||
if parent.ExcessBlobGas != nil {
|
||||
parentExcessBlobGas = *parent.ExcessBlobGas
|
||||
|
|
@ -169,8 +172,8 @@ func calcExcessBlobGas(isOsaka bool, bcfg *BlobConfig, parent *types.Header) uin
|
|||
|
||||
// CalcBlobFee calculates the blobfee from the header's excess blob gas field.
|
||||
func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int {
|
||||
blobConfig := latestBlobConfig(config, header.Time)
|
||||
if blobConfig == nil {
|
||||
blobConfig, err := latestBlobConfig(config, header.Time)
|
||||
if err != nil {
|
||||
panic("calculating blob fee on unsupported fork")
|
||||
}
|
||||
return blobConfig.blobBaseFee(*header.ExcessBlobGas)
|
||||
|
|
@ -178,8 +181,8 @@ func CalcBlobFee(config *params.ChainConfig, header *types.Header) *big.Int {
|
|||
|
||||
// MaxBlobsPerBlock returns the max blobs per block for a block at the given timestamp.
|
||||
func MaxBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
|
||||
blobConfig := latestBlobConfig(cfg, time)
|
||||
if blobConfig == nil {
|
||||
blobConfig, err := latestBlobConfig(cfg, time)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return blobConfig.Max
|
||||
|
|
@ -193,8 +196,8 @@ func MaxBlobGasPerBlock(cfg *params.ChainConfig, time uint64) uint64 {
|
|||
// LatestMaxBlobsPerBlock returns the latest max blobs per block defined by the
|
||||
// configuration, regardless of the currently active fork.
|
||||
func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {
|
||||
bcfg := latestBlobConfig(cfg, math.MaxUint64)
|
||||
if bcfg == nil {
|
||||
bcfg, err := latestBlobConfig(cfg, math.MaxUint64)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return bcfg.Max
|
||||
|
|
@ -202,8 +205,8 @@ func LatestMaxBlobsPerBlock(cfg *params.ChainConfig) int {
|
|||
|
||||
// TargetBlobsPerBlock returns the target blobs per block for a block at the given timestamp.
|
||||
func TargetBlobsPerBlock(cfg *params.ChainConfig, time uint64) int {
|
||||
blobConfig := latestBlobConfig(cfg, time)
|
||||
if blobConfig == nil {
|
||||
blobConfig, err := latestBlobConfig(cfg, time)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return blobConfig.Target
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ func (c *Console) AutoCompleteInput(line string, pos int) (string, []string, str
|
|||
for ; start > 0; start-- {
|
||||
// Skip all methods and namespaces (i.e. including the dot)
|
||||
c := line[start]
|
||||
if c == '.' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '1' && c <= '9') {
|
||||
if c == '.' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') {
|
||||
continue
|
||||
}
|
||||
// We've hit an unexpected character, autocomplete form here
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
|
@ -210,7 +211,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
|||
t.Fatalf("post-block %d: unexpected result returned: %v", i, result)
|
||||
case <-time.After(25 * time.Millisecond):
|
||||
}
|
||||
chain.InsertBlockWithoutSetHead(postBlocks[i], false)
|
||||
chain.InsertBlockWithoutSetHead(context.Background(), postBlocks[i], false)
|
||||
}
|
||||
|
||||
// Verify the blocks with pre-merge blocks and post-merge blocks
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
|
@ -47,6 +48,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/internal/syncx"
|
||||
"github.com/ethereum/go-ethereum/internal/telemetry"
|
||||
"github.com/ethereum/go-ethereum/internal/version"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
|
|
@ -76,6 +78,7 @@ var (
|
|||
storageUpdateTimer = metrics.NewRegisteredResettingTimer("chain/storage/updates", nil)
|
||||
storageCommitTimer = metrics.NewRegisteredResettingTimer("chain/storage/commits", nil)
|
||||
codeReadTimer = metrics.NewRegisteredResettingTimer("chain/code/reads", nil)
|
||||
codeReadBytesTimer = metrics.NewRegisteredResettingTimer("chain/code/readbytes", nil)
|
||||
|
||||
accountCacheHitMeter = metrics.NewRegisteredMeter("chain/account/reads/cache/process/hit", nil)
|
||||
accountCacheMissMeter = metrics.NewRegisteredMeter("chain/account/reads/cache/process/miss", nil)
|
||||
|
|
@ -90,9 +93,7 @@ var (
|
|||
accountReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/account/single/reads", nil)
|
||||
storageReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/storage/single/reads", nil)
|
||||
codeReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/code/single/reads", nil)
|
||||
|
||||
snapshotCommitTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/commits", nil)
|
||||
triedbCommitTimer = metrics.NewRegisteredResettingTimer("chain/triedb/commits", nil)
|
||||
triedbCommitTimer = metrics.NewRegisteredResettingTimer("chain/triedb/commits", nil)
|
||||
|
||||
blockInsertTimer = metrics.NewRegisteredResettingTimer("chain/inserts", nil)
|
||||
blockValidationTimer = metrics.NewRegisteredResettingTimer("chain/validation", nil)
|
||||
|
|
@ -182,14 +183,19 @@ type BlockChainConfig struct {
|
|||
// If set to -1, no trienode history will be retained;
|
||||
TrienodeHistory int64
|
||||
|
||||
// The frequency of full-value encoding. For example, a value of 16 means
|
||||
// that, on average, for a given trie node across its 16 consecutive historical
|
||||
// versions, only one version is stored in full format, while the others
|
||||
// are stored in diff mode for storage compression.
|
||||
NodeFullValueCheckpoint uint32
|
||||
|
||||
// State snapshot related options
|
||||
SnapshotLimit int // Memory allowance (MB) to use for caching snapshot entries in memory
|
||||
SnapshotNoBuild bool // Whether the background generation is allowed
|
||||
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
|
||||
|
||||
// This defines the cutoff block for history expiry.
|
||||
// Blocks before this number may be unavailable in the chain database.
|
||||
ChainHistoryMode history.HistoryMode
|
||||
// HistoryPolicy defines the chain history pruning intent.
|
||||
HistoryPolicy history.HistoryPolicy
|
||||
|
||||
// Misc options
|
||||
NoPrefetch bool // Whether to disable heuristic state prefetching when processing blocks
|
||||
|
|
@ -207,21 +213,26 @@ type BlockChainConfig struct {
|
|||
StateSizeTracking bool
|
||||
|
||||
// SlowBlockThreshold is the block execution time threshold beyond which
|
||||
// detailed statistics will be logged.
|
||||
// detailed statistics will be logged. Negative value means disabled (default),
|
||||
// zero logs all blocks, positive value filters blocks by execution time.
|
||||
SlowBlockThreshold time.Duration
|
||||
|
||||
// Execution configs
|
||||
StatelessSelfValidation bool // Generate execution witnesses and self-check against them (testing purpose)
|
||||
EnableWitnessStats bool // Whether trie access statistics collection is enabled
|
||||
}
|
||||
|
||||
// DefaultConfig returns the default config.
|
||||
// Note the returned object is safe to modify!
|
||||
func DefaultConfig() *BlockChainConfig {
|
||||
return &BlockChainConfig{
|
||||
TrieCleanLimit: 256,
|
||||
TrieDirtyLimit: 256,
|
||||
TrieTimeLimit: 5 * time.Minute,
|
||||
StateScheme: rawdb.HashScheme,
|
||||
SnapshotLimit: 256,
|
||||
SnapshotWait: true,
|
||||
ChainHistoryMode: history.KeepAll,
|
||||
TrieCleanLimit: 256,
|
||||
TrieDirtyLimit: 256,
|
||||
TrieTimeLimit: 5 * time.Minute,
|
||||
StateScheme: rawdb.HashScheme,
|
||||
SnapshotLimit: 256,
|
||||
SnapshotWait: true,
|
||||
HistoryPolicy: history.HistoryPolicy{Mode: history.KeepAll},
|
||||
// Transaction indexing is disabled by default.
|
||||
// This is appropriate for most unit tests.
|
||||
TxLookupLimit: -1,
|
||||
|
|
@ -259,18 +270,22 @@ func (cfg *BlockChainConfig) triedbConfig(isVerkle bool) *triedb.Config {
|
|||
}
|
||||
if cfg.StateScheme == rawdb.PathScheme {
|
||||
config.PathDB = &pathdb.Config{
|
||||
StateHistory: cfg.StateHistory,
|
||||
TrienodeHistory: cfg.TrienodeHistory,
|
||||
EnableStateIndexing: cfg.ArchiveMode,
|
||||
TrieCleanSize: cfg.TrieCleanLimit * 1024 * 1024,
|
||||
StateCleanSize: cfg.SnapshotLimit * 1024 * 1024,
|
||||
JournalDirectory: cfg.TrieJournalDirectory,
|
||||
|
||||
TrieCleanSize: cfg.TrieCleanLimit * 1024 * 1024,
|
||||
StateCleanSize: cfg.SnapshotLimit * 1024 * 1024,
|
||||
// TODO(rjl493456442): The write buffer represents the memory limit used
|
||||
// for flushing both trie data and state data to disk. The config name
|
||||
// should be updated to eliminate the confusion.
|
||||
WriteBufferSize: cfg.TrieDirtyLimit * 1024 * 1024,
|
||||
NoAsyncFlush: cfg.TrieNoAsyncFlush,
|
||||
WriteBufferSize: cfg.TrieDirtyLimit * 1024 * 1024,
|
||||
JournalDirectory: cfg.TrieJournalDirectory,
|
||||
|
||||
// Historical state configurations
|
||||
StateHistory: cfg.StateHistory,
|
||||
TrienodeHistory: cfg.TrienodeHistory,
|
||||
EnableStateIndexing: cfg.ArchiveMode,
|
||||
FullValueCheckpoint: cfg.NodeFullValueCheckpoint,
|
||||
|
||||
// Testing configurations
|
||||
NoAsyncFlush: cfg.TrieNoAsyncFlush,
|
||||
}
|
||||
}
|
||||
return config
|
||||
|
|
@ -308,7 +323,7 @@ type BlockChain struct {
|
|||
lastWrite uint64 // Last block when the state was flushed
|
||||
flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state
|
||||
triedb *triedb.Database // The database handler for maintaining trie nodes.
|
||||
statedb *state.CachingDB // State database to reuse between imports (contains state cache)
|
||||
codedb *state.CodeDB // The database handler for maintaining contract codes.
|
||||
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
||||
|
||||
hc *HeaderChain
|
||||
|
|
@ -390,6 +405,7 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
|
|||
cfg: cfg,
|
||||
db: db,
|
||||
triedb: triedb,
|
||||
codedb: state.NewCodeDB(db),
|
||||
triegc: prque.New[int64, common.Hash](nil),
|
||||
chainmu: syncx.NewClosableMutex(),
|
||||
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
|
||||
|
|
@ -406,7 +422,6 @@ func NewBlockChain(db ethdb.Database, genesis *Genesis, engine consensus.Engine,
|
|||
return nil, err
|
||||
}
|
||||
bc.flushInterval.Store(int64(cfg.TrieTimeLimit))
|
||||
bc.statedb = state.NewDatabase(bc.triedb, nil)
|
||||
bc.validator = NewBlockValidator(chainConfig, bc)
|
||||
bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
|
||||
bc.processor = NewStateProcessor(bc.hc)
|
||||
|
|
@ -583,9 +598,6 @@ func (bc *BlockChain) setupSnapshot() {
|
|||
AsyncBuild: !bc.cfg.SnapshotWait,
|
||||
}
|
||||
bc.snaps, _ = snapshot.New(snapconfig, bc.db, bc.triedb, head.Root)
|
||||
|
||||
// Re-initialize the state database with snapshot
|
||||
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -703,45 +715,43 @@ func (bc *BlockChain) loadLastState() error {
|
|||
// initializeHistoryPruning sets bc.historyPrunePoint.
|
||||
func (bc *BlockChain) initializeHistoryPruning(latest uint64) error {
|
||||
freezerTail, _ := bc.db.Tail()
|
||||
policy := bc.cfg.HistoryPolicy
|
||||
|
||||
switch bc.cfg.ChainHistoryMode {
|
||||
switch policy.Mode {
|
||||
case history.KeepAll:
|
||||
if freezerTail == 0 {
|
||||
return nil
|
||||
if freezerTail > 0 {
|
||||
// Database was pruned externally. Record the actual state.
|
||||
log.Warn("Chain history database is pruned", "tail", freezerTail, "mode", policy.Mode)
|
||||
bc.historyPrunePoint.Store(&history.PrunePoint{
|
||||
BlockNumber: freezerTail,
|
||||
BlockHash: bc.GetCanonicalHash(freezerTail),
|
||||
})
|
||||
}
|
||||
// The database was pruned somehow, so we need to figure out if it's a known
|
||||
// configuration or an error.
|
||||
predefinedPoint := history.PrunePoints[bc.genesisBlock.Hash()]
|
||||
if predefinedPoint == nil || freezerTail != predefinedPoint.BlockNumber {
|
||||
log.Error("Chain history database is pruned with unknown configuration", "tail", freezerTail)
|
||||
return errors.New("unexpected database tail")
|
||||
}
|
||||
bc.historyPrunePoint.Store(predefinedPoint)
|
||||
return nil
|
||||
|
||||
case history.KeepPostMerge:
|
||||
if freezerTail == 0 && latest != 0 {
|
||||
// This is the case where a user is trying to run with --history.chain
|
||||
// postmerge directly on an existing DB. We could just trigger the pruning
|
||||
// here, but it'd be a bit dangerous since they may not have intended this
|
||||
// action to happen. So just tell them how to do it.
|
||||
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned.", bc.cfg.ChainHistoryMode.String()))
|
||||
log.Error(fmt.Sprintf("Run 'geth prune-history' to prune pre-merge history."))
|
||||
return errors.New("history pruning requested via configuration")
|
||||
case history.KeepPostMerge, history.KeepPostPrague:
|
||||
target := policy.Target
|
||||
// Already at the target.
|
||||
if freezerTail == target.BlockNumber {
|
||||
bc.historyPrunePoint.Store(target)
|
||||
return nil
|
||||
}
|
||||
predefinedPoint := history.PrunePoints[bc.genesisBlock.Hash()]
|
||||
if predefinedPoint == nil {
|
||||
log.Error("Chain history pruning is not supported for this network", "genesis", bc.genesisBlock.Hash())
|
||||
return errors.New("history pruning requested for unknown network")
|
||||
} else if freezerTail > 0 && freezerTail != predefinedPoint.BlockNumber {
|
||||
log.Error("Chain history database is pruned to unknown block", "tail", freezerTail)
|
||||
return errors.New("unexpected database tail")
|
||||
// Database is pruned beyond the target.
|
||||
if freezerTail > target.BlockNumber {
|
||||
return fmt.Errorf("database pruned beyond requested history (tail=%d, target=%d)", freezerTail, target.BlockNumber)
|
||||
}
|
||||
bc.historyPrunePoint.Store(predefinedPoint)
|
||||
// Database needs pruning (freezerTail < target).
|
||||
if latest != 0 {
|
||||
log.Error(fmt.Sprintf("Chain history mode is configured as %q, but database is not pruned to the target block.", policy.Mode.String()))
|
||||
log.Error(fmt.Sprintf("Run 'geth prune-history --history.chain %s' to prune history.", policy.Mode.String()))
|
||||
return errors.New("history pruning required")
|
||||
}
|
||||
// Fresh database (latest == 0), will sync from target point.
|
||||
bc.historyPrunePoint.Store(target)
|
||||
return nil
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid history mode: %d", bc.cfg.ChainHistoryMode)
|
||||
return fmt.Errorf("invalid history mode: %d", policy.Mode)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1265,6 +1275,8 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
|||
func (bc *BlockChain) writeHeadBlock(block *types.Block) {
|
||||
// Add the block to the canonical chain number scheme and mark as the head
|
||||
batch := bc.db.NewBatch()
|
||||
defer batch.Close()
|
||||
|
||||
rawdb.WriteHeadHeaderHash(batch, block.Hash())
|
||||
rawdb.WriteHeadFastBlockHash(batch, block.Hash())
|
||||
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
|
||||
|
|
@ -1639,6 +1651,8 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
|||
batch = bc.db.NewBatch()
|
||||
start = time.Now()
|
||||
)
|
||||
defer batch.Close()
|
||||
|
||||
rawdb.WriteBlock(batch, block)
|
||||
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts)
|
||||
rawdb.WritePreimages(batch, statedb.Preimages())
|
||||
|
|
@ -1806,7 +1820,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
}
|
||||
defer bc.chainmu.Unlock()
|
||||
|
||||
_, n, err := bc.insertChain(chain, true, false) // No witness collection for mass inserts (would get super large)
|
||||
_, n, err := bc.insertChain(context.Background(), chain, true, false) // No witness collection for mass inserts (would get super large)
|
||||
return n, err
|
||||
}
|
||||
|
||||
|
|
@ -1818,7 +1832,7 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
// racey behaviour. If a sidechain import is in progress, and the historic state
|
||||
// is imported, but then new canon-head is added before the actual sidechain
|
||||
// completes, then the historic state could be pruned again
|
||||
func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) {
|
||||
func (bc *BlockChain) insertChain(ctx context.Context, chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) {
|
||||
// If the chain is terminating, don't even bother starting up.
|
||||
if bc.insertStopped() {
|
||||
return nil, 0, nil
|
||||
|
|
@ -1900,11 +1914,11 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
|||
if setHead {
|
||||
// First block is pruned, insert as sidechain and reorg only if TD grows enough
|
||||
log.Debug("Pruned ancestor, inserting as sidechain", "number", block.Number(), "hash", block.Hash())
|
||||
return bc.insertSideChain(block, it, makeWitness)
|
||||
return bc.insertSideChain(ctx, block, it, makeWitness)
|
||||
} else {
|
||||
// We're post-merge and the parent is pruned, try to recover the parent state
|
||||
log.Debug("Pruned ancestor", "number", block.Number(), "hash", block.Hash())
|
||||
_, err := bc.recoverAncestors(block, makeWitness)
|
||||
_, err := bc.recoverAncestors(ctx, block, makeWitness)
|
||||
return nil, it.index, err
|
||||
}
|
||||
// Some other error(except ErrKnownBlock) occurred, abort.
|
||||
|
|
@ -1976,7 +1990,15 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
|||
}
|
||||
// The traced section of block import.
|
||||
start := time.Now()
|
||||
res, err := bc.ProcessBlock(parent.Root, block, setHead, makeWitness && len(chain) == 1)
|
||||
config := ExecuteConfig{
|
||||
WriteState: true,
|
||||
WriteHead: setHead,
|
||||
EnableTracer: true,
|
||||
MakeWitness: makeWitness && len(chain) == 1,
|
||||
StatelessSelfValidation: bc.cfg.StatelessSelfValidation,
|
||||
EnableWitnessStats: bc.cfg.EnableWitnessStats,
|
||||
}
|
||||
res, err := bc.ProcessBlock(ctx, parent.Root, block, config)
|
||||
if err != nil {
|
||||
return nil, it.index, err
|
||||
}
|
||||
|
|
@ -2059,19 +2081,47 @@ func (bpr *blockProcessingResult) Stats() *ExecuteStats {
|
|||
return bpr.stats
|
||||
}
|
||||
|
||||
// ExecuteConfig defines optional behaviors during execution.
|
||||
type ExecuteConfig struct {
|
||||
// WriteState controls whether the computed state changes are persisted to
|
||||
// the underlying storage. If false, execution is performed in-memory only.
|
||||
WriteState bool
|
||||
|
||||
// WriteHead indicates whether the execution result should update the canonical
|
||||
// chain head. It's only relevant with WriteState == True.
|
||||
WriteHead bool
|
||||
|
||||
// EnableTracer enables execution tracing. This is typically used for debugging
|
||||
// or analysis and may significantly impact performance.
|
||||
EnableTracer bool
|
||||
|
||||
// MakeWitness indicates whether to generate execution witness data during
|
||||
// execution. Enabling this may introduce additional memory and CPU overhead.
|
||||
MakeWitness bool
|
||||
|
||||
// StatelessSelfValidation indicates whether the execution witnesses generation
|
||||
// and self-validation (testing purpose) is enabled.
|
||||
StatelessSelfValidation bool
|
||||
|
||||
// EnableWitnessStats indicates whether to enable collection of witness trie
|
||||
// access statistics
|
||||
EnableWitnessStats bool
|
||||
}
|
||||
|
||||
// ProcessBlock executes and validates the given block. If there was no error
|
||||
// it writes the block and associated state to database.
|
||||
func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, setHead bool, makeWitness bool) (result *blockProcessingResult, blockEndErr error) {
|
||||
func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash, block *types.Block, config ExecuteConfig) (result *blockProcessingResult, blockEndErr error) {
|
||||
var (
|
||||
err error
|
||||
startTime = time.Now()
|
||||
statedb *state.StateDB
|
||||
interrupt atomic.Bool
|
||||
sdb = state.NewDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps)
|
||||
)
|
||||
defer interrupt.Store(true) // terminate the prefetch at the end
|
||||
|
||||
if bc.cfg.NoPrefetch {
|
||||
statedb, err = state.New(parentRoot, bc.statedb)
|
||||
statedb, err = state.New(parentRoot, sdb)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -2081,38 +2131,29 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
//
|
||||
// Note: the main processor and prefetcher share the same reader with a local
|
||||
// cache for mitigating the overhead of state access.
|
||||
prefetch, process, err := bc.statedb.ReadersWithCacheStats(parentRoot)
|
||||
prefetch, process, err := sdb.ReadersWithCacheStats(parentRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
throwaway, err := state.NewWithReader(parentRoot, bc.statedb, prefetch)
|
||||
throwaway, err := state.NewWithReader(parentRoot, sdb, prefetch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statedb, err = state.NewWithReader(parentRoot, bc.statedb, process)
|
||||
statedb, err = state.NewWithReader(parentRoot, sdb, process)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Upload the statistics of reader at the end
|
||||
defer func() {
|
||||
pStat := prefetch.GetStats()
|
||||
accountCacheHitPrefetchMeter.Mark(pStat.AccountCacheHit)
|
||||
accountCacheMissPrefetchMeter.Mark(pStat.AccountCacheMiss)
|
||||
storageCacheHitPrefetchMeter.Mark(pStat.StorageCacheHit)
|
||||
storageCacheMissPrefetchMeter.Mark(pStat.StorageCacheMiss)
|
||||
|
||||
rStat := process.GetStats()
|
||||
accountCacheHitMeter.Mark(rStat.AccountCacheHit)
|
||||
accountCacheMissMeter.Mark(rStat.AccountCacheMiss)
|
||||
storageCacheHitMeter.Mark(rStat.StorageCacheHit)
|
||||
storageCacheMissMeter.Mark(rStat.StorageCacheMiss)
|
||||
|
||||
if result != nil {
|
||||
result.stats.StatePrefetchCacheStats = pStat
|
||||
result.stats.StateReadCacheStats = rStat
|
||||
if stater, ok := prefetch.(state.ReaderStater); ok {
|
||||
result.stats.StatePrefetchCacheStats = stater.GetStats()
|
||||
}
|
||||
if stater, ok := process.(state.ReaderStater); ok {
|
||||
result.stats.StateReadCacheStats = stater.GetStats()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func(start time.Time, throwaway *state.StateDB, block *types.Block) {
|
||||
// Disable tracing for prefetcher executions.
|
||||
vmCfg := bc.cfg.VmConfig
|
||||
|
|
@ -2137,12 +2178,12 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
// Generate witnesses either if we're self-testing, or if it's the
|
||||
// only block being inserted. A bit crude, but witnesses are huge,
|
||||
// so we refuse to make an entire chain of them.
|
||||
if bc.cfg.VmConfig.StatelessSelfValidation || makeWitness {
|
||||
if config.StatelessSelfValidation || config.MakeWitness {
|
||||
witness, err = stateless.NewWitness(block.Header(), bc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bc.cfg.VmConfig.EnableWitnessStats {
|
||||
if config.EnableWitnessStats {
|
||||
witnessStats = stateless.NewWitnessStats()
|
||||
}
|
||||
}
|
||||
|
|
@ -2150,22 +2191,27 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
defer statedb.StopPrefetcher()
|
||||
}
|
||||
|
||||
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
||||
bc.logger.OnBlockStart(tracing.BlockEvent{
|
||||
Block: block,
|
||||
Finalized: bc.CurrentFinalBlock(),
|
||||
Safe: bc.CurrentSafeBlock(),
|
||||
})
|
||||
}
|
||||
if bc.logger != nil && bc.logger.OnBlockEnd != nil {
|
||||
defer func() {
|
||||
bc.logger.OnBlockEnd(blockEndErr)
|
||||
}()
|
||||
// Instrument the blockchain tracing
|
||||
if config.EnableTracer {
|
||||
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
||||
bc.logger.OnBlockStart(tracing.BlockEvent{
|
||||
Block: block,
|
||||
Finalized: bc.CurrentFinalBlock(),
|
||||
Safe: bc.CurrentSafeBlock(),
|
||||
})
|
||||
}
|
||||
if bc.logger != nil && bc.logger.OnBlockEnd != nil {
|
||||
defer func() {
|
||||
bc.logger.OnBlockEnd(blockEndErr)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
// Process block using the parent state as reference point
|
||||
pstart := time.Now()
|
||||
res, err := bc.processor.Process(block, statedb, bc.cfg.VmConfig)
|
||||
pctx, _, spanEnd := telemetry.StartSpan(ctx, "bc.processor.Process")
|
||||
res, err := bc.processor.Process(pctx, block, statedb, bc.cfg.VmConfig)
|
||||
spanEnd(&err)
|
||||
if err != nil {
|
||||
bc.reportBadBlock(block, res, err)
|
||||
return nil, err
|
||||
|
|
@ -2173,7 +2219,10 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
ptime := time.Since(pstart)
|
||||
|
||||
vstart := time.Now()
|
||||
if err := bc.validator.ValidateState(block, statedb, res, false); err != nil {
|
||||
_, _, spanEnd = telemetry.StartSpan(ctx, "bc.validator.ValidateState")
|
||||
err = bc.validator.ValidateState(block, statedb, res, false)
|
||||
spanEnd(&err)
|
||||
if err != nil {
|
||||
bc.reportBadBlock(block, res, err)
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -2185,7 +2234,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
// witness builder/runner, which would otherwise be impossible due to the
|
||||
// various invalid chain states/behaviors being contained in those tests.
|
||||
xvstart := time.Now()
|
||||
if witness := statedb.Witness(); witness != nil && bc.cfg.VmConfig.StatelessSelfValidation {
|
||||
if witness := statedb.Witness(); witness != nil && config.StatelessSelfValidation {
|
||||
log.Warn("Running stateless self-validation", "block", block.Number(), "hash", block.Hash())
|
||||
|
||||
// Remove critical computed fields from the block to force true recalculation
|
||||
|
|
@ -2196,7 +2245,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
task := types.NewBlockWithHeader(context).WithBody(*block.Body())
|
||||
|
||||
// Run the stateless self-cross-validation
|
||||
crossStateRoot, crossReceiptRoot, err := ExecuteStateless(bc.chainConfig, bc.cfg.VmConfig, task, witness)
|
||||
crossStateRoot, crossReceiptRoot, err := ExecuteStateless(ctx, bc.chainConfig, bc.cfg.VmConfig, task, witness)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("stateless self-validation failed: %v", err)
|
||||
}
|
||||
|
|
@ -2227,38 +2276,39 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
stats.StorageLoaded = statedb.StorageLoaded
|
||||
stats.StorageUpdated = int(statedb.StorageUpdated.Load())
|
||||
stats.StorageDeleted = int(statedb.StorageDeleted.Load())
|
||||
|
||||
stats.CodeLoaded = statedb.CodeLoaded
|
||||
stats.CodeLoadBytes = statedb.CodeLoadBytes
|
||||
stats.CodeUpdated = statedb.CodeUpdated
|
||||
stats.CodeUpdateBytes = statedb.CodeUpdateBytes
|
||||
|
||||
stats.Execution = ptime - (statedb.AccountReads + statedb.StorageReads + statedb.CodeReads) // The time spent on EVM processing
|
||||
stats.Validation = vtime - (statedb.AccountHashes + statedb.AccountUpdates + statedb.StorageUpdates) // The time spent on block validation
|
||||
stats.CrossValidation = xvtime // The time spent on stateless cross validation
|
||||
|
||||
// Write the block to the chain and get the status.
|
||||
var (
|
||||
wstart = time.Now()
|
||||
status WriteStatus
|
||||
)
|
||||
if !setHead {
|
||||
// Don't set the head, only insert the block
|
||||
err = bc.writeBlockWithState(block, res.Receipts, statedb)
|
||||
} else {
|
||||
status, err = bc.writeBlockAndSetHead(block, res.Receipts, res.Logs, statedb, false)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var status WriteStatus
|
||||
if config.WriteState {
|
||||
wstart := time.Now()
|
||||
if !config.WriteHead {
|
||||
// Don't set the head, only insert the block
|
||||
err = bc.writeBlockWithState(block, res.Receipts, statedb)
|
||||
} else {
|
||||
status, err = bc.writeBlockAndSetHead(block, res.Receipts, res.Logs, statedb, false)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Update the metrics touched during block commit
|
||||
stats.AccountCommits = statedb.AccountCommits // Account commits are complete, we can mark them
|
||||
stats.StorageCommits = statedb.StorageCommits // Storage commits are complete, we can mark them
|
||||
stats.DatabaseCommit = statedb.DatabaseCommits // Database commits are complete, we can mark them
|
||||
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.DatabaseCommits
|
||||
}
|
||||
// Report the collected witness statistics
|
||||
if witnessStats != nil {
|
||||
witnessStats.ReportMetrics(block.NumberU64())
|
||||
}
|
||||
|
||||
// Update the metrics touched during block commit
|
||||
stats.AccountCommits = statedb.AccountCommits // Account commits are complete, we can mark them
|
||||
stats.StorageCommits = statedb.StorageCommits // Storage commits are complete, we can mark them
|
||||
stats.SnapshotCommit = statedb.SnapshotCommits // Snapshot commits are complete, we can mark them
|
||||
stats.TrieDBCommit = statedb.TrieDBCommits // Trie database commits are complete, we can mark them
|
||||
stats.BlockWrite = time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits
|
||||
|
||||
elapsed := time.Since(startTime) + 1 // prevent zero division
|
||||
stats.TotalTime = elapsed
|
||||
stats.MgasPerSecond = float64(res.GasUsed) * 1000 / float64(elapsed)
|
||||
|
|
@ -2279,7 +2329,7 @@ func (bc *BlockChain) ProcessBlock(parentRoot common.Hash, block *types.Block, s
|
|||
// The method writes all (header-and-body-valid) blocks to disk, then tries to
|
||||
// switch over to the new chain if the TD exceeded the current chain.
|
||||
// insertSideChain is only used pre-merge.
|
||||
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, makeWitness bool) (*stateless.Witness, int, error) {
|
||||
func (bc *BlockChain) insertSideChain(ctx context.Context, block *types.Block, it *insertIterator, makeWitness bool) (*stateless.Witness, int, error) {
|
||||
var current = bc.CurrentBlock()
|
||||
|
||||
// The first sidechain block error is already verified to be ErrPrunedAncestor.
|
||||
|
|
@ -2360,7 +2410,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
|||
// memory here.
|
||||
if len(blocks) >= 2048 || memory > 64*1024*1024 {
|
||||
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
|
||||
if _, _, err := bc.insertChain(blocks, true, false); err != nil {
|
||||
if _, _, err := bc.insertChain(ctx, blocks, true, false); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
blocks, memory = blocks[:0], 0
|
||||
|
|
@ -2374,7 +2424,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
|||
}
|
||||
if len(blocks) > 0 {
|
||||
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
|
||||
return bc.insertChain(blocks, true, makeWitness)
|
||||
return bc.insertChain(ctx, blocks, true, makeWitness)
|
||||
}
|
||||
return nil, 0, nil
|
||||
}
|
||||
|
|
@ -2383,7 +2433,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
|||
// all the ancestor blocks since that.
|
||||
// recoverAncestors is only used post-merge.
|
||||
// We return the hash of the latest block that we could correctly validate.
|
||||
func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (common.Hash, error) {
|
||||
func (bc *BlockChain) recoverAncestors(ctx context.Context, block *types.Block, makeWitness bool) (common.Hash, error) {
|
||||
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
||||
var (
|
||||
hashes []common.Hash
|
||||
|
|
@ -2423,7 +2473,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (co
|
|||
} else {
|
||||
b = bc.GetBlock(hashes[i], numbers[i])
|
||||
}
|
||||
if _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0); err != nil {
|
||||
if _, _, err := bc.insertChain(ctx, types.Blocks{b}, false, makeWitness && i == 0); err != nil {
|
||||
return b.ParentHash(), err
|
||||
}
|
||||
}
|
||||
|
|
@ -2539,6 +2589,7 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
|
|||
// as the txlookups should be changed atomically, and all subsequent
|
||||
// reads should be blocked until the mutation is complete.
|
||||
bc.txLookupLock.Lock()
|
||||
defer bc.txLookupLock.Unlock()
|
||||
|
||||
// Reorg can be executed, start reducing the chain's old blocks and appending
|
||||
// the new blocks
|
||||
|
|
@ -2616,6 +2667,8 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
|
|||
// Delete useless indexes right now which includes the non-canonical
|
||||
// transaction indexes, canonical chain indexes which above the head.
|
||||
batch := bc.db.NewBatch()
|
||||
defer batch.Close()
|
||||
|
||||
for _, tx := range types.HashDifference(deletedTxs, rebirthTxs) {
|
||||
rawdb.DeleteTxLookupEntry(batch, tx)
|
||||
}
|
||||
|
|
@ -2639,9 +2692,6 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
|
|||
// Reset the tx lookup cache to clear stale txlookup cache.
|
||||
bc.txLookupCache.Purge()
|
||||
|
||||
// Release the tx-lookup lock after mutation.
|
||||
bc.txLookupLock.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -2650,14 +2700,16 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Header) error
|
|||
// The key difference between the InsertChain is it won't do the canonical chain
|
||||
// updating. It relies on the additional SetCanonical call to finalize the entire
|
||||
// procedure.
|
||||
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool) (*stateless.Witness, error) {
|
||||
func (bc *BlockChain) InsertBlockWithoutSetHead(ctx context.Context, block *types.Block, makeWitness bool) (witness *stateless.Witness, err error) {
|
||||
_, _, spanEnd := telemetry.StartSpan(ctx, "core.blockchain.InsertBlockWithoutSetHead")
|
||||
defer spanEnd(&err)
|
||||
if !bc.chainmu.TryLock() {
|
||||
return nil, errChainStopped
|
||||
}
|
||||
defer bc.chainmu.Unlock()
|
||||
|
||||
witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness)
|
||||
return witness, err
|
||||
witness, _, err = bc.insertChain(ctx, types.Blocks{block}, false, makeWitness)
|
||||
return
|
||||
}
|
||||
|
||||
// SetCanonical rewinds the chain to set the new head block as the specified
|
||||
|
|
@ -2671,7 +2723,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) {
|
|||
|
||||
// Re-execute the reorged chain in case the head state is missing.
|
||||
if !bc.HasState(head.Root()) {
|
||||
if latestValidHash, err := bc.recoverAncestors(head, false); err != nil {
|
||||
if latestValidHash, err := bc.recoverAncestors(context.Background(), head, false); err != nil {
|
||||
return latestValidHash, err
|
||||
}
|
||||
log.Info("Recovered head state", "number", head.Number(), "hash", head.Hash())
|
||||
|
|
|
|||
|
|
@ -371,7 +371,7 @@ func (bc *BlockChain) TxIndexDone() bool {
|
|||
|
||||
// HasState checks if state trie is fully present in the database or not.
|
||||
func (bc *BlockChain) HasState(hash common.Hash) bool {
|
||||
_, err := bc.statedb.OpenTrie(hash)
|
||||
_, err := bc.triedb.NodeReader(hash)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
|
|
@ -403,7 +403,7 @@ func (bc *BlockChain) stateRecoverable(root common.Hash) bool {
|
|||
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) []byte {
|
||||
// TODO(rjl493456442) The associated account address is also required
|
||||
// in Verkle scheme. Fix it once snap-sync is supported for Verkle.
|
||||
return bc.statedb.ContractCodeWithPrefix(common.Address{}, hash)
|
||||
return bc.codedb.Reader().CodeWithPrefix(common.Address{}, hash)
|
||||
}
|
||||
|
||||
// State returns a new mutable state based on the current HEAD block.
|
||||
|
|
@ -413,14 +413,14 @@ func (bc *BlockChain) State() (*state.StateDB, error) {
|
|||
|
||||
// StateAt returns a new mutable state based on a particular point in time.
|
||||
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
||||
return state.New(root, bc.statedb)
|
||||
return state.New(root, state.NewDatabase(bc.triedb, bc.codedb).WithSnapshot(bc.snaps))
|
||||
}
|
||||
|
||||
// HistoricState returns a historic state specified by the given root.
|
||||
// Live states are not available and won't be served, please use `State`
|
||||
// or `StateAt` instead.
|
||||
func (bc *BlockChain) HistoricState(root common.Hash) (*state.StateDB, error) {
|
||||
return state.New(root, state.NewHistoricDatabase(bc.db, bc.triedb))
|
||||
return state.New(root, state.NewHistoricDatabase(bc.triedb, bc.codedb))
|
||||
}
|
||||
|
||||
// Config retrieves the chain's fork configuration.
|
||||
|
|
@ -444,11 +444,6 @@ func (bc *BlockChain) Processor() Processor {
|
|||
return bc.processor
|
||||
}
|
||||
|
||||
// StateCache returns the caching database underpinning the blockchain instance.
|
||||
func (bc *BlockChain) StateCache() state.Database {
|
||||
return bc.statedb
|
||||
}
|
||||
|
||||
// GasLimit returns the gas limit of the current HEAD block.
|
||||
func (bc *BlockChain) GasLimit() uint64 {
|
||||
return bc.CurrentBlock().GasLimit
|
||||
|
|
@ -492,6 +487,11 @@ func (bc *BlockChain) TrieDB() *triedb.Database {
|
|||
return bc.triedb
|
||||
}
|
||||
|
||||
// CodeDB retrieves the low level contract code database used for data storage.
|
||||
func (bc *BlockChain) CodeDB() *state.CodeDB {
|
||||
return bc.codedb
|
||||
}
|
||||
|
||||
// HeaderChain returns the underlying header chain.
|
||||
func (bc *BlockChain) HeaderChain() *HeaderChain {
|
||||
return bc.hc
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb/pebble"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
|
@ -2041,7 +2040,6 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
|||
dbconfig.HashDB = hashdb.Defaults
|
||||
}
|
||||
chain.triedb = triedb.NewDatabase(chain.db, dbconfig)
|
||||
chain.statedb = state.NewDatabase(chain.triedb, chain.snaps)
|
||||
|
||||
// Force run a freeze cycle
|
||||
type freezer interface {
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -39,19 +38,21 @@ type ExecuteStats struct {
|
|||
StorageCommits time.Duration // Time spent on the storage trie commit
|
||||
CodeReads time.Duration // Time spent on the contract code read
|
||||
|
||||
AccountLoaded int // Number of accounts loaded
|
||||
AccountUpdated int // Number of accounts updated
|
||||
AccountDeleted int // Number of accounts deleted
|
||||
StorageLoaded int // Number of storage slots loaded
|
||||
StorageUpdated int // Number of storage slots updated
|
||||
StorageDeleted int // Number of storage slots deleted
|
||||
CodeLoaded int // Number of contract code loaded
|
||||
AccountLoaded int // Number of accounts loaded
|
||||
AccountUpdated int // Number of accounts updated
|
||||
AccountDeleted int // Number of accounts deleted
|
||||
StorageLoaded int // Number of storage slots loaded
|
||||
StorageUpdated int // Number of storage slots updated
|
||||
StorageDeleted int // Number of storage slots deleted
|
||||
CodeLoaded int // Number of contract code loaded
|
||||
CodeLoadBytes int // Number of bytes read from contract code
|
||||
CodeUpdated int // Number of contract code written (CREATE/CREATE2 + EIP-7702)
|
||||
CodeUpdateBytes int // Total bytes of code written
|
||||
|
||||
Execution time.Duration // Time spent on the EVM execution
|
||||
Validation time.Duration // Time spent on the block validation
|
||||
CrossValidation time.Duration // Optional, time spent on the block cross validation
|
||||
SnapshotCommit time.Duration // Time spent on snapshot commit
|
||||
TrieDBCommit time.Duration // Time spent on database commit
|
||||
DatabaseCommit time.Duration // Time spent on database commit
|
||||
BlockWrite time.Duration // Time spent on block write
|
||||
TotalTime time.Duration // The total time spent on block execution
|
||||
MgasPerSecond float64 // The million gas processed per second
|
||||
|
|
@ -74,6 +75,7 @@ func (s *ExecuteStats) reportMetrics() {
|
|||
if s.CodeLoaded != 0 {
|
||||
codeReadTimer.Update(s.CodeReads)
|
||||
codeReadSingleTimer.Update(s.CodeReads / time.Duration(s.CodeLoaded))
|
||||
codeReadBytesTimer.Update(time.Duration(s.CodeLoadBytes))
|
||||
}
|
||||
accountUpdateTimer.Update(s.AccountUpdates) // Account updates are complete(in validation)
|
||||
storageUpdateTimer.Update(s.StorageUpdates) // Storage updates are complete(in validation)
|
||||
|
|
@ -84,82 +86,170 @@ func (s *ExecuteStats) reportMetrics() {
|
|||
blockExecutionTimer.Update(s.Execution) // The time spent on EVM processing
|
||||
blockValidationTimer.Update(s.Validation) // The time spent on block validation
|
||||
blockCrossValidationTimer.Update(s.CrossValidation) // The time spent on stateless cross validation
|
||||
snapshotCommitTimer.Update(s.SnapshotCommit) // Snapshot commits are complete, we can mark them
|
||||
triedbCommitTimer.Update(s.TrieDBCommit) // Trie database commits are complete, we can mark them
|
||||
triedbCommitTimer.Update(s.DatabaseCommit) // Trie database commits are complete, we can mark them
|
||||
blockWriteTimer.Update(s.BlockWrite) // The time spent on block write
|
||||
blockInsertTimer.Update(s.TotalTime) // The total time spent on block execution
|
||||
chainMgaspsMeter.Update(time.Duration(s.MgasPerSecond)) // TODO(rjl493456442) generalize the ResettingTimer
|
||||
|
||||
// Cache hit rates
|
||||
accountCacheHitPrefetchMeter.Mark(s.StatePrefetchCacheStats.AccountCacheHit)
|
||||
accountCacheMissPrefetchMeter.Mark(s.StatePrefetchCacheStats.AccountCacheMiss)
|
||||
storageCacheHitPrefetchMeter.Mark(s.StatePrefetchCacheStats.StorageCacheHit)
|
||||
storageCacheMissPrefetchMeter.Mark(s.StatePrefetchCacheStats.StorageCacheMiss)
|
||||
accountCacheHitPrefetchMeter.Mark(s.StatePrefetchCacheStats.StateStats.AccountCacheHit)
|
||||
accountCacheMissPrefetchMeter.Mark(s.StatePrefetchCacheStats.StateStats.AccountCacheMiss)
|
||||
storageCacheHitPrefetchMeter.Mark(s.StatePrefetchCacheStats.StateStats.StorageCacheHit)
|
||||
storageCacheMissPrefetchMeter.Mark(s.StatePrefetchCacheStats.StateStats.StorageCacheMiss)
|
||||
|
||||
accountCacheHitMeter.Mark(s.StateReadCacheStats.AccountCacheHit)
|
||||
accountCacheMissMeter.Mark(s.StateReadCacheStats.AccountCacheMiss)
|
||||
storageCacheHitMeter.Mark(s.StateReadCacheStats.StorageCacheHit)
|
||||
storageCacheMissMeter.Mark(s.StateReadCacheStats.StorageCacheMiss)
|
||||
accountCacheHitMeter.Mark(s.StateReadCacheStats.StateStats.AccountCacheHit)
|
||||
accountCacheMissMeter.Mark(s.StateReadCacheStats.StateStats.AccountCacheMiss)
|
||||
storageCacheHitMeter.Mark(s.StateReadCacheStats.StateStats.StorageCacheHit)
|
||||
storageCacheMissMeter.Mark(s.StateReadCacheStats.StateStats.StorageCacheMiss)
|
||||
}
|
||||
|
||||
// logSlow prints the detailed execution statistics if the block is regarded as slow.
|
||||
// slowBlockLog represents the JSON structure for slow block logging.
|
||||
// This format is designed for cross-client compatibility with other
|
||||
// Ethereum execution clients (reth, Besu, Nethermind).
|
||||
type slowBlockLog struct {
|
||||
Level string `json:"level"`
|
||||
Msg string `json:"msg"`
|
||||
Block slowBlockInfo `json:"block"`
|
||||
Timing slowBlockTime `json:"timing"`
|
||||
Throughput slowBlockThru `json:"throughput"`
|
||||
StateReads slowBlockReads `json:"state_reads"`
|
||||
StateWrites slowBlockWrites `json:"state_writes"`
|
||||
Cache slowBlockCache `json:"cache"`
|
||||
}
|
||||
|
||||
type slowBlockInfo struct {
|
||||
Number uint64 `json:"number"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
GasUsed uint64 `json:"gas_used"`
|
||||
TxCount int `json:"tx_count"`
|
||||
}
|
||||
|
||||
type slowBlockTime struct {
|
||||
ExecutionMs float64 `json:"execution_ms"`
|
||||
StateReadMs float64 `json:"state_read_ms"`
|
||||
StateHashMs float64 `json:"state_hash_ms"`
|
||||
CommitMs float64 `json:"commit_ms"`
|
||||
TotalMs float64 `json:"total_ms"`
|
||||
}
|
||||
|
||||
type slowBlockThru struct {
|
||||
MgasPerSec float64 `json:"mgas_per_sec"`
|
||||
}
|
||||
|
||||
type slowBlockReads struct {
|
||||
Accounts int `json:"accounts"`
|
||||
StorageSlots int `json:"storage_slots"`
|
||||
Code int `json:"code"`
|
||||
CodeBytes int `json:"code_bytes"`
|
||||
}
|
||||
|
||||
type slowBlockWrites struct {
|
||||
Accounts int `json:"accounts"`
|
||||
AccountsDeleted int `json:"accounts_deleted"`
|
||||
StorageSlots int `json:"storage_slots"`
|
||||
StorageSlotsDeleted int `json:"storage_slots_deleted"`
|
||||
Code int `json:"code"`
|
||||
CodeBytes int `json:"code_bytes"`
|
||||
}
|
||||
|
||||
// slowBlockCache represents cache hit/miss statistics for cross-client analysis.
|
||||
type slowBlockCache struct {
|
||||
Account slowBlockCacheEntry `json:"account"`
|
||||
Storage slowBlockCacheEntry `json:"storage"`
|
||||
Code slowBlockCodeCacheEntry `json:"code"`
|
||||
}
|
||||
|
||||
// slowBlockCacheEntry represents cache statistics for account/storage caches.
|
||||
type slowBlockCacheEntry struct {
|
||||
Hits int64 `json:"hits"`
|
||||
Misses int64 `json:"misses"`
|
||||
HitRate float64 `json:"hit_rate"`
|
||||
}
|
||||
|
||||
// slowBlockCodeCacheEntry represents cache statistics for code cache with byte-level granularity.
|
||||
type slowBlockCodeCacheEntry struct {
|
||||
Hits int64 `json:"hits"`
|
||||
Misses int64 `json:"misses"`
|
||||
HitRate float64 `json:"hit_rate"`
|
||||
HitBytes int64 `json:"hit_bytes"`
|
||||
MissBytes int64 `json:"miss_bytes"`
|
||||
}
|
||||
|
||||
// durationToMs converts a time.Duration to milliseconds as a float64
|
||||
// with sub-millisecond precision for accurate cross-client metrics.
|
||||
func durationToMs(d time.Duration) float64 {
|
||||
return float64(d.Nanoseconds()) / 1e6
|
||||
}
|
||||
|
||||
// logSlow prints the detailed execution statistics in JSON format if the block
|
||||
// is regarded as slow. The JSON format is designed for cross-client compatibility
|
||||
// with other Ethereum execution clients.
|
||||
func (s *ExecuteStats) logSlow(block *types.Block, slowBlockThreshold time.Duration) {
|
||||
if slowBlockThreshold == 0 {
|
||||
// Negative threshold means disabled (default when flag not set)
|
||||
if slowBlockThreshold < 0 {
|
||||
return
|
||||
}
|
||||
if s.TotalTime < slowBlockThreshold {
|
||||
// Threshold of 0 logs all blocks; positive threshold filters
|
||||
if slowBlockThreshold > 0 && s.TotalTime < slowBlockThreshold {
|
||||
return
|
||||
}
|
||||
msg := fmt.Sprintf(`
|
||||
########## SLOW BLOCK #########
|
||||
Block: %v (%#x) txs: %d, mgasps: %.2f, elapsed: %v
|
||||
|
||||
EVM execution: %v
|
||||
|
||||
Validation: %v
|
||||
Account hash: %v
|
||||
Storage hash: %v
|
||||
|
||||
State read: %v
|
||||
Account read: %v(%d)
|
||||
Storage read: %v(%d)
|
||||
Code read: %v(%d)
|
||||
|
||||
State write: %v
|
||||
Trie commit: %v
|
||||
State write: %v
|
||||
Block write: %v
|
||||
|
||||
%s
|
||||
##############################
|
||||
`, block.Number(), block.Hash(), len(block.Transactions()), s.MgasPerSecond, common.PrettyDuration(s.TotalTime),
|
||||
// EVM execution
|
||||
common.PrettyDuration(s.Execution),
|
||||
|
||||
// Block validation
|
||||
common.PrettyDuration(s.Validation+s.CrossValidation+s.AccountHashes+s.AccountUpdates+s.StorageUpdates),
|
||||
common.PrettyDuration(s.AccountHashes+s.AccountUpdates),
|
||||
common.PrettyDuration(s.StorageUpdates),
|
||||
|
||||
// State read
|
||||
common.PrettyDuration(s.AccountReads+s.StorageReads+s.CodeReads),
|
||||
common.PrettyDuration(s.AccountReads), s.AccountLoaded,
|
||||
common.PrettyDuration(s.StorageReads), s.StorageLoaded,
|
||||
common.PrettyDuration(s.CodeReads), s.CodeLoaded,
|
||||
|
||||
// State write
|
||||
common.PrettyDuration(max(s.AccountCommits, s.StorageCommits)+s.TrieDBCommit+s.SnapshotCommit+s.BlockWrite),
|
||||
common.PrettyDuration(max(s.AccountCommits, s.StorageCommits)),
|
||||
common.PrettyDuration(s.TrieDBCommit+s.SnapshotCommit),
|
||||
common.PrettyDuration(s.BlockWrite),
|
||||
|
||||
// cache statistics
|
||||
s.StateReadCacheStats,
|
||||
)
|
||||
for _, line := range strings.Split(msg, "\n") {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
log.Info(line)
|
||||
logEntry := slowBlockLog{
|
||||
Level: "warn",
|
||||
Msg: "Slow block",
|
||||
Block: slowBlockInfo{
|
||||
Number: block.NumberU64(),
|
||||
Hash: block.Hash(),
|
||||
GasUsed: block.GasUsed(),
|
||||
TxCount: len(block.Transactions()),
|
||||
},
|
||||
Timing: slowBlockTime{
|
||||
ExecutionMs: durationToMs(s.Execution),
|
||||
StateReadMs: durationToMs(s.AccountReads + s.StorageReads + s.CodeReads),
|
||||
StateHashMs: durationToMs(s.AccountHashes + s.AccountUpdates + s.StorageUpdates),
|
||||
CommitMs: durationToMs(max(s.AccountCommits, s.StorageCommits) + s.DatabaseCommit + s.BlockWrite),
|
||||
TotalMs: durationToMs(s.TotalTime),
|
||||
},
|
||||
Throughput: slowBlockThru{
|
||||
MgasPerSec: s.MgasPerSecond,
|
||||
},
|
||||
StateReads: slowBlockReads{
|
||||
Accounts: s.AccountLoaded,
|
||||
StorageSlots: s.StorageLoaded,
|
||||
Code: s.CodeLoaded,
|
||||
CodeBytes: s.CodeLoadBytes,
|
||||
},
|
||||
StateWrites: slowBlockWrites{
|
||||
Accounts: s.AccountUpdated,
|
||||
AccountsDeleted: s.AccountDeleted,
|
||||
StorageSlots: s.StorageUpdated,
|
||||
StorageSlotsDeleted: s.StorageDeleted,
|
||||
Code: s.CodeUpdated,
|
||||
CodeBytes: s.CodeUpdateBytes,
|
||||
},
|
||||
Cache: slowBlockCache{
|
||||
Account: slowBlockCacheEntry{
|
||||
Hits: s.StateReadCacheStats.StateStats.AccountCacheHit,
|
||||
Misses: s.StateReadCacheStats.StateStats.AccountCacheMiss,
|
||||
HitRate: s.StateReadCacheStats.StateStats.AccountCacheHitRate(),
|
||||
},
|
||||
Storage: slowBlockCacheEntry{
|
||||
Hits: s.StateReadCacheStats.StateStats.StorageCacheHit,
|
||||
Misses: s.StateReadCacheStats.StateStats.StorageCacheMiss,
|
||||
HitRate: s.StateReadCacheStats.StateStats.StorageCacheHitRate(),
|
||||
},
|
||||
Code: slowBlockCodeCacheEntry{
|
||||
Hits: s.StateReadCacheStats.CodeStats.CacheHit,
|
||||
Misses: s.StateReadCacheStats.CodeStats.CacheMiss,
|
||||
HitRate: s.StateReadCacheStats.CodeStats.HitRate(),
|
||||
HitBytes: s.StateReadCacheStats.CodeStats.CacheHitBytes,
|
||||
MissBytes: s.StateReadCacheStats.CodeStats.CacheMissBytes,
|
||||
},
|
||||
},
|
||||
}
|
||||
jsonBytes, err := json.Marshal(logEntry)
|
||||
if err != nil {
|
||||
log.Error("Failed to marshal slow block log", "error", err)
|
||||
return
|
||||
}
|
||||
log.Warn(string(jsonBytes))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package core
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
gomath "math"
|
||||
|
|
@ -35,7 +36,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/history"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -156,11 +156,11 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.statedb)
|
||||
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), state.NewDatabase(blockchain.triedb, blockchain.codedb))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := blockchain.processor.Process(block, statedb, vm.Config{})
|
||||
res, err := blockchain.processor.Process(context.Background(), block, statedb, vm.Config{})
|
||||
if err != nil {
|
||||
blockchain.reportBadBlock(block, res, err)
|
||||
return err
|
||||
|
|
@ -3456,7 +3456,7 @@ func testSetCanonical(t *testing.T, scheme string) {
|
|||
gen.AddTx(tx)
|
||||
})
|
||||
for _, block := range side {
|
||||
_, err := chain.InsertBlockWithoutSetHead(block, false)
|
||||
_, err := chain.InsertBlockWithoutSetHead(context.Background(), block, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to insert into chain: %v", err)
|
||||
}
|
||||
|
|
@ -4336,26 +4336,13 @@ func TestInsertChainWithCutoff(t *testing.T) {
|
|||
func testInsertChainWithCutoff(t *testing.T, cutoff uint64, ancientLimit uint64, genesis *Genesis, blocks []*types.Block, receipts []types.Receipts) {
|
||||
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true)))
|
||||
|
||||
// Add a known pruning point for the duration of the test.
|
||||
ghash := genesis.ToBlock().Hash()
|
||||
cutoffBlock := blocks[cutoff-1]
|
||||
history.PrunePoints[ghash] = &history.PrunePoint{
|
||||
BlockNumber: cutoffBlock.NumberU64(),
|
||||
BlockHash: cutoffBlock.Hash(),
|
||||
}
|
||||
defer func() {
|
||||
delete(history.PrunePoints, ghash)
|
||||
}()
|
||||
|
||||
// Enable pruning in cache config.
|
||||
config := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
||||
config.ChainHistoryMode = history.KeepPostMerge
|
||||
|
||||
db, _ := rawdb.Open(rawdb.NewMemoryDatabase(), rawdb.OpenOptions{})
|
||||
defer db.Close()
|
||||
|
||||
options := DefaultConfig().WithStateScheme(rawdb.PathScheme)
|
||||
chain, _ := NewBlockChain(db, genesis, beacon.New(ethash.NewFaker()), options)
|
||||
chain, _ := NewBlockChain(db, genesis, beacon.New(ethash.NewFaker()), DefaultConfig().WithStateScheme(rawdb.PathScheme))
|
||||
defer chain.Stop()
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
|
|
@ -63,7 +64,7 @@ func (b *BlockGen) SetCoinbase(addr common.Address) {
|
|||
panic("coinbase can only be set once")
|
||||
}
|
||||
b.header.Coinbase = addr
|
||||
b.gasPool = new(GasPool).AddGas(b.header.GasLimit)
|
||||
b.gasPool = NewGasPool(b.header.GasLimit)
|
||||
}
|
||||
|
||||
// SetExtra sets the extra data field of the generated block.
|
||||
|
|
@ -117,10 +118,12 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
|
|||
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig)
|
||||
)
|
||||
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
|
||||
receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed)
|
||||
receipt, err := ApplyTransaction(evm, b.gasPool, b.statedb, b.header, tx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
b.header.GasUsed = b.gasPool.Used()
|
||||
|
||||
// Merge the tx-local access event into the "block-local" one, in order to collect
|
||||
// all values, so that the witness can be built.
|
||||
if b.statedb.Database().TrieDB().IsVerkle() {
|
||||
|
|
@ -409,7 +412,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
|||
}
|
||||
|
||||
body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals}
|
||||
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts)
|
||||
block, err := b.engine.FinalizeAndAssemble(context.Background(), cm, b.header, statedb, &body, b.receipts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -479,13 +482,14 @@ func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int,
|
|||
if genesis.Config != nil && genesis.Config.IsVerkle(genesis.Config.ChainID, 0) {
|
||||
triedbConfig = triedb.VerkleDefaults
|
||||
}
|
||||
triedb := triedb.NewDatabase(db, triedbConfig)
|
||||
defer triedb.Close()
|
||||
_, err := genesis.Commit(db, triedb, nil)
|
||||
genesisTriedb := triedb.NewDatabase(db, triedbConfig)
|
||||
block, err := genesis.Commit(db, genesisTriedb, nil)
|
||||
if err != nil {
|
||||
genesisTriedb.Close()
|
||||
panic(err)
|
||||
}
|
||||
blocks, receipts := GenerateChain(genesis.Config, genesis.ToBlock(), engine, db, n, gen)
|
||||
genesisTriedb.Close()
|
||||
blocks, receipts := GenerateChain(genesis.Config, block, engine, db, n, gen)
|
||||
return db, blocks, receipts
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ var (
|
|||
// by a transaction is higher than what's left in the block.
|
||||
ErrGasLimitReached = errors.New("gas limit reached")
|
||||
|
||||
// ErrGasLimitOverflow is returned by the gas pool if the remaining gas
|
||||
// exceeds the maximum value of uint64.
|
||||
ErrGasLimitOverflow = errors.New("gas limit overflow")
|
||||
|
||||
// ErrInsufficientFundsForTransfer is returned if the transaction sender doesn't
|
||||
// have enough funds for transfer(topmost call only).
|
||||
ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer")
|
||||
|
|
|
|||
11
core/evm.go
11
core/evm.go
|
|
@ -44,6 +44,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
|||
baseFee *big.Int
|
||||
blobBaseFee *big.Int
|
||||
random *common.Hash
|
||||
slotNum uint64
|
||||
)
|
||||
|
||||
// If we don't have an explicit author (i.e. not mining), extract from the header
|
||||
|
|
@ -61,6 +62,10 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
|||
if header.Difficulty.Sign() == 0 {
|
||||
random = &header.MixDigest
|
||||
}
|
||||
if header.SlotNumber != nil {
|
||||
slotNum = *header.SlotNumber
|
||||
}
|
||||
|
||||
return vm.BlockContext{
|
||||
CanTransfer: CanTransfer,
|
||||
Transfer: Transfer,
|
||||
|
|
@ -73,6 +78,7 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
|||
BlobBaseFee: blobBaseFee,
|
||||
GasLimit: header.GasLimit,
|
||||
Random: random,
|
||||
SlotNum: slotNum,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -80,12 +86,9 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
|
|||
func NewEVMTxContext(msg *Message) vm.TxContext {
|
||||
ctx := vm.TxContext{
|
||||
Origin: msg.From,
|
||||
GasPrice: new(big.Int).Set(msg.GasPrice),
|
||||
GasPrice: uint256.MustFromBig(msg.GasPrice),
|
||||
BlobHashes: msg.BlobHashes,
|
||||
}
|
||||
if msg.BlobGasFeeCap != nil {
|
||||
ctx.BlobFeeCap = new(big.Int).Set(msg.BlobGasFeeCap)
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,39 +21,87 @@ import (
|
|||
"math"
|
||||
)
|
||||
|
||||
// GasPool tracks the amount of gas available during execution of the transactions
|
||||
// in a block. The zero value is a pool with zero gas available.
|
||||
type GasPool uint64
|
||||
// GasPool tracks the amount of gas available for transaction execution
|
||||
// within a block, along with the cumulative gas consumed.
|
||||
type GasPool struct {
|
||||
remaining uint64
|
||||
initial uint64
|
||||
cumulativeUsed uint64
|
||||
}
|
||||
|
||||
// AddGas makes gas available for execution.
|
||||
func (gp *GasPool) AddGas(amount uint64) *GasPool {
|
||||
if uint64(*gp) > math.MaxUint64-amount {
|
||||
panic("gas pool pushed above uint64")
|
||||
// NewGasPool initializes the gasPool with the given amount.
|
||||
func NewGasPool(amount uint64) *GasPool {
|
||||
return &GasPool{
|
||||
remaining: amount,
|
||||
initial: amount,
|
||||
}
|
||||
*(*uint64)(gp) += amount
|
||||
return gp
|
||||
}
|
||||
|
||||
// SubGas deducts the given amount from the pool if enough gas is
|
||||
// available and returns an error otherwise.
|
||||
func (gp *GasPool) SubGas(amount uint64) error {
|
||||
if uint64(*gp) < amount {
|
||||
if gp.remaining < amount {
|
||||
return ErrGasLimitReached
|
||||
}
|
||||
*(*uint64)(gp) -= amount
|
||||
gp.remaining -= amount
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReturnGas adds the refunded gas back to the pool and updates
|
||||
// the cumulative gas usage accordingly.
|
||||
func (gp *GasPool) ReturnGas(returned uint64, gasUsed uint64) error {
|
||||
if gp.remaining > math.MaxUint64-returned {
|
||||
return fmt.Errorf("%w: remaining: %d, returned: %d", ErrGasLimitOverflow, gp.remaining, returned)
|
||||
}
|
||||
// The returned gas calculation differs across forks.
|
||||
//
|
||||
// - Pre-Amsterdam:
|
||||
// returned = purchased - remaining (refund included)
|
||||
//
|
||||
// - Post-Amsterdam:
|
||||
// returned = purchased - gasUsed (refund excluded)
|
||||
gp.remaining += returned
|
||||
|
||||
// gasUsed = max(txGasUsed - gasRefund, calldataFloorGasCost)
|
||||
// regardless of Amsterdam is activated or not.
|
||||
gp.cumulativeUsed += gasUsed
|
||||
return nil
|
||||
}
|
||||
|
||||
// Gas returns the amount of gas remaining in the pool.
|
||||
func (gp *GasPool) Gas() uint64 {
|
||||
return uint64(*gp)
|
||||
return gp.remaining
|
||||
}
|
||||
|
||||
// SetGas sets the amount of gas with the provided number.
|
||||
func (gp *GasPool) SetGas(gas uint64) {
|
||||
*(*uint64)(gp) = gas
|
||||
// CumulativeUsed returns the amount of cumulative consumed gas (refunded included).
|
||||
func (gp *GasPool) CumulativeUsed() uint64 {
|
||||
return gp.cumulativeUsed
|
||||
}
|
||||
|
||||
// Used returns the amount of consumed gas.
|
||||
func (gp *GasPool) Used() uint64 {
|
||||
if gp.initial < gp.remaining {
|
||||
panic("gas used underflow")
|
||||
}
|
||||
return gp.initial - gp.remaining
|
||||
}
|
||||
|
||||
// Snapshot returns the deep-copied object as the snapshot.
|
||||
func (gp *GasPool) Snapshot() *GasPool {
|
||||
return &GasPool{
|
||||
initial: gp.initial,
|
||||
remaining: gp.remaining,
|
||||
cumulativeUsed: gp.cumulativeUsed,
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets the content of gasPool with the provided one.
|
||||
func (gp *GasPool) Set(other *GasPool) {
|
||||
gp.initial = other.initial
|
||||
gp.remaining = other.remaining
|
||||
gp.cumulativeUsed = other.cumulativeUsed
|
||||
}
|
||||
|
||||
func (gp *GasPool) String() string {
|
||||
return fmt.Sprintf("%d", *gp)
|
||||
return fmt.Sprintf("initial: %d, remaining: %d, cumulative used: %d", gp.initial, gp.remaining, gp.cumulativeUsed)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
|||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||
SlotNumber *uint64 `json:"slotNumber"`
|
||||
}
|
||||
var enc Genesis
|
||||
enc.Config = g.Config
|
||||
|
|
@ -56,6 +57,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
|||
enc.BaseFee = (*math.HexOrDecimal256)(g.BaseFee)
|
||||
enc.ExcessBlobGas = (*math.HexOrDecimal64)(g.ExcessBlobGas)
|
||||
enc.BlobGasUsed = (*math.HexOrDecimal64)(g.BlobGasUsed)
|
||||
enc.SlotNumber = g.SlotNumber
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
|
|
@ -77,6 +79,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
|||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||
SlotNumber *uint64 `json:"slotNumber"`
|
||||
}
|
||||
var dec Genesis
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
|
@ -133,5 +136,8 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
|||
if dec.BlobGasUsed != nil {
|
||||
g.BlobGasUsed = (*uint64)(dec.BlobGasUsed)
|
||||
}
|
||||
if dec.SlotNumber != nil {
|
||||
g.SlotNumber = dec.SlotNumber
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ type Genesis struct {
|
|||
BaseFee *big.Int `json:"baseFeePerGas"` // EIP-1559
|
||||
ExcessBlobGas *uint64 `json:"excessBlobGas"` // EIP-4844
|
||||
BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844
|
||||
SlotNumber *uint64 `json:"slotNumber"` // EIP-7843
|
||||
}
|
||||
|
||||
// copy copies the genesis.
|
||||
|
|
@ -122,6 +123,7 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) {
|
|||
genesis.BaseFee = genesisHeader.BaseFee
|
||||
genesis.ExcessBlobGas = genesisHeader.ExcessBlobGas
|
||||
genesis.BlobGasUsed = genesisHeader.BlobGasUsed
|
||||
genesis.SlotNumber = genesisHeader.SlotNumber
|
||||
|
||||
return &genesis, nil
|
||||
}
|
||||
|
|
@ -547,6 +549,12 @@ func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
|
|||
if conf.IsPrague(num, g.Timestamp) {
|
||||
head.RequestsHash = &types.EmptyRequestsHash
|
||||
}
|
||||
if conf.IsAmsterdam(num, g.Timestamp) {
|
||||
head.SlotNumber = g.SlotNumber
|
||||
if head.SlotNumber == nil {
|
||||
head.SlotNumber = new(uint64)
|
||||
}
|
||||
}
|
||||
}
|
||||
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
expected := common.FromHex("19056b480530799a4fdaa9fd9407043b965a3a5c37b4d2a1a9a4f3395a327561")
|
||||
expected := common.FromHex("1fd154971d9a386c4ec75fe7138c17efb569bfc2962e46e94a376ba997e3fadc")
|
||||
got := genesis.ToBlock().Root().Bytes()
|
||||
if !bytes.Equal(got, expected) {
|
||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
||||
|
|
|
|||
|
|
@ -32,10 +32,13 @@ const (
|
|||
|
||||
// KeepPostMerge sets the history pruning point to the merge activation block.
|
||||
KeepPostMerge
|
||||
|
||||
// KeepPostPrague sets the history pruning point to the Prague (Pectra) activation block.
|
||||
KeepPostPrague
|
||||
)
|
||||
|
||||
func (m HistoryMode) IsValid() bool {
|
||||
return m <= KeepPostMerge
|
||||
return m <= KeepPostPrague
|
||||
}
|
||||
|
||||
func (m HistoryMode) String() string {
|
||||
|
|
@ -44,6 +47,8 @@ func (m HistoryMode) String() string {
|
|||
return "all"
|
||||
case KeepPostMerge:
|
||||
return "postmerge"
|
||||
case KeepPostPrague:
|
||||
return "postprague"
|
||||
default:
|
||||
return fmt.Sprintf("invalid HistoryMode(%d)", m)
|
||||
}
|
||||
|
|
@ -64,33 +69,73 @@ func (m *HistoryMode) UnmarshalText(text []byte) error {
|
|||
*m = KeepAll
|
||||
case "postmerge":
|
||||
*m = KeepPostMerge
|
||||
case "postprague":
|
||||
*m = KeepPostPrague
|
||||
default:
|
||||
return fmt.Errorf(`unknown sync mode %q, want "all" or "postmerge"`, text)
|
||||
return fmt.Errorf(`unknown history mode %q, want "all", "postmerge", or "postprague"`, text)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrunePoint identifies a specific block for history pruning.
|
||||
type PrunePoint struct {
|
||||
BlockNumber uint64
|
||||
BlockHash common.Hash
|
||||
}
|
||||
|
||||
// PrunePoints the pre-defined history pruning cutoff blocks for known networks.
|
||||
// They point to the first post-merge block. Any pruning should truncate *up to* but excluding
|
||||
// given block.
|
||||
var PrunePoints = map[common.Hash]*PrunePoint{
|
||||
// mainnet
|
||||
params.MainnetGenesisHash: {
|
||||
BlockNumber: 15537393,
|
||||
BlockHash: common.HexToHash("0x55b11b918355b1ef9c5db810302ebad0bf2544255b530cdce90674d5887bb286"),
|
||||
// staticPrunePoints contains the pre-defined history pruning cutoff blocks for
|
||||
// known networks, keyed by history mode and genesis hash. They point to the first
|
||||
// block after the respective fork. Any pruning should truncate *up to* but
|
||||
// excluding the given block.
|
||||
var staticPrunePoints = map[HistoryMode]map[common.Hash]*PrunePoint{
|
||||
KeepPostMerge: {
|
||||
params.MainnetGenesisHash: {
|
||||
BlockNumber: 15537393,
|
||||
BlockHash: common.HexToHash("0x55b11b918355b1ef9c5db810302ebad0bf2544255b530cdce90674d5887bb286"),
|
||||
},
|
||||
params.SepoliaGenesisHash: {
|
||||
BlockNumber: 1450409,
|
||||
BlockHash: common.HexToHash("0x229f6b18ca1552f1d5146deceb5387333f40dc6275aebee3f2c5c4ece07d02db"),
|
||||
},
|
||||
},
|
||||
// sepolia
|
||||
params.SepoliaGenesisHash: {
|
||||
BlockNumber: 1450409,
|
||||
BlockHash: common.HexToHash("0x229f6b18ca1552f1d5146deceb5387333f40dc6275aebee3f2c5c4ece07d02db"),
|
||||
KeepPostPrague: {
|
||||
params.MainnetGenesisHash: {
|
||||
BlockNumber: 22431084,
|
||||
BlockHash: common.HexToHash("0x50c8cab760b2948349c590461b166773c45d8f4858cccf5a43025ab2960152e8"),
|
||||
},
|
||||
params.SepoliaGenesisHash: {
|
||||
BlockNumber: 7836331,
|
||||
BlockHash: common.HexToHash("0xe6571beb68bf24dbd8a6ba354518996920c55a3f8d8fdca423e391b8ad071f22"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// HistoryPolicy describes the configured history pruning strategy. It captures
|
||||
// user intent as opposed to the actual DB state.
|
||||
type HistoryPolicy struct {
|
||||
Mode HistoryMode
|
||||
// Static prune point for PostMerge/PostPrague, nil otherwise.
|
||||
Target *PrunePoint
|
||||
}
|
||||
|
||||
// NewPolicy constructs a HistoryPolicy from the given mode and genesis hash.
|
||||
func NewPolicy(mode HistoryMode, genesisHash common.Hash) (HistoryPolicy, error) {
|
||||
switch mode {
|
||||
case KeepAll:
|
||||
return HistoryPolicy{Mode: KeepAll}, nil
|
||||
|
||||
case KeepPostMerge, KeepPostPrague:
|
||||
point := staticPrunePoints[mode][genesisHash]
|
||||
if point == nil {
|
||||
return HistoryPolicy{}, fmt.Errorf("%s history pruning not available for network %s", mode, genesisHash.Hex())
|
||||
}
|
||||
return HistoryPolicy{Mode: mode, Target: point}, nil
|
||||
|
||||
default:
|
||||
return HistoryPolicy{}, fmt.Errorf("invalid history mode: %d", mode)
|
||||
}
|
||||
}
|
||||
|
||||
// PrunedHistoryError is returned by APIs when the requested history is pruned.
|
||||
type PrunedHistoryError struct{}
|
||||
|
||||
|
|
|
|||
58
core/history/historymode_test.go
Normal file
58
core/history/historymode_test.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2026 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 history
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func TestNewPolicy(t *testing.T) {
|
||||
// KeepAll: no target.
|
||||
p, err := NewPolicy(KeepAll, params.MainnetGenesisHash)
|
||||
if err != nil {
|
||||
t.Fatalf("KeepAll: %v", err)
|
||||
}
|
||||
if p.Mode != KeepAll || p.Target != nil {
|
||||
t.Errorf("KeepAll: unexpected policy %+v", p)
|
||||
}
|
||||
|
||||
// PostMerge: resolves known mainnet prune point.
|
||||
p, err = NewPolicy(KeepPostMerge, params.MainnetGenesisHash)
|
||||
if err != nil {
|
||||
t.Fatalf("PostMerge: %v", err)
|
||||
}
|
||||
if p.Target == nil || p.Target.BlockNumber != 15537393 {
|
||||
t.Errorf("PostMerge: unexpected target %+v", p.Target)
|
||||
}
|
||||
|
||||
// PostPrague: resolves known mainnet prune point.
|
||||
p, err = NewPolicy(KeepPostPrague, params.MainnetGenesisHash)
|
||||
if err != nil {
|
||||
t.Fatalf("PostPrague: %v", err)
|
||||
}
|
||||
if p.Target == nil || p.Target.BlockNumber != 22431084 {
|
||||
t.Errorf("PostPrague: unexpected target %+v", p.Target)
|
||||
}
|
||||
|
||||
// PostMerge on unknown network: error.
|
||||
if _, err = NewPolicy(KeepPostMerge, common.HexToHash("0xdeadbeef")); err == nil {
|
||||
t.Fatal("PostMerge unknown network: expected error")
|
||||
}
|
||||
}
|
||||
|
|
@ -28,9 +28,9 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// Tests block header storage and retrieval operations.
|
||||
|
|
@ -52,10 +52,7 @@ func TestHeaderStorage(t *testing.T) {
|
|||
if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
|
||||
t.Fatalf("Stored header RLP not found")
|
||||
} else {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher.Write(entry)
|
||||
|
||||
if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() {
|
||||
if hash := crypto.Keccak256Hash(entry); hash != header.Hash() {
|
||||
t.Fatalf("Retrieved RLP header mismatch: have %v, want %v", entry, header)
|
||||
}
|
||||
}
|
||||
|
|
@ -72,8 +69,7 @@ func TestBodyStorage(t *testing.T) {
|
|||
|
||||
// Create a test body to move around the database and make sure it's really new
|
||||
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
|
||||
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher := keccak.NewLegacyKeccak256()
|
||||
rlp.Encode(hasher, body)
|
||||
hash := common.BytesToHash(hasher.Sum(nil))
|
||||
|
||||
|
|
@ -90,10 +86,7 @@ func TestBodyStorage(t *testing.T) {
|
|||
if entry := ReadBodyRLP(db, hash, 0); entry == nil {
|
||||
t.Fatalf("Stored body RLP not found")
|
||||
} else {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher.Write(entry)
|
||||
|
||||
if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash {
|
||||
if calc := crypto.Keccak256Hash(entry); calc != hash {
|
||||
t.Fatalf("Retrieved RLP body mismatch: have %v, want %v", entry, body)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,9 +147,6 @@ func findTxInBlockBody(blockbody rlp.RawValue, target common.Hash) (*types.Trans
|
|||
}
|
||||
txIndex := uint64(0)
|
||||
for iter.Next() {
|
||||
if iter.Err() != nil {
|
||||
return nil, 0, iter.Err()
|
||||
}
|
||||
// The preimage for the hash calculation of legacy transactions
|
||||
// is just their RLP encoding. For typed (EIP-2718) transactions,
|
||||
// which are encoded as byte arrays, the preimage is the content of
|
||||
|
|
@ -171,6 +168,9 @@ func findTxInBlockBody(blockbody rlp.RawValue, target common.Hash) (*types.Trans
|
|||
}
|
||||
txIndex++
|
||||
}
|
||||
if iter.Err() != nil {
|
||||
return nil, 0, iter.Err()
|
||||
}
|
||||
return nil, 0, errors.New("transaction not found")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -166,6 +166,7 @@ func InspectFreezerTable(ancient string, freezerName string, tableName string, s
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer table.Close()
|
||||
table.dumpIndexStdout(start, end)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,6 +260,46 @@ func basicWrite(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
|||
if err != nil {
|
||||
t.Fatalf("Failed to write ancient data %v", err)
|
||||
}
|
||||
|
||||
// Write should work after truncating from tail but over the head
|
||||
db.TruncateTail(200)
|
||||
head, err := db.Ancients()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve head ancients %v", err)
|
||||
}
|
||||
tail, err := db.Tail()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve tail ancients %v", err)
|
||||
}
|
||||
if head != 200 || tail != 200 {
|
||||
t.Fatalf("Ancient head and tail are not expected")
|
||||
}
|
||||
_, err = db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
offset := uint64(200)
|
||||
for i := 0; i < 100; i++ {
|
||||
if err := op.AppendRaw("a", offset+uint64(i), dataA[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := op.AppendRaw("b", offset+uint64(i), dataB[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write ancient data %v", err)
|
||||
}
|
||||
head, err = db.Ancients()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve head ancients %v", err)
|
||||
}
|
||||
tail, err = db.Tail()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to retrieve tail ancients %v", err)
|
||||
}
|
||||
if head != 300 || tail != 200 {
|
||||
t.Fatalf("Ancient head and tail are not expected")
|
||||
}
|
||||
}
|
||||
|
||||
func nonMutable(t *testing.T, newFn func(kinds []string) ethdb.AncientStore) {
|
||||
|
|
|
|||
|
|
@ -153,9 +153,9 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
|
|||
err: err,
|
||||
}
|
||||
} else {
|
||||
var hashes []common.Hash
|
||||
for _, tx := range body.Transactions {
|
||||
hashes = append(hashes, tx.Hash())
|
||||
hashes := make([]common.Hash, len(body.Transactions))
|
||||
for i, tx := range body.Transactions {
|
||||
hashes[i] = tx.Hash()
|
||||
}
|
||||
result = &blockTxHashes{
|
||||
hashes: hashes,
|
||||
|
|
@ -357,9 +357,9 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
|
|||
}
|
||||
select {
|
||||
case <-interrupt:
|
||||
logger("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
logger("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", nextNum, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
default:
|
||||
logger("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
logger("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", nextNum, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||
"github.com/ethereum/go-ethereum/internal/tablewriter"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
|
@ -429,7 +430,8 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
filterMapBlockLV stat
|
||||
|
||||
// Path-mode archive data
|
||||
stateIndex stat
|
||||
stateIndex stat
|
||||
trienodeIndex stat
|
||||
|
||||
// Verkle statistics
|
||||
verkleTries stat
|
||||
|
|
@ -476,9 +478,9 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
bodies.add(size)
|
||||
case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
|
||||
receipts.add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix) && len(key) == (len(headerPrefix)+8+common.HashLength+len(headerTDSuffix)):
|
||||
tds.add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix) && len(key) == (len(headerPrefix)+8+common.HashLength+len(headerHashSuffix)):
|
||||
numHashPairings.add(size)
|
||||
case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
|
||||
hashNumPairings.add(size)
|
||||
|
|
@ -524,8 +526,19 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
bloomBits.add(size)
|
||||
|
||||
// Path-based historic state indexes
|
||||
case bytes.HasPrefix(key, StateHistoryIndexPrefix) && len(key) >= len(StateHistoryIndexPrefix)+common.HashLength:
|
||||
case bytes.HasPrefix(key, StateHistoryAccountMetadataPrefix) && len(key) == len(StateHistoryAccountMetadataPrefix)+common.HashLength:
|
||||
stateIndex.add(size)
|
||||
case bytes.HasPrefix(key, StateHistoryStorageMetadataPrefix) && len(key) == len(StateHistoryStorageMetadataPrefix)+2*common.HashLength:
|
||||
stateIndex.add(size)
|
||||
case bytes.HasPrefix(key, StateHistoryAccountBlockPrefix) && len(key) == len(StateHistoryAccountBlockPrefix)+common.HashLength+4:
|
||||
stateIndex.add(size)
|
||||
case bytes.HasPrefix(key, StateHistoryStorageBlockPrefix) && len(key) == len(StateHistoryStorageBlockPrefix)+2*common.HashLength+4:
|
||||
stateIndex.add(size)
|
||||
|
||||
case bytes.HasPrefix(key, TrienodeHistoryMetadataPrefix) && len(key) >= len(TrienodeHistoryMetadataPrefix)+common.HashLength:
|
||||
trienodeIndex.add(size)
|
||||
case bytes.HasPrefix(key, TrienodeHistoryBlockPrefix) && len(key) >= len(TrienodeHistoryBlockPrefix)+common.HashLength+4:
|
||||
trienodeIndex.add(size)
|
||||
|
||||
// Verkle trie data is detected, determine the sub-category
|
||||
case bytes.HasPrefix(key, VerklePrefix):
|
||||
|
|
@ -622,12 +635,13 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
{"Key-Value store", "Path trie state lookups", stateLookups.sizeString(), stateLookups.countString()},
|
||||
{"Key-Value store", "Path trie account nodes", accountTries.sizeString(), accountTries.countString()},
|
||||
{"Key-Value store", "Path trie storage nodes", storageTries.sizeString(), storageTries.countString()},
|
||||
{"Key-Value store", "Path state history indexes", stateIndex.sizeString(), stateIndex.countString()},
|
||||
{"Key-Value store", "Verkle trie nodes", verkleTries.sizeString(), verkleTries.countString()},
|
||||
{"Key-Value store", "Verkle trie state lookups", verkleStateLookups.sizeString(), verkleStateLookups.countString()},
|
||||
{"Key-Value store", "Trie preimages", preimages.sizeString(), preimages.countString()},
|
||||
{"Key-Value store", "Account snapshot", accountSnaps.sizeString(), accountSnaps.countString()},
|
||||
{"Key-Value store", "Storage snapshot", storageSnaps.sizeString(), storageSnaps.countString()},
|
||||
{"Key-Value store", "Historical state index", stateIndex.sizeString(), stateIndex.countString()},
|
||||
{"Key-Value store", "Historical trie index", trienodeIndex.sizeString(), trienodeIndex.countString()},
|
||||
{"Key-Value store", "Beacon sync headers", beaconHeaders.sizeString(), beaconHeaders.countString()},
|
||||
{"Key-Value store", "Clique snapshots", cliqueSnaps.sizeString(), cliqueSnaps.countString()},
|
||||
{"Key-Value store", "Singleton metadata", metadata.sizeString(), metadata.countString()},
|
||||
|
|
@ -650,7 +664,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||
total.Add(uint64(ancient.size()))
|
||||
}
|
||||
|
||||
table := NewTableWriter(os.Stdout)
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Database", "Category", "Size", "Items"})
|
||||
table.SetFooter([]string{"", "Total", common.StorageSize(total.Load()).String(), fmt.Sprintf("%d", count.Load())})
|
||||
table.AppendBulk(stats)
|
||||
|
|
@ -672,7 +686,7 @@ var knownMetadataKeys = [][]byte{
|
|||
snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
|
||||
uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
|
||||
persistentStateIDKey, trieJournalKey, snapshotSyncStatusKey, snapSyncStatusFlagKey,
|
||||
filterMapsRangeKey, headStateHistoryIndexKey, VerkleTransitionStatePrefix,
|
||||
filterMapsRangeKey, headStateHistoryIndexKey, headTrienodeHistoryIndexKey, VerkleTransitionStatePrefix,
|
||||
}
|
||||
|
||||
// printChainMetadata prints out chain metadata to stderr.
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/internal/era"
|
||||
"github.com/ethereum/go-ethereum/internal/era/onedb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
|
@ -51,7 +52,7 @@ type Store struct {
|
|||
type fileCacheEntry struct {
|
||||
refcount int // reference count. This is protected by Store.mu!
|
||||
opened chan struct{} // signals opening of file has completed
|
||||
file *era.Era // the file
|
||||
file *onedb.Era // the file
|
||||
err error // error from opening the file
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +103,7 @@ func (db *Store) Close() {
|
|||
|
||||
// GetRawBody returns the raw body for a given block number.
|
||||
func (db *Store) GetRawBody(number uint64) ([]byte, error) {
|
||||
epoch := number / uint64(era.MaxEra1Size)
|
||||
epoch := number / uint64(era.MaxSize)
|
||||
entry := db.getEraByEpoch(epoch)
|
||||
if entry.err != nil {
|
||||
if errors.Is(entry.err, fs.ErrNotExist) {
|
||||
|
|
@ -117,7 +118,7 @@ func (db *Store) GetRawBody(number uint64) ([]byte, error) {
|
|||
|
||||
// GetRawReceipts returns the raw receipts for a given block number.
|
||||
func (db *Store) GetRawReceipts(number uint64) ([]byte, error) {
|
||||
epoch := number / uint64(era.MaxEra1Size)
|
||||
epoch := number / uint64(era.MaxSize)
|
||||
entry := db.getEraByEpoch(epoch)
|
||||
if entry.err != nil {
|
||||
if errors.Is(entry.err, fs.ErrNotExist) {
|
||||
|
|
@ -249,7 +250,7 @@ func (db *Store) getCacheEntry(epoch uint64) (stat fileCacheStatus, entry *fileC
|
|||
}
|
||||
|
||||
// fileOpened is called after an era file has been successfully opened.
|
||||
func (db *Store) fileOpened(epoch uint64, entry *fileCacheEntry, file *era.Era) {
|
||||
func (db *Store) fileOpened(epoch uint64, entry *fileCacheEntry, file *onedb.Era) {
|
||||
db.mu.Lock()
|
||||
defer db.mu.Unlock()
|
||||
|
||||
|
|
@ -282,7 +283,7 @@ func (db *Store) fileFailedToOpen(epoch uint64, entry *fileCacheEntry, err error
|
|||
entry.err = err
|
||||
}
|
||||
|
||||
func (db *Store) openEraFile(epoch uint64) (*era.Era, error) {
|
||||
func (db *Store) openEraFile(epoch uint64) (*onedb.Era, error) {
|
||||
// File name scheme is <network>-<epoch>-<root>.
|
||||
glob := fmt.Sprintf("*-%05d-*.era1", epoch)
|
||||
matches, err := filepath.Glob(filepath.Join(db.datadir, glob))
|
||||
|
|
@ -297,17 +298,17 @@ func (db *Store) openEraFile(epoch uint64) (*era.Era, error) {
|
|||
}
|
||||
filename := matches[0]
|
||||
|
||||
e, err := era.Open(filename)
|
||||
e, err := onedb.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Sanity-check start block.
|
||||
if e.Start()%uint64(era.MaxEra1Size) != 0 {
|
||||
if e.Start()%uint64(era.MaxSize) != 0 {
|
||||
e.Close()
|
||||
return nil, fmt.Errorf("pre-merge era1 file has invalid boundary. %d %% %d != 0", e.Start(), era.MaxEra1Size)
|
||||
return nil, fmt.Errorf("pre-merge era1 file has invalid boundary. %d %% %d != 0", e.Start(), era.MaxSize)
|
||||
}
|
||||
log.Debug("Opened era1 file", "epoch", epoch)
|
||||
return e, nil
|
||||
return e.(*onedb.Era), nil
|
||||
}
|
||||
|
||||
// doneWithFile signals that the caller has finished using a file.
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const freezerTableSize = 2 * 1000 * 1000 * 1000
|
|||
// - The in-order data ensures that disk reads are always optimized.
|
||||
type Freezer struct {
|
||||
datadir string
|
||||
frozen atomic.Uint64 // Number of items already frozen
|
||||
head atomic.Uint64 // Number of items stored (including items removed from tail)
|
||||
tail atomic.Uint64 // Number of the first stored item in the freezer
|
||||
|
||||
// This lock synchronizes writers and the truncate operation, as well as
|
||||
|
|
@ -97,12 +97,12 @@ func NewFreezer(datadir string, namespace string, readonly bool, maxTableSize ui
|
|||
return nil, errSymlinkDatadir
|
||||
}
|
||||
}
|
||||
// Leveldb/Pebble uses LOCK as the filelock filename. To prevent the
|
||||
// name collision, we use FLOCK as the lock name.
|
||||
flockFile := filepath.Join(datadir, "FLOCK")
|
||||
if err := os.MkdirAll(filepath.Dir(flockFile), 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Leveldb uses LOCK as the filelock filename. To prevent the
|
||||
// name collision, we use FLOCK as the lock name.
|
||||
lock := flock.New(flockFile)
|
||||
tryLock := lock.TryLock
|
||||
if readonly {
|
||||
|
|
@ -213,7 +213,7 @@ func (f *Freezer) AncientBytes(kind string, id, offset, length uint64) ([]byte,
|
|||
|
||||
// Ancients returns the length of the frozen items.
|
||||
func (f *Freezer) Ancients() (uint64, error) {
|
||||
return f.frozen.Load(), nil
|
||||
return f.head.Load(), nil
|
||||
}
|
||||
|
||||
// Tail returns the number of first stored item in the freezer.
|
||||
|
|
@ -252,7 +252,7 @@ func (f *Freezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (writeSize
|
|||
defer f.writeLock.Unlock()
|
||||
|
||||
// Roll back all tables to the starting position in case of error.
|
||||
prevItem := f.frozen.Load()
|
||||
prevItem := f.head.Load()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// The write operation has failed. Go back to the previous item position.
|
||||
|
|
@ -273,7 +273,7 @@ func (f *Freezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (writeSize
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
f.frozen.Store(item)
|
||||
f.head.Store(item)
|
||||
return writeSize, nil
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +286,7 @@ func (f *Freezer) TruncateHead(items uint64) (uint64, error) {
|
|||
f.writeLock.Lock()
|
||||
defer f.writeLock.Unlock()
|
||||
|
||||
oitems := f.frozen.Load()
|
||||
oitems := f.head.Load()
|
||||
if oitems <= items {
|
||||
return oitems, nil
|
||||
}
|
||||
|
|
@ -295,7 +295,7 @@ func (f *Freezer) TruncateHead(items uint64) (uint64, error) {
|
|||
return 0, err
|
||||
}
|
||||
}
|
||||
f.frozen.Store(items)
|
||||
f.head.Store(items)
|
||||
return oitems, nil
|
||||
}
|
||||
|
||||
|
|
@ -320,6 +320,11 @@ func (f *Freezer) TruncateTail(tail uint64) (uint64, error) {
|
|||
}
|
||||
}
|
||||
f.tail.Store(tail)
|
||||
|
||||
// Update the head if the requested tail exceeds the current head
|
||||
if f.head.Load() < tail {
|
||||
f.head.Store(tail)
|
||||
}
|
||||
return old, nil
|
||||
}
|
||||
|
||||
|
|
@ -379,7 +384,7 @@ func (f *Freezer) validate() error {
|
|||
prunedTail = &tmp
|
||||
}
|
||||
|
||||
f.frozen.Store(head)
|
||||
f.head.Store(head)
|
||||
f.tail.Store(*prunedTail)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -414,7 +419,7 @@ func (f *Freezer) repair() error {
|
|||
}
|
||||
}
|
||||
|
||||
f.frozen.Store(head)
|
||||
f.head.Store(head)
|
||||
f.tail.Store(prunedTail)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ func (t *memoryTable) truncateTail(items uint64) error {
|
|||
return nil
|
||||
}
|
||||
if t.items < items {
|
||||
return errors.New("truncation above head")
|
||||
return t.reset(items)
|
||||
}
|
||||
for i := uint64(0); i < items-t.offset; i++ {
|
||||
if t.size > uint64(len(t.data[i])) {
|
||||
|
|
@ -127,6 +127,16 @@ func (t *memoryTable) truncateTail(items uint64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// reset clears the entire table and sets both the head and tail to the given
|
||||
// value. It assumes the caller holds the lock and that tail > t.items.
|
||||
func (t *memoryTable) reset(offset uint64) error {
|
||||
t.size = 0
|
||||
t.data = nil
|
||||
t.items = offset
|
||||
t.offset = offset
|
||||
return nil
|
||||
}
|
||||
|
||||
// commit merges the given item batch into table. It's presumed that the
|
||||
// batch is ordered and continuous with table.
|
||||
func (t *memoryTable) commit(batch [][]byte) error {
|
||||
|
|
@ -387,6 +397,9 @@ func (f *MemoryFreezer) TruncateTail(tail uint64) (uint64, error) {
|
|||
}
|
||||
}
|
||||
f.tail = tail
|
||||
if f.items < tail {
|
||||
f.items = tail
|
||||
}
|
||||
return old, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -221,13 +221,12 @@ func cleanup(path string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
names, err := dir.Readdirnames(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cerr := dir.Close(); cerr != nil {
|
||||
return cerr
|
||||
}
|
||||
for _, name := range names {
|
||||
if name == filepath.Base(path)+tmpSuffix {
|
||||
log.Info("Removed leftover freezer directory", "name", name)
|
||||
|
|
|
|||
|
|
@ -707,12 +707,13 @@ func (t *freezerTable) truncateTail(items uint64) error {
|
|||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
|
||||
// Ensure the given truncate target falls in the correct range
|
||||
// Short-circuit if the requested tail deletion points to a stale position
|
||||
if t.itemHidden.Load() >= items {
|
||||
return nil
|
||||
}
|
||||
// If the requested tail exceeds the current head, reset the entire table
|
||||
if t.items.Load() < items {
|
||||
return errors.New("truncation above head")
|
||||
return t.resetTo(items)
|
||||
}
|
||||
// Load the new tail index by the given new tail position
|
||||
var (
|
||||
|
|
@ -822,10 +823,9 @@ func (t *freezerTable) truncateTail(items uint64) error {
|
|||
shorten := indexEntrySize * int64(newDeleted-deleted)
|
||||
if t.metadata.flushOffset <= shorten {
|
||||
return fmt.Errorf("invalid index flush offset: %d, shorten: %d", t.metadata.flushOffset, shorten)
|
||||
} else {
|
||||
if err := t.metadata.setFlushOffset(t.metadata.flushOffset-shorten, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := t.metadata.setFlushOffset(t.metadata.flushOffset-shorten, true); err != nil {
|
||||
return err
|
||||
}
|
||||
// Retrieve the new size and update the total size counter
|
||||
newSize, err := t.sizeNolock()
|
||||
|
|
@ -836,6 +836,59 @@ func (t *freezerTable) truncateTail(items uint64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// resetTo clears the entire table and sets both the head and tail to the given
|
||||
// value. It assumes the caller holds the lock and that tail > t.items.
|
||||
func (t *freezerTable) resetTo(tail uint64) error {
|
||||
// Sync the entire table before resetting, eliminating the potential
|
||||
// data corruption.
|
||||
err := t.doSync()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Update the index file to reflect the new offset
|
||||
if err := t.index.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
entry := &indexEntry{
|
||||
filenum: t.headId + 1,
|
||||
offset: uint32(tail),
|
||||
}
|
||||
if err := reset(t.index.Name(), entry.append(nil)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.metadata.setVirtualTail(tail, true); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.metadata.setFlushOffset(indexEntrySize, true); err != nil {
|
||||
return err
|
||||
}
|
||||
t.index, err = openFreezerFileForAppend(t.index.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Purge all the existing data file
|
||||
if err := t.head.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
t.headId = t.headId + 1
|
||||
t.tailId = t.headId
|
||||
t.headBytes = 0
|
||||
|
||||
t.head, err = t.openFile(t.headId, openFreezerFileTruncated)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.releaseFilesBefore(t.headId, true)
|
||||
|
||||
t.items.Store(tail)
|
||||
t.itemOffset.Store(tail)
|
||||
t.itemHidden.Store(tail)
|
||||
t.sizeGauge.Update(0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes all opened files and finalizes the freezer table for use.
|
||||
// This operation must be completed before shutdown to prevent the loss of
|
||||
// recent writes.
|
||||
|
|
@ -1247,25 +1300,20 @@ func (t *freezerTable) doSync() error {
|
|||
if t.index == nil || t.head == nil || t.metadata.file == nil {
|
||||
return errClosed
|
||||
}
|
||||
var err error
|
||||
trackError := func(e error) {
|
||||
if e != nil && err == nil {
|
||||
err = e
|
||||
}
|
||||
if err := t.index.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := t.head.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
trackError(t.index.Sync())
|
||||
trackError(t.head.Sync())
|
||||
|
||||
// A crash may occur before the offset is updated, leaving the offset
|
||||
// points to a old position. If so, the extra items above the offset
|
||||
// points to an old position. If so, the extra items above the offset
|
||||
// will be truncated during the next run.
|
||||
stat, err := t.index.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
offset := stat.Size()
|
||||
trackError(t.metadata.setFlushOffset(offset, true))
|
||||
return err
|
||||
return t.metadata.setFlushOffset(stat.Size(), true)
|
||||
}
|
||||
|
||||
func (t *freezerTable) dumpIndexStdout(start, stop int64) {
|
||||
|
|
|
|||
|
|
@ -1139,6 +1139,7 @@ const (
|
|||
opTruncateHeadAll
|
||||
opTruncateTail
|
||||
opTruncateTailAll
|
||||
opTruncateTailOverHead
|
||||
opCheckAll
|
||||
opMax // boundary value, not an actual op
|
||||
)
|
||||
|
|
@ -1226,6 +1227,11 @@ func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
|
|||
step.target = deleted + uint64(len(items))
|
||||
items = items[:0]
|
||||
deleted = step.target
|
||||
case opTruncateTailOverHead:
|
||||
newDeleted := deleted + uint64(len(items)) + 10
|
||||
step.target = newDeleted
|
||||
deleted = newDeleted
|
||||
items = items[:0]
|
||||
}
|
||||
steps = append(steps, step)
|
||||
}
|
||||
|
|
@ -1268,7 +1274,7 @@ func runRandTest(rt randTest) bool {
|
|||
for i := 0; i < len(step.items); i++ {
|
||||
batch.AppendRaw(step.items[i], step.blobs[i])
|
||||
}
|
||||
batch.commit()
|
||||
rt[i].err = batch.commit()
|
||||
values = append(values, step.blobs...)
|
||||
|
||||
case opRetrieve:
|
||||
|
|
@ -1290,24 +1296,28 @@ func runRandTest(rt randTest) bool {
|
|||
}
|
||||
|
||||
case opTruncateHead:
|
||||
f.truncateHead(step.target)
|
||||
rt[i].err = f.truncateHead(step.target)
|
||||
|
||||
length := f.items.Load() - f.itemHidden.Load()
|
||||
values = values[:length]
|
||||
|
||||
case opTruncateHeadAll:
|
||||
f.truncateHead(step.target)
|
||||
rt[i].err = f.truncateHead(step.target)
|
||||
values = nil
|
||||
|
||||
case opTruncateTail:
|
||||
prev := f.itemHidden.Load()
|
||||
f.truncateTail(step.target)
|
||||
rt[i].err = f.truncateTail(step.target)
|
||||
|
||||
truncated := f.itemHidden.Load() - prev
|
||||
values = values[truncated:]
|
||||
|
||||
case opTruncateTailAll:
|
||||
f.truncateTail(step.target)
|
||||
rt[i].err = f.truncateTail(step.target)
|
||||
values = nil
|
||||
|
||||
case opTruncateTailOverHead:
|
||||
rt[i].err = f.truncateTail(step.target)
|
||||
values = nil
|
||||
}
|
||||
// Abort the test on error.
|
||||
|
|
@ -1633,3 +1643,43 @@ func TestFreezerAncientBytes(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTruncateOverHead(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
fn := fmt.Sprintf("t-%d", rand.Uint64())
|
||||
f, err := newTable(os.TempDir(), fn, metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 100, freezerTableConfig{noSnappy: true}, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Tail truncation on an empty table
|
||||
if err := f.truncateTail(10); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
batch := f.newBatch()
|
||||
data := getChunk(10, 1)
|
||||
require.NoError(t, batch.AppendRaw(uint64(10), data))
|
||||
require.NoError(t, batch.commit())
|
||||
|
||||
got, err := f.RetrieveItems(uint64(10), 1, 0)
|
||||
require.NoError(t, err)
|
||||
if !bytes.Equal(got[0], data) {
|
||||
t.Fatalf("Unexpected bytes, want: %v, got: %v", data, got[0])
|
||||
}
|
||||
|
||||
// Tail truncation on the non-empty table
|
||||
if err := f.truncateTail(20); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
batch = f.newBatch()
|
||||
data = getChunk(10, 1)
|
||||
require.NoError(t, batch.AppendRaw(uint64(20), data))
|
||||
require.NoError(t, batch.commit())
|
||||
|
||||
got, err = f.RetrieveItems(uint64(20), 1, 0)
|
||||
require.NoError(t, err)
|
||||
if !bytes.Equal(got[0], data) {
|
||||
t.Fatalf("Unexpected bytes, want: %v, got: %v", data, got[0])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,19 @@ import (
|
|||
"path/filepath"
|
||||
)
|
||||
|
||||
func atomicRename(src, dest string) error {
|
||||
if err := os.Rename(src, dest); err != nil {
|
||||
return err
|
||||
}
|
||||
dir, err := os.Open(filepath.Dir(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dir.Close()
|
||||
|
||||
return dir.Sync()
|
||||
}
|
||||
|
||||
// copyFrom copies data from 'srcPath' at offset 'offset' into 'destPath'.
|
||||
// The 'destPath' is created if it doesn't exist, otherwise it is overwritten.
|
||||
// Before the copy is executed, there is a callback can be registered to
|
||||
|
|
@ -73,13 +86,48 @@ func copyFrom(srcPath, destPath string, offset uint64, before func(f *os.File) e
|
|||
return err
|
||||
}
|
||||
f = nil
|
||||
return os.Rename(fname, destPath)
|
||||
|
||||
return atomicRename(fname, destPath)
|
||||
}
|
||||
|
||||
// reset atomically replaces the file at the given path with the provided content.
|
||||
func reset(path string, content []byte) error {
|
||||
// Create a temp file in the same dir where we want it to wind up
|
||||
f, err := os.CreateTemp(filepath.Dir(path), "*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fname := f.Name()
|
||||
|
||||
// Clean up the leftover file
|
||||
defer func() {
|
||||
if f != nil {
|
||||
f.Close()
|
||||
}
|
||||
os.Remove(fname)
|
||||
}()
|
||||
|
||||
// Write the content into the temp file
|
||||
_, err = f.Write(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Permanently persist the content into disk
|
||||
if err := f.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
f = nil
|
||||
|
||||
return atomicRename(fname, path)
|
||||
}
|
||||
|
||||
// openFreezerFileForAppend opens a freezer table file and seeks to the end
|
||||
func openFreezerFileForAppend(filename string) (*os.File, error) {
|
||||
// Open the file without the O_APPEND flag
|
||||
// because it has differing behaviour during Truncate operations
|
||||
// because it has differing behavior during Truncate operations
|
||||
// on different OS's
|
||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -253,6 +253,11 @@ func (b *tableBatch) Reset() {
|
|||
b.batch.Reset()
|
||||
}
|
||||
|
||||
// Close closes the batch and releases all associated resources.
|
||||
func (b *tableBatch) Close() {
|
||||
b.batch.Close()
|
||||
}
|
||||
|
||||
// tableReplayer is a wrapper around a batch replayer which truncates
|
||||
// the added prefix.
|
||||
type tableReplayer struct {
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ import (
|
|||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/keccak"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
func getBlock(transactions int, uncles int, dataSize int) *types.Block {
|
||||
|
|
@ -147,7 +147,7 @@ func BenchmarkHashing(b *testing.B) {
|
|||
blockRlp, _ = rlp.EncodeToBytes(block)
|
||||
}
|
||||
var got common.Hash
|
||||
var hasher = sha3.NewLegacyKeccak256()
|
||||
var hasher = keccak.NewLegacyKeccak256()
|
||||
b.Run("iteratorhashing", func(b *testing.B) {
|
||||
for b.Loop() {
|
||||
var hash common.Hash
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/overlay"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||
"github.com/ethereum/go-ethereum/trie/transitiontrie"
|
||||
|
|
@ -34,14 +34,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
const (
|
||||
// Number of codehash->size associations to keep.
|
||||
codeSizeCacheSize = 1_000_000 // 4 megabytes in total
|
||||
|
||||
// Cache size granted for caching clean code.
|
||||
codeCacheSize = 256 * 1024 * 1024
|
||||
)
|
||||
|
||||
// Database wraps access to tries and contract code.
|
||||
type Database interface {
|
||||
// Reader returns a state reader associated with the specified state root.
|
||||
|
|
@ -58,6 +50,11 @@ type Database interface {
|
|||
|
||||
// Snapshot returns the underlying state snapshot.
|
||||
Snapshot() *snapshot.Tree
|
||||
|
||||
// Commit flushes all pending writes and finalizes the state transition,
|
||||
// committing the changes to the underlying storage. It returns an error
|
||||
// if the commit fails.
|
||||
Commit(update *stateUpdate) error
|
||||
}
|
||||
|
||||
// Trie is a Ethereum Merkle Patricia trie.
|
||||
|
|
@ -149,32 +146,34 @@ type Trie interface {
|
|||
// state snapshot to provide functionalities for state access. It's meant to be a
|
||||
// long-live object and has a few caches inside for sharing between blocks.
|
||||
type CachingDB struct {
|
||||
disk ethdb.KeyValueStore
|
||||
triedb *triedb.Database
|
||||
snap *snapshot.Tree
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
codeSizeCache *lru.Cache[common.Hash, int]
|
||||
|
||||
// Transition-specific fields
|
||||
TransitionStatePerRoot *lru.Cache[common.Hash, *overlay.TransitionState]
|
||||
triedb *triedb.Database
|
||||
codedb *CodeDB
|
||||
snap *snapshot.Tree
|
||||
}
|
||||
|
||||
// NewDatabase creates a state database with the provided data sources.
|
||||
func NewDatabase(triedb *triedb.Database, snap *snapshot.Tree) *CachingDB {
|
||||
func NewDatabase(triedb *triedb.Database, codedb *CodeDB) *CachingDB {
|
||||
if codedb == nil {
|
||||
codedb = NewCodeDB(triedb.Disk())
|
||||
}
|
||||
return &CachingDB{
|
||||
disk: triedb.Disk(),
|
||||
triedb: triedb,
|
||||
snap: snap,
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||
TransitionStatePerRoot: lru.NewCache[common.Hash, *overlay.TransitionState](1000),
|
||||
triedb: triedb,
|
||||
codedb: codedb,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDatabaseForTesting is similar to NewDatabase, but it initializes the caching
|
||||
// db by using an ephemeral memory db with default config for testing.
|
||||
func NewDatabaseForTesting() *CachingDB {
|
||||
return NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
return NewDatabase(triedb.NewDatabase(db, nil), NewCodeDB(db))
|
||||
}
|
||||
|
||||
// WithSnapshot configures the provided contract code cache. Note that this
|
||||
// registration must be performed before the cachingDB is used.
|
||||
func (db *CachingDB) WithSnapshot(snapshot *snapshot.Tree) *CachingDB {
|
||||
db.snap = snapshot
|
||||
return db
|
||||
}
|
||||
|
||||
// StateReader returns a state reader associated with the specified state root.
|
||||
|
|
@ -218,21 +217,20 @@ func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), sr), nil
|
||||
return newReader(db.codedb.Reader(), sr), nil
|
||||
}
|
||||
|
||||
// ReadersWithCacheStats creates a pair of state readers that share the same
|
||||
// underlying state reader and internal state cache, while maintaining separate
|
||||
// statistics respectively.
|
||||
func (db *CachingDB) ReadersWithCacheStats(stateRoot common.Hash) (ReaderWithStats, ReaderWithStats, error) {
|
||||
func (db *CachingDB) ReadersWithCacheStats(stateRoot common.Hash) (Reader, Reader, error) {
|
||||
r, err := db.StateReader(stateRoot)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sr := newStateReaderWithCache(r)
|
||||
|
||||
ra := newReaderWithStats(sr, newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache))
|
||||
rb := newReaderWithStats(sr, newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache))
|
||||
ra := newReader(db.codedb.Reader(), newStateReaderWithStats(sr))
|
||||
rb := newReader(db.codedb.Reader(), newStateReaderWithStats(sr))
|
||||
return ra, rb, nil
|
||||
}
|
||||
|
||||
|
|
@ -268,22 +266,6 @@ func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre
|
|||
return tr, nil
|
||||
}
|
||||
|
||||
// ContractCodeWithPrefix retrieves a particular contract's code. If the
|
||||
// code can't be found in the cache, then check the existence with **new**
|
||||
// db scheme.
|
||||
func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) []byte {
|
||||
code, _ := db.codeCache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
return code
|
||||
}
|
||||
code = rawdb.ReadCodeWithPrefix(db.disk, codeHash)
|
||||
if len(code) > 0 {
|
||||
db.codeCache.Add(codeHash, code)
|
||||
db.codeSizeCache.Add(codeHash, len(code))
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
// TrieDB retrieves any intermediate trie-node caching layer.
|
||||
func (db *CachingDB) TrieDB() *triedb.Database {
|
||||
return db.triedb
|
||||
|
|
@ -294,6 +276,40 @@ func (db *CachingDB) Snapshot() *snapshot.Tree {
|
|||
return db.snap
|
||||
}
|
||||
|
||||
// Commit flushes all pending writes and finalizes the state transition,
|
||||
// committing the changes to the underlying storage. It returns an error
|
||||
// if the commit fails.
|
||||
func (db *CachingDB) Commit(update *stateUpdate) error {
|
||||
// Short circuit if nothing to commit
|
||||
if update.empty() {
|
||||
return nil
|
||||
}
|
||||
// Commit dirty contract code if any exists
|
||||
if len(update.codes) > 0 {
|
||||
batch := db.codedb.NewBatchWithSize(len(update.codes))
|
||||
for _, code := range update.codes {
|
||||
batch.Put(code.hash, code.blob)
|
||||
}
|
||||
if err := batch.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// If snapshotting is enabled, update the snapshot tree with this new version
|
||||
if db.snap != nil && db.snap.Snapshot(update.originRoot) != nil {
|
||||
if err := db.snap.Update(update.root, update.originRoot, update.accounts, update.storages); err != nil {
|
||||
log.Warn("Failed to update snapshot tree", "from", update.originRoot, "to", update.root, "err", err)
|
||||
}
|
||||
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
||||
// - head layer is paired with HEAD state
|
||||
// - head-1 layer is paired with HEAD-1 state
|
||||
// - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state
|
||||
if err := db.snap.Cap(update.root, TriesInMemory); err != nil {
|
||||
log.Warn("Failed to cap snapshot tree", "root", update.root, "layers", TriesInMemory, "err", err)
|
||||
}
|
||||
}
|
||||
return db.triedb.Update(update.root, update.originRoot, update.blockNumber, update.nodes, update.stateSet())
|
||||
}
|
||||
|
||||
// mustCopyTrie returns a deep-copied trie.
|
||||
func mustCopyTrie(t Trie) Trie {
|
||||
switch t := t.(type) {
|
||||
|
|
|
|||
231
core/state/database_code.go
Normal file
231
core/state/database_code.go
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
// Copyright 2026 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 state
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
const (
|
||||
// Number of codeHash->size associations to keep.
|
||||
codeSizeCacheSize = 1_000_000
|
||||
|
||||
// Cache size granted for caching clean code.
|
||||
codeCacheSize = 256 * 1024 * 1024
|
||||
)
|
||||
|
||||
// CodeCache maintains cached contract code that is shared across blocks, enabling
|
||||
// fast access for external calls such as RPCs and state transitions.
|
||||
//
|
||||
// It is thread-safe and has a bounded size.
|
||||
type codeCache struct {
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
codeSizeCache *lru.Cache[common.Hash, int]
|
||||
}
|
||||
|
||||
// newCodeCache initializes the contract code cache with the predefined capacity.
|
||||
func newCodeCache() *codeCache {
|
||||
return &codeCache{
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the contract code associated with the provided code hash.
|
||||
func (c *codeCache) Get(hash common.Hash) ([]byte, bool) {
|
||||
return c.codeCache.Get(hash)
|
||||
}
|
||||
|
||||
// GetSize returns the contract code size associated with the provided code hash.
|
||||
func (c *codeCache) GetSize(hash common.Hash) (int, bool) {
|
||||
return c.codeSizeCache.Get(hash)
|
||||
}
|
||||
|
||||
// Put adds the provided contract code along with its size information into the cache.
|
||||
func (c *codeCache) Put(hash common.Hash, code []byte) {
|
||||
c.codeCache.Add(hash, code)
|
||||
c.codeSizeCache.Add(hash, len(code))
|
||||
}
|
||||
|
||||
// CodeReader implements state.ContractCodeReader, accessing contract code either in
|
||||
// local key-value store or the shared code cache.
|
||||
//
|
||||
// Reader is safe for concurrent access.
|
||||
type CodeReader struct {
|
||||
db ethdb.KeyValueReader
|
||||
cache *codeCache
|
||||
|
||||
// Cache statistics
|
||||
hit atomic.Int64 // Number of code lookups found in the cache
|
||||
miss atomic.Int64 // Number of code lookups not found in the cache
|
||||
hitBytes atomic.Int64 // Total number of bytes read from cache
|
||||
missBytes atomic.Int64 // Total number of bytes read from database
|
||||
}
|
||||
|
||||
// newCodeReader constructs the code reader with provided key value store and the cache.
|
||||
func newCodeReader(db ethdb.KeyValueReader, cache *codeCache) *CodeReader {
|
||||
return &CodeReader{
|
||||
db: db,
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
// Has returns the flag indicating whether the contract code with
|
||||
// specified address and hash exists or not.
|
||||
func (r *CodeReader) Has(addr common.Address, codeHash common.Hash) bool {
|
||||
return len(r.Code(addr, codeHash)) > 0
|
||||
}
|
||||
|
||||
// Code implements state.ContractCodeReader, retrieving a particular contract's code.
|
||||
// Null is returned if the contract code is not present.
|
||||
func (r *CodeReader) Code(addr common.Address, codeHash common.Hash) []byte {
|
||||
code, _ := r.cache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
r.hit.Add(1)
|
||||
r.hitBytes.Add(int64(len(code)))
|
||||
return code
|
||||
}
|
||||
r.miss.Add(1)
|
||||
|
||||
code = rawdb.ReadCode(r.db, codeHash)
|
||||
if len(code) > 0 {
|
||||
r.cache.Put(codeHash, code)
|
||||
r.missBytes.Add(int64(len(code)))
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
// CodeSize implements state.ContractCodeReader, retrieving a particular contract
|
||||
// code's size. Zero is returned if the contract code is not present.
|
||||
func (r *CodeReader) CodeSize(addr common.Address, codeHash common.Hash) int {
|
||||
if cached, ok := r.cache.GetSize(codeHash); ok {
|
||||
r.hit.Add(1)
|
||||
return cached
|
||||
}
|
||||
return len(r.Code(addr, codeHash))
|
||||
}
|
||||
|
||||
// CodeWithPrefix retrieves the contract code for the specified account address
|
||||
// and code hash. It is almost identical to Code, but uses rawdb.ReadCodeWithPrefix
|
||||
// for database lookups. The intention is to gradually deprecate the old
|
||||
// contract code scheme.
|
||||
func (r *CodeReader) CodeWithPrefix(addr common.Address, codeHash common.Hash) []byte {
|
||||
code, _ := r.cache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
r.hit.Add(1)
|
||||
r.hitBytes.Add(int64(len(code)))
|
||||
return code
|
||||
}
|
||||
r.miss.Add(1)
|
||||
|
||||
code = rawdb.ReadCodeWithPrefix(r.db, codeHash)
|
||||
if len(code) > 0 {
|
||||
r.cache.Put(codeHash, code)
|
||||
r.missBytes.Add(int64(len(code)))
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
// GetCodeStats implements ContractCodeReaderStater, returning the statistics
|
||||
// of the code reader.
|
||||
func (r *CodeReader) GetCodeStats() ContractCodeReaderStats {
|
||||
return ContractCodeReaderStats{
|
||||
CacheHit: r.hit.Load(),
|
||||
CacheMiss: r.miss.Load(),
|
||||
CacheHitBytes: r.hitBytes.Load(),
|
||||
CacheMissBytes: r.missBytes.Load(),
|
||||
}
|
||||
}
|
||||
|
||||
type CodeBatch struct {
|
||||
db *CodeDB
|
||||
codes [][]byte
|
||||
codeHashes []common.Hash
|
||||
}
|
||||
|
||||
// newCodeBatch constructs the batch for writing contract code.
|
||||
func newCodeBatch(db *CodeDB) *CodeBatch {
|
||||
return &CodeBatch{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
|
||||
// newCodeBatchWithSize constructs the batch with a pre-allocated capacity.
|
||||
func newCodeBatchWithSize(db *CodeDB, size int) *CodeBatch {
|
||||
return &CodeBatch{
|
||||
db: db,
|
||||
codes: make([][]byte, 0, size),
|
||||
codeHashes: make([]common.Hash, 0, size),
|
||||
}
|
||||
}
|
||||
|
||||
// Put inserts the given contract code into the writer, waiting for commit.
|
||||
func (b *CodeBatch) Put(codeHash common.Hash, code []byte) {
|
||||
b.codes = append(b.codes, code)
|
||||
b.codeHashes = append(b.codeHashes, codeHash)
|
||||
}
|
||||
|
||||
// Commit flushes the accumulated dirty contract code into the database and
|
||||
// also place them in the cache.
|
||||
func (b *CodeBatch) Commit() error {
|
||||
batch := b.db.db.NewBatch()
|
||||
for i, code := range b.codes {
|
||||
rawdb.WriteCode(batch, b.codeHashes[i], code)
|
||||
b.db.cache.Put(b.codeHashes[i], code)
|
||||
}
|
||||
if err := batch.Write(); err != nil {
|
||||
return err
|
||||
}
|
||||
b.codes = b.codes[:0]
|
||||
b.codeHashes = b.codeHashes[:0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// CodeDB is responsible for managing the contract code and provides the access
|
||||
// to it. It can be used as a global object, sharing it between multiple entities.
|
||||
type CodeDB struct {
|
||||
db ethdb.KeyValueStore
|
||||
cache *codeCache
|
||||
}
|
||||
|
||||
// NewCodeDB constructs the contract code database with the provided key value store.
|
||||
func NewCodeDB(db ethdb.KeyValueStore) *CodeDB {
|
||||
return &CodeDB{
|
||||
db: db,
|
||||
cache: newCodeCache(),
|
||||
}
|
||||
}
|
||||
|
||||
// Reader returns the contract code reader.
|
||||
func (d *CodeDB) Reader() *CodeReader {
|
||||
return newCodeReader(d.db, d.cache)
|
||||
}
|
||||
|
||||
// NewBatch returns the batch for flushing contract codes.
|
||||
func (d *CodeDB) NewBatch() *CodeBatch {
|
||||
return newCodeBatch(d)
|
||||
}
|
||||
|
||||
// NewBatchWithSize returns the batch with pre-allocated capacity.
|
||||
func (d *CodeDB) NewBatchWithSize(size int) *CodeBatch {
|
||||
return newCodeBatchWithSize(d, size)
|
||||
}
|
||||
|
|
@ -18,39 +18,37 @@ package state
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/database"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
)
|
||||
|
||||
// historicReader wraps a historical state reader defined in path database,
|
||||
// providing historic state serving over the path scheme.
|
||||
//
|
||||
// TODO(rjl493456442): historicReader is not thread-safe and does not fully
|
||||
// comply with the StateReader interface requirements, needs to be fixed.
|
||||
// Currently, it is only used in a non-concurrent context, so it is safe for now.
|
||||
type historicReader struct {
|
||||
// historicStateReader implements StateReader, wrapping a historical state reader
|
||||
// defined in path database and provide historic state serving over the path scheme.
|
||||
type historicStateReader struct {
|
||||
reader *pathdb.HistoricalStateReader
|
||||
lock sync.Mutex // Lock for protecting concurrent read
|
||||
}
|
||||
|
||||
// newHistoricReader constructs a reader for historic state serving.
|
||||
func newHistoricReader(r *pathdb.HistoricalStateReader) *historicReader {
|
||||
return &historicReader{reader: r}
|
||||
// newHistoricStateReader constructs a reader for historical state serving.
|
||||
func newHistoricStateReader(r *pathdb.HistoricalStateReader) *historicStateReader {
|
||||
return &historicStateReader{reader: r}
|
||||
}
|
||||
|
||||
// Account implements StateReader, retrieving the account specified by the address.
|
||||
//
|
||||
// An error will be returned if the associated snapshot is already stale or
|
||||
// the requested account is not yet covered by the snapshot.
|
||||
//
|
||||
// The returned account might be nil if it's not existent.
|
||||
func (r *historicReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
func (r *historicStateReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
account, err := r.reader.Account(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -80,7 +78,10 @@ func (r *historicReader) Account(addr common.Address) (*types.StateAccount, erro
|
|||
// the requested storage slot is not yet covered by the snapshot.
|
||||
//
|
||||
// The returned storage slot might be empty if it's not existent.
|
||||
func (r *historicReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||
func (r *historicStateReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
blob, err := r.reader.Storage(addr, key)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
|
|
@ -97,43 +98,190 @@ func (r *historicReader) Storage(addr common.Address, key common.Hash) (common.H
|
|||
return slot, nil
|
||||
}
|
||||
|
||||
// historicTrieOpener is a wrapper of pathdb.HistoricalNodeReader, implementing
|
||||
// the database.NodeDatabase by adding NodeReader function.
|
||||
type historicTrieOpener struct {
|
||||
root common.Hash
|
||||
reader *pathdb.HistoricalNodeReader
|
||||
}
|
||||
|
||||
// newHistoricTrieOpener constructs the historical trie opener.
|
||||
func newHistoricTrieOpener(root common.Hash, reader *pathdb.HistoricalNodeReader) *historicTrieOpener {
|
||||
return &historicTrieOpener{
|
||||
root: root,
|
||||
reader: reader,
|
||||
}
|
||||
}
|
||||
|
||||
// NodeReader implements database.NodeDatabase, returning a node reader of a
|
||||
// specified state.
|
||||
func (o *historicTrieOpener) NodeReader(root common.Hash) (database.NodeReader, error) {
|
||||
if root != o.root {
|
||||
return nil, fmt.Errorf("state %x is not available", root)
|
||||
}
|
||||
return o.reader, nil
|
||||
}
|
||||
|
||||
// historicalTrieReader wraps a historical node reader defined in path database,
|
||||
// providing historical node serving over the path scheme.
|
||||
type historicalTrieReader struct {
|
||||
root common.Hash
|
||||
opener *historicTrieOpener
|
||||
tr Trie
|
||||
|
||||
subRoots map[common.Address]common.Hash // Set of storage roots, cached when the account is resolved
|
||||
subTries map[common.Address]Trie // Group of storage tries, cached when it's resolved
|
||||
lock sync.Mutex // Lock for protecting concurrent read
|
||||
}
|
||||
|
||||
// newHistoricalTrieReader constructs a reader for historical trie node serving.
|
||||
func newHistoricalTrieReader(root common.Hash, r *pathdb.HistoricalNodeReader) (*historicalTrieReader, error) {
|
||||
opener := newHistoricTrieOpener(root, r)
|
||||
tr, err := trie.NewStateTrie(trie.StateTrieID(root), opener)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &historicalTrieReader{
|
||||
root: root,
|
||||
opener: opener,
|
||||
tr: tr,
|
||||
subRoots: make(map[common.Address]common.Hash),
|
||||
subTries: make(map[common.Address]Trie),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// account is the inner version of Account and assumes the r.lock is already held.
|
||||
func (r *historicalTrieReader) account(addr common.Address) (*types.StateAccount, error) {
|
||||
account, err := r.tr.GetAccount(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if account == nil {
|
||||
r.subRoots[addr] = types.EmptyRootHash
|
||||
} else {
|
||||
r.subRoots[addr] = account.Root
|
||||
}
|
||||
return account, nil
|
||||
}
|
||||
|
||||
// Account implements StateReader, retrieving the account specified by the address.
|
||||
//
|
||||
// An error will be returned if the associated snapshot is already stale or
|
||||
// the requested account is not yet covered by the snapshot.
|
||||
//
|
||||
// The returned account might be nil if it's not existent.
|
||||
func (r *historicalTrieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
return r.account(addr)
|
||||
}
|
||||
|
||||
// Storage implements StateReader, retrieving the storage slot specified by the
|
||||
// address and slot key.
|
||||
//
|
||||
// An error will be returned if the associated snapshot is already stale or
|
||||
// the requested storage slot is not yet covered by the snapshot.
|
||||
//
|
||||
// The returned storage slot might be empty if it's not existent.
|
||||
func (r *historicalTrieReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
|
||||
tr, found := r.subTries[addr]
|
||||
if !found {
|
||||
root, ok := r.subRoots[addr]
|
||||
|
||||
// The storage slot is accessed without account caching. It's unexpected
|
||||
// behavior but try to resolve the account first anyway.
|
||||
if !ok {
|
||||
_, err := r.account(addr)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
root = r.subRoots[addr]
|
||||
}
|
||||
var err error
|
||||
tr, err = trie.NewStateTrie(trie.StorageTrieID(r.root, crypto.Keccak256Hash(addr.Bytes()), root), r.opener)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
r.subTries[addr] = tr
|
||||
}
|
||||
ret, err := tr.GetStorage(addr, key.Bytes())
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
var value common.Hash
|
||||
value.SetBytes(ret)
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// HistoricDB is the implementation of Database interface, with the ability to
|
||||
// access historical state.
|
||||
type HistoricDB struct {
|
||||
disk ethdb.KeyValueStore
|
||||
triedb *triedb.Database
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
codeSizeCache *lru.Cache[common.Hash, int]
|
||||
triedb *triedb.Database
|
||||
codedb *CodeDB
|
||||
}
|
||||
|
||||
// NewHistoricDatabase creates a historic state database.
|
||||
func NewHistoricDatabase(disk ethdb.KeyValueStore, triedb *triedb.Database) *HistoricDB {
|
||||
func NewHistoricDatabase(triedb *triedb.Database, codedb *CodeDB) *HistoricDB {
|
||||
return &HistoricDB{
|
||||
disk: disk,
|
||||
triedb: triedb,
|
||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||
triedb: triedb,
|
||||
codedb: codedb,
|
||||
}
|
||||
}
|
||||
|
||||
// Reader implements Database interface, returning a reader of the specific state.
|
||||
func (db *HistoricDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||
hr, err := db.triedb.HistoricReader(stateRoot)
|
||||
var readers []StateReader
|
||||
sr, err := db.triedb.HistoricStateReader(stateRoot)
|
||||
if err == nil {
|
||||
readers = append(readers, newHistoricStateReader(sr))
|
||||
}
|
||||
nr, err := db.triedb.HistoricNodeReader(stateRoot)
|
||||
if err == nil {
|
||||
tr, err := newHistoricalTrieReader(stateRoot, nr)
|
||||
if err == nil {
|
||||
readers = append(readers, tr)
|
||||
}
|
||||
}
|
||||
if len(readers) == 0 {
|
||||
return nil, fmt.Errorf("historical state %x is not available", stateRoot)
|
||||
}
|
||||
combined, err := newMultiStateReader(readers...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newReader(newCachingCodeReader(db.disk, db.codeCache, db.codeSizeCache), newHistoricReader(hr)), nil
|
||||
return newReader(db.codedb.Reader(), combined), nil
|
||||
}
|
||||
|
||||
// OpenTrie opens the main account trie. It's not supported by historic database.
|
||||
func (db *HistoricDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
nr, err := db.triedb.HistoricNodeReader(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr, err := trie.NewStateTrie(trie.StateTrieID(root), newHistoricTrieOpener(root, nr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// OpenStorageTrie opens the storage trie of an account. It's not supported by
|
||||
// historic database.
|
||||
func (db *HistoricDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, trie Trie) (Trie, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
func (db *HistoricDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, _ Trie) (Trie, error) {
|
||||
nr, err := db.triedb.HistoricNodeReader(stateRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
id := trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root)
|
||||
tr, err := trie.NewStateTrie(id, newHistoricTrieOpener(stateRoot, nr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// TrieDB returns the underlying trie database for managing trie nodes.
|
||||
|
|
@ -145,3 +293,10 @@ func (db *HistoricDB) TrieDB() *triedb.Database {
|
|||
func (db *HistoricDB) Snapshot() *snapshot.Tree {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commit flushes all pending writes and finalizes the state transition,
|
||||
// committing the changes to the underlying storage. It returns an error
|
||||
// if the commit fails.
|
||||
func (db *HistoricDB) Commit(update *stateUpdate) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,10 +144,7 @@ func (it *nodeIterator) step() error {
|
|||
}
|
||||
if !bytes.Equal(account.CodeHash, types.EmptyCodeHash.Bytes()) {
|
||||
it.codeHash = common.BytesToHash(account.CodeHash)
|
||||
it.code, err = it.state.reader.Code(address, common.BytesToHash(account.CodeHash))
|
||||
if err != nil {
|
||||
return fmt.Errorf("code %x: %v", account.CodeHash, err)
|
||||
}
|
||||
it.code = it.state.reader.Code(address, common.BytesToHash(account.CodeHash))
|
||||
if len(it.code) == 0 {
|
||||
return fmt.Errorf("code is not found: %x", account.CodeHash)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ type journalEntry interface {
|
|||
revert(*StateDB)
|
||||
|
||||
// dirtied returns the Ethereum address modified by this journal entry.
|
||||
dirtied() *common.Address
|
||||
// indicates false if no address was changed.
|
||||
dirtied() (common.Address, bool)
|
||||
|
||||
// copy returns a deep-copied journal entry.
|
||||
copy() journalEntry
|
||||
|
|
@ -100,8 +101,8 @@ func (j *journal) revertToSnapshot(revid int, s *StateDB) {
|
|||
// append inserts a new modification entry to the end of the change journal.
|
||||
func (j *journal) append(entry journalEntry) {
|
||||
j.entries = append(j.entries, entry)
|
||||
if addr := entry.dirtied(); addr != nil {
|
||||
j.dirties[*addr]++
|
||||
if addr, dirty := entry.dirtied(); dirty {
|
||||
j.dirties[addr]++
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,9 +114,9 @@ func (j *journal) revert(statedb *StateDB, snapshot int) {
|
|||
j.entries[i].revert(statedb)
|
||||
|
||||
// Drop any dirty tracking induced by the change
|
||||
if addr := j.entries[i].dirtied(); addr != nil {
|
||||
if j.dirties[*addr]--; j.dirties[*addr] == 0 {
|
||||
delete(j.dirties, *addr)
|
||||
if addr, dirty := j.entries[i].dirtied(); dirty {
|
||||
if j.dirties[addr]--; j.dirties[addr] == 0 {
|
||||
delete(j.dirties, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -294,8 +295,8 @@ func (ch createObjectChange) revert(s *StateDB) {
|
|||
delete(s.stateObjects, ch.account)
|
||||
}
|
||||
|
||||
func (ch createObjectChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
func (ch createObjectChange) dirtied() (common.Address, bool) {
|
||||
return ch.account, true
|
||||
}
|
||||
|
||||
func (ch createObjectChange) copy() journalEntry {
|
||||
|
|
@ -308,8 +309,8 @@ func (ch createContractChange) revert(s *StateDB) {
|
|||
s.getStateObject(ch.account).newContract = false
|
||||
}
|
||||
|
||||
func (ch createContractChange) dirtied() *common.Address {
|
||||
return nil
|
||||
func (ch createContractChange) dirtied() (common.Address, bool) {
|
||||
return common.Address{}, false
|
||||
}
|
||||
|
||||
func (ch createContractChange) copy() journalEntry {
|
||||
|
|
@ -325,8 +326,8 @@ func (ch selfDestructChange) revert(s *StateDB) {
|
|||
}
|
||||
}
|
||||
|
||||
func (ch selfDestructChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
func (ch selfDestructChange) dirtied() (common.Address, bool) {
|
||||
return ch.account, true
|
||||
}
|
||||
|
||||
func (ch selfDestructChange) copy() journalEntry {
|
||||
|
|
@ -340,8 +341,8 @@ var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
|
|||
func (ch touchChange) revert(s *StateDB) {
|
||||
}
|
||||
|
||||
func (ch touchChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
func (ch touchChange) dirtied() (common.Address, bool) {
|
||||
return ch.account, true
|
||||
}
|
||||
|
||||
func (ch touchChange) copy() journalEntry {
|
||||
|
|
@ -354,8 +355,8 @@ func (ch balanceChange) revert(s *StateDB) {
|
|||
s.getStateObject(ch.account).setBalance(ch.prev)
|
||||
}
|
||||
|
||||
func (ch balanceChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
func (ch balanceChange) dirtied() (common.Address, bool) {
|
||||
return ch.account, true
|
||||
}
|
||||
|
||||
func (ch balanceChange) copy() journalEntry {
|
||||
|
|
@ -369,8 +370,8 @@ func (ch nonceChange) revert(s *StateDB) {
|
|||
s.getStateObject(ch.account).setNonce(ch.prev)
|
||||
}
|
||||
|
||||
func (ch nonceChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
func (ch nonceChange) dirtied() (common.Address, bool) {
|
||||
return ch.account, true
|
||||
}
|
||||
|
||||
func (ch nonceChange) copy() journalEntry {
|
||||
|
|
@ -384,8 +385,8 @@ func (ch codeChange) revert(s *StateDB) {
|
|||
s.getStateObject(ch.account).setCode(crypto.Keccak256Hash(ch.prevCode), ch.prevCode)
|
||||
}
|
||||
|
||||
func (ch codeChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
func (ch codeChange) dirtied() (common.Address, bool) {
|
||||
return ch.account, true
|
||||
}
|
||||
|
||||
func (ch codeChange) copy() journalEntry {
|
||||
|
|
@ -399,8 +400,8 @@ func (ch storageChange) revert(s *StateDB) {
|
|||
s.getStateObject(ch.account).setState(ch.key, ch.prevvalue, ch.origvalue)
|
||||
}
|
||||
|
||||
func (ch storageChange) dirtied() *common.Address {
|
||||
return &ch.account
|
||||
func (ch storageChange) dirtied() (common.Address, bool) {
|
||||
return ch.account, true
|
||||
}
|
||||
|
||||
func (ch storageChange) copy() journalEntry {
|
||||
|
|
@ -416,8 +417,8 @@ func (ch transientStorageChange) revert(s *StateDB) {
|
|||
s.setTransientState(ch.account, ch.key, ch.prevalue)
|
||||
}
|
||||
|
||||
func (ch transientStorageChange) dirtied() *common.Address {
|
||||
return nil
|
||||
func (ch transientStorageChange) dirtied() (common.Address, bool) {
|
||||
return common.Address{}, false
|
||||
}
|
||||
|
||||
func (ch transientStorageChange) copy() journalEntry {
|
||||
|
|
@ -432,8 +433,8 @@ func (ch refundChange) revert(s *StateDB) {
|
|||
s.refund = ch.prev
|
||||
}
|
||||
|
||||
func (ch refundChange) dirtied() *common.Address {
|
||||
return nil
|
||||
func (ch refundChange) dirtied() (common.Address, bool) {
|
||||
return common.Address{}, false
|
||||
}
|
||||
|
||||
func (ch refundChange) copy() journalEntry {
|
||||
|
|
@ -452,8 +453,8 @@ func (ch addLogChange) revert(s *StateDB) {
|
|||
s.logSize--
|
||||
}
|
||||
|
||||
func (ch addLogChange) dirtied() *common.Address {
|
||||
return nil
|
||||
func (ch addLogChange) dirtied() (common.Address, bool) {
|
||||
return common.Address{}, false
|
||||
}
|
||||
|
||||
func (ch addLogChange) copy() journalEntry {
|
||||
|
|
@ -475,8 +476,8 @@ func (ch accessListAddAccountChange) revert(s *StateDB) {
|
|||
s.accessList.DeleteAddress(ch.address)
|
||||
}
|
||||
|
||||
func (ch accessListAddAccountChange) dirtied() *common.Address {
|
||||
return nil
|
||||
func (ch accessListAddAccountChange) dirtied() (common.Address, bool) {
|
||||
return common.Address{}, false
|
||||
}
|
||||
|
||||
func (ch accessListAddAccountChange) copy() journalEntry {
|
||||
|
|
@ -489,8 +490,8 @@ func (ch accessListAddSlotChange) revert(s *StateDB) {
|
|||
s.accessList.DeleteSlot(ch.address, ch.slot)
|
||||
}
|
||||
|
||||
func (ch accessListAddSlotChange) dirtied() *common.Address {
|
||||
return nil
|
||||
func (ch accessListAddSlotChange) dirtied() (common.Address, bool) {
|
||||
return common.Address{}, false
|
||||
}
|
||||
|
||||
func (ch accessListAddSlotChange) copy() journalEntry {
|
||||
|
|
|
|||
|
|
@ -18,17 +18,13 @@ package state
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/lru"
|
||||
"github.com/ethereum/go-ethereum/core/overlay"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/bintrie"
|
||||
|
|
@ -38,38 +34,26 @@ import (
|
|||
)
|
||||
|
||||
// ContractCodeReader defines the interface for accessing contract code.
|
||||
//
|
||||
// ContractCodeReader is supposed to be thread-safe.
|
||||
type ContractCodeReader interface {
|
||||
// Has returns the flag indicating whether the contract code with
|
||||
// specified address and hash exists or not.
|
||||
Has(addr common.Address, codeHash common.Hash) bool
|
||||
|
||||
// Code retrieves a particular contract's code.
|
||||
//
|
||||
// - Returns nil code along with nil error if the requested contract code
|
||||
// doesn't exist
|
||||
// - Returns an error only if an unexpected issue occurs
|
||||
Code(addr common.Address, codeHash common.Hash) ([]byte, error)
|
||||
// Code retrieves a particular contract's code. Returns nil code if the
|
||||
// requested contract code doesn't exist.
|
||||
Code(addr common.Address, codeHash common.Hash) []byte
|
||||
|
||||
// CodeSize retrieves a particular contracts code's size.
|
||||
//
|
||||
// - Returns zero code size along with nil error if the requested contract code
|
||||
// doesn't exist
|
||||
// - Returns an error only if an unexpected issue occurs
|
||||
CodeSize(addr common.Address, codeHash common.Hash) (int, error)
|
||||
}
|
||||
|
||||
// ContractCodeReaderWithStats extends ContractCodeReader by adding GetStats to
|
||||
// expose statistics of code reader.
|
||||
type ContractCodeReaderWithStats interface {
|
||||
ContractCodeReader
|
||||
GetStats() (int64, int64)
|
||||
// CodeSize retrieves a particular contracts code's size. Returns zero code
|
||||
// size if the requested contract code doesn't exist.
|
||||
CodeSize(addr common.Address, codeHash common.Hash) int
|
||||
}
|
||||
|
||||
// StateReader defines the interface for accessing accounts and storage slots
|
||||
// associated with a specific state.
|
||||
//
|
||||
// StateReader is assumed to be thread-safe and implementation must take care
|
||||
// of the concurrency issue by themselves.
|
||||
// StateReader is supposed to be thread-safe.
|
||||
type StateReader interface {
|
||||
// Account retrieves the account associated with a particular address.
|
||||
//
|
||||
|
|
@ -97,116 +81,6 @@ type Reader interface {
|
|||
StateReader
|
||||
}
|
||||
|
||||
// ReaderStats wraps the statistics of reader.
|
||||
type ReaderStats struct {
|
||||
// Cache stats
|
||||
AccountCacheHit int64
|
||||
AccountCacheMiss int64
|
||||
StorageCacheHit int64
|
||||
StorageCacheMiss int64
|
||||
ContractCodeHit int64
|
||||
ContractCodeMiss int64
|
||||
}
|
||||
|
||||
// String implements fmt.Stringer, returning string format statistics.
|
||||
func (s ReaderStats) String() string {
|
||||
var (
|
||||
accountCacheHitRate float64
|
||||
storageCacheHitRate float64
|
||||
contractCodeHitRate float64
|
||||
)
|
||||
if s.AccountCacheHit > 0 {
|
||||
accountCacheHitRate = float64(s.AccountCacheHit) / float64(s.AccountCacheHit+s.AccountCacheMiss) * 100
|
||||
}
|
||||
if s.StorageCacheHit > 0 {
|
||||
storageCacheHitRate = float64(s.StorageCacheHit) / float64(s.StorageCacheHit+s.StorageCacheMiss) * 100
|
||||
}
|
||||
if s.ContractCodeHit > 0 {
|
||||
contractCodeHitRate = float64(s.ContractCodeHit) / float64(s.ContractCodeHit+s.ContractCodeMiss) * 100
|
||||
}
|
||||
msg := fmt.Sprintf("Reader statistics\n")
|
||||
msg += fmt.Sprintf("account: hit: %d, miss: %d, rate: %.2f\n", s.AccountCacheHit, s.AccountCacheMiss, accountCacheHitRate)
|
||||
msg += fmt.Sprintf("storage: hit: %d, miss: %d, rate: %.2f\n", s.StorageCacheHit, s.StorageCacheMiss, storageCacheHitRate)
|
||||
msg += fmt.Sprintf("code: hit: %d, miss: %d, rate: %.2f\n", s.ContractCodeHit, s.ContractCodeMiss, contractCodeHitRate)
|
||||
return msg
|
||||
}
|
||||
|
||||
// ReaderWithStats wraps the additional method to retrieve the reader statistics from.
|
||||
type ReaderWithStats interface {
|
||||
Reader
|
||||
GetStats() ReaderStats
|
||||
}
|
||||
|
||||
// cachingCodeReader implements ContractCodeReader, accessing contract code either in
|
||||
// local key-value store or the shared code cache.
|
||||
//
|
||||
// cachingCodeReader is safe for concurrent access.
|
||||
type cachingCodeReader struct {
|
||||
db ethdb.KeyValueReader
|
||||
|
||||
// These caches could be shared by multiple code reader instances,
|
||||
// they are natively thread-safe.
|
||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||
codeSizeCache *lru.Cache[common.Hash, int]
|
||||
|
||||
// Cache statistics
|
||||
hit atomic.Int64 // Number of code lookups found in the cache.
|
||||
miss atomic.Int64 // Number of code lookups not found in the cache.
|
||||
}
|
||||
|
||||
// newCachingCodeReader constructs the code reader.
|
||||
func newCachingCodeReader(db ethdb.KeyValueReader, codeCache *lru.SizeConstrainedCache[common.Hash, []byte], codeSizeCache *lru.Cache[common.Hash, int]) *cachingCodeReader {
|
||||
return &cachingCodeReader{
|
||||
db: db,
|
||||
codeCache: codeCache,
|
||||
codeSizeCache: codeSizeCache,
|
||||
}
|
||||
}
|
||||
|
||||
// Code implements ContractCodeReader, retrieving a particular contract's code.
|
||||
// If the contract code doesn't exist, no error will be returned.
|
||||
func (r *cachingCodeReader) Code(addr common.Address, codeHash common.Hash) ([]byte, error) {
|
||||
code, _ := r.codeCache.Get(codeHash)
|
||||
if len(code) > 0 {
|
||||
r.hit.Add(1)
|
||||
return code, nil
|
||||
}
|
||||
r.miss.Add(1)
|
||||
|
||||
code = rawdb.ReadCode(r.db, codeHash)
|
||||
if len(code) > 0 {
|
||||
r.codeCache.Add(codeHash, code)
|
||||
r.codeSizeCache.Add(codeHash, len(code))
|
||||
}
|
||||
return code, nil
|
||||
}
|
||||
|
||||
// CodeSize implements ContractCodeReader, retrieving a particular contracts code's size.
|
||||
// If the contract code doesn't exist, no error will be returned.
|
||||
func (r *cachingCodeReader) CodeSize(addr common.Address, codeHash common.Hash) (int, error) {
|
||||
if cached, ok := r.codeSizeCache.Get(codeHash); ok {
|
||||
r.hit.Add(1)
|
||||
return cached, nil
|
||||
}
|
||||
code, err := r.Code(addr, codeHash)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(code), nil
|
||||
}
|
||||
|
||||
// Has returns the flag indicating whether the contract code with
|
||||
// specified address and hash exists or not.
|
||||
func (r *cachingCodeReader) Has(addr common.Address, codeHash common.Hash) bool {
|
||||
code, _ := r.Code(addr, codeHash)
|
||||
return len(code) > 0
|
||||
}
|
||||
|
||||
// GetStats returns the cache statistics fo the code reader.
|
||||
func (r *cachingCodeReader) GetStats() (int64, int64) {
|
||||
return r.hit.Load(), r.miss.Load()
|
||||
}
|
||||
|
||||
// flatReader wraps a database state reader and is safe for concurrent access.
|
||||
type flatReader struct {
|
||||
reader database.StateReader
|
||||
|
|
@ -224,7 +98,7 @@ func newFlatReader(reader database.StateReader) *flatReader {
|
|||
//
|
||||
// The returned account might be nil if it's not existent.
|
||||
func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
account, err := r.reader.Account(crypto.Keccak256Hash(addr.Bytes()))
|
||||
account, err := r.reader.Account(crypto.Keccak256Hash(addr[:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -254,8 +128,8 @@ func (r *flatReader) Account(addr common.Address) (*types.StateAccount, error) {
|
|||
//
|
||||
// The returned storage slot might be empty if it's not existent.
|
||||
func (r *flatReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||
addrHash := crypto.Keccak256Hash(addr.Bytes())
|
||||
slotHash := crypto.Keccak256Hash(key.Bytes())
|
||||
addrHash := crypto.Keccak256Hash(addr[:])
|
||||
slotHash := crypto.Keccak256Hash(key[:])
|
||||
ret, err := r.reader.Storage(addrHash, slotHash)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
|
|
@ -475,20 +349,6 @@ func (r *multiStateReader) Storage(addr common.Address, slot common.Hash) (commo
|
|||
return common.Hash{}, errors.Join(errs...)
|
||||
}
|
||||
|
||||
// reader is the wrapper of ContractCodeReader and StateReader interface.
|
||||
type reader struct {
|
||||
ContractCodeReader
|
||||
StateReader
|
||||
}
|
||||
|
||||
// newReader constructs a reader with the supplied code reader and state reader.
|
||||
func newReader(codeReader ContractCodeReader, stateReader StateReader) *reader {
|
||||
return &reader{
|
||||
ContractCodeReader: codeReader,
|
||||
StateReader: stateReader,
|
||||
}
|
||||
}
|
||||
|
||||
// stateReaderWithCache is a wrapper around StateReader that maintains additional
|
||||
// state caches to support concurrent state access.
|
||||
type stateReaderWithCache struct {
|
||||
|
|
@ -599,9 +459,10 @@ func (r *stateReaderWithCache) Storage(addr common.Address, slot common.Hash) (c
|
|||
return value, err
|
||||
}
|
||||
|
||||
type readerWithStats struct {
|
||||
// stateReaderWithStats is a wrapper over the stateReaderWithCache, tracking
|
||||
// the cache hit statistics of the reader.
|
||||
type stateReaderWithStats struct {
|
||||
*stateReaderWithCache
|
||||
ContractCodeReaderWithStats
|
||||
|
||||
accountCacheHit atomic.Int64
|
||||
accountCacheMiss atomic.Int64
|
||||
|
|
@ -609,11 +470,10 @@ type readerWithStats struct {
|
|||
storageCacheMiss atomic.Int64
|
||||
}
|
||||
|
||||
// newReaderWithStats constructs the reader with additional statistics tracked.
|
||||
func newReaderWithStats(sr *stateReaderWithCache, cr ContractCodeReaderWithStats) *readerWithStats {
|
||||
return &readerWithStats{
|
||||
stateReaderWithCache: sr,
|
||||
ContractCodeReaderWithStats: cr,
|
||||
// newReaderWithStats constructs the state reader with additional statistics tracked.
|
||||
func newStateReaderWithStats(sr *stateReaderWithCache) *stateReaderWithStats {
|
||||
return &stateReaderWithStats{
|
||||
stateReaderWithCache: sr,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -621,7 +481,7 @@ func newReaderWithStats(sr *stateReaderWithCache, cr ContractCodeReaderWithStats
|
|||
// The returned account might be nil if it's not existent.
|
||||
//
|
||||
// An error will be returned if the state is corrupted in the underlying reader.
|
||||
func (r *readerWithStats) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
func (r *stateReaderWithStats) Account(addr common.Address) (*types.StateAccount, error) {
|
||||
account, incache, err := r.stateReaderWithCache.account(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -639,7 +499,7 @@ func (r *readerWithStats) Account(addr common.Address) (*types.StateAccount, err
|
|||
// existent.
|
||||
//
|
||||
// An error will be returned if the state is corrupted in the underlying reader.
|
||||
func (r *readerWithStats) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||
func (r *stateReaderWithStats) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||
value, incache, err := r.stateReaderWithCache.storage(addr, slot)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
|
|
@ -652,15 +512,51 @@ func (r *readerWithStats) Storage(addr common.Address, slot common.Hash) (common
|
|||
return value, nil
|
||||
}
|
||||
|
||||
// GetStats implements ReaderWithStats, returning the statistics of state reader.
|
||||
func (r *readerWithStats) GetStats() ReaderStats {
|
||||
codeHit, codeMiss := r.ContractCodeReaderWithStats.GetStats()
|
||||
return ReaderStats{
|
||||
// GetStateStats implements StateReaderStater, returning the statistics of the
|
||||
// state reader.
|
||||
func (r *stateReaderWithStats) GetStateStats() StateReaderStats {
|
||||
return StateReaderStats{
|
||||
AccountCacheHit: r.accountCacheHit.Load(),
|
||||
AccountCacheMiss: r.accountCacheMiss.Load(),
|
||||
StorageCacheHit: r.storageCacheHit.Load(),
|
||||
StorageCacheMiss: r.storageCacheMiss.Load(),
|
||||
ContractCodeHit: codeHit,
|
||||
ContractCodeMiss: codeMiss,
|
||||
}
|
||||
}
|
||||
|
||||
// reader aggregates a code reader and a state reader into a single object.
|
||||
type reader struct {
|
||||
ContractCodeReader
|
||||
StateReader
|
||||
}
|
||||
|
||||
// newReader constructs a reader with the supplied code reader and state reader.
|
||||
func newReader(codeReader ContractCodeReader, stateReader StateReader) *reader {
|
||||
return &reader{
|
||||
ContractCodeReader: codeReader,
|
||||
StateReader: stateReader,
|
||||
}
|
||||
}
|
||||
|
||||
// GetCodeStats returns the statistics of code access.
|
||||
func (r *reader) GetCodeStats() ContractCodeReaderStats {
|
||||
if stater, ok := r.ContractCodeReader.(ContractCodeReaderStater); ok {
|
||||
return stater.GetCodeStats()
|
||||
}
|
||||
return ContractCodeReaderStats{}
|
||||
}
|
||||
|
||||
// GetStateStats returns the statistics of state access.
|
||||
func (r *reader) GetStateStats() StateReaderStats {
|
||||
if stater, ok := r.StateReader.(StateReaderStater); ok {
|
||||
return stater.GetStateStats()
|
||||
}
|
||||
return StateReaderStats{}
|
||||
}
|
||||
|
||||
// GetStats returns the aggregated statistics for both state and code access.
|
||||
func (r *reader) GetStats() ReaderStats {
|
||||
return ReaderStats{
|
||||
CodeStats: r.GetCodeStats(),
|
||||
StateStats: r.GetStateStats(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
82
core/state/reader_stater.go
Normal file
82
core/state/reader_stater.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2026 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 state
|
||||
|
||||
// ContractCodeReaderStats aggregates statistics for the contract code reader.
|
||||
type ContractCodeReaderStats struct {
|
||||
CacheHit int64 // Number of cache hits
|
||||
CacheMiss int64 // Number of cache misses
|
||||
CacheHitBytes int64 // Total bytes served from cache
|
||||
CacheMissBytes int64 // Total bytes read on cache misses
|
||||
}
|
||||
|
||||
// HitRate returns the cache hit rate in percentage.
|
||||
func (s ContractCodeReaderStats) HitRate() float64 {
|
||||
total := s.CacheHit + s.CacheMiss
|
||||
if total == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(s.CacheHit) / float64(total) * 100
|
||||
}
|
||||
|
||||
// ContractCodeReaderStater wraps the method to retrieve the statistics of
|
||||
// contract code reader.
|
||||
type ContractCodeReaderStater interface {
|
||||
GetCodeStats() ContractCodeReaderStats
|
||||
}
|
||||
|
||||
// StateReaderStats aggregates statistics for the state reader.
|
||||
type StateReaderStats struct {
|
||||
AccountCacheHit int64 // Number of account cache hits
|
||||
AccountCacheMiss int64 // Number of account cache misses
|
||||
StorageCacheHit int64 // Number of storage cache hits
|
||||
StorageCacheMiss int64 // Number of storage cache misses
|
||||
}
|
||||
|
||||
// AccountCacheHitRate returns the cache hit rate of account requests in percentage.
|
||||
func (s StateReaderStats) AccountCacheHitRate() float64 {
|
||||
total := s.AccountCacheHit + s.AccountCacheMiss
|
||||
if total == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(s.AccountCacheHit) / float64(total) * 100
|
||||
}
|
||||
|
||||
// StorageCacheHitRate returns the cache hit rate of storage requests in percentage.
|
||||
func (s StateReaderStats) StorageCacheHitRate() float64 {
|
||||
total := s.StorageCacheHit + s.StorageCacheMiss
|
||||
if total == 0 {
|
||||
return 0
|
||||
}
|
||||
return float64(s.StorageCacheHit) / float64(total) * 100
|
||||
}
|
||||
|
||||
// StateReaderStater wraps the method to retrieve the statistics of state reader.
|
||||
type StateReaderStater interface {
|
||||
GetStateStats() StateReaderStats
|
||||
}
|
||||
|
||||
// ReaderStats wraps the statistics of reader.
|
||||
type ReaderStats struct {
|
||||
CodeStats ContractCodeReaderStats
|
||||
StateStats StateReaderStats
|
||||
}
|
||||
|
||||
// ReaderStater defines the capability to retrieve aggregated statistics.
|
||||
type ReaderStater interface {
|
||||
GetStats() ReaderStats
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
|
@ -34,16 +35,10 @@ import (
|
|||
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
"github.com/holiman/uint256"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
func hashData(input []byte) common.Hash {
|
||||
var hasher = sha3.NewLegacyKeccak256()
|
||||
var hash common.Hash
|
||||
hasher.Reset()
|
||||
hasher.Write(input)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
return crypto.Keccak256Hash(input)
|
||||
}
|
||||
|
||||
// Tests that snapshot generation from an empty database.
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue