mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
Merge tag 'v1.14.10' into lmartins/upstream_merge_v1.14.10
This commit is contained in:
commit
fc2198bedd
276 changed files with 8415 additions and 3118 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -30,6 +30,14 @@ cover.out
|
||||||
/build/bin/
|
/build/bin/
|
||||||
/geth*.zip
|
/geth*.zip
|
||||||
|
|
||||||
|
# used by the build/ci.go archive + upload tool
|
||||||
|
/geth*.tar.gz
|
||||||
|
/geth*.tar.gz.sig
|
||||||
|
/geth*.tar.gz.asc
|
||||||
|
/geth*.zip.sig
|
||||||
|
/geth*.zip.asc
|
||||||
|
|
||||||
|
|
||||||
# travis
|
# travis
|
||||||
profile.tmp
|
profile.tmp
|
||||||
profile.cov
|
profile.cov
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,10 @@ issues:
|
||||||
- path: crypto/bn256/
|
- path: crypto/bn256/
|
||||||
linters:
|
linters:
|
||||||
- revive
|
- revive
|
||||||
|
- path: cmd/utils/flags.go
|
||||||
|
text: "SA1019: cfg.TxLookupLimit is deprecated: use 'TransactionHistory' instead."
|
||||||
|
- path: cmd/utils/flags.go
|
||||||
|
text: "SA1019: ethconfig.Defaults.TxLookupLimit is deprecated: use 'TransactionHistory' instead."
|
||||||
- path: internal/build/pgp.go
|
- path: internal/build/pgp.go
|
||||||
text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.'
|
text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.'
|
||||||
- path: core/vm/contracts.go
|
- path: core/vm/contracts.go
|
||||||
|
|
|
||||||
34
.travis.yml
34
.travis.yml
|
|
@ -27,8 +27,8 @@ jobs:
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
arch: amd64
|
arch: amd64
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- docker
|
- docker
|
||||||
services:
|
services:
|
||||||
|
|
@ -44,8 +44,8 @@ jobs:
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
arch: arm64
|
arch: arm64
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- docker
|
- docker
|
||||||
services:
|
services:
|
||||||
|
|
@ -85,9 +85,9 @@ jobs:
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
dist: noble
|
dist: focal
|
||||||
sudo: required
|
sudo: required
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- azure-linux
|
- azure-linux
|
||||||
git:
|
git:
|
||||||
|
|
@ -99,6 +99,7 @@ jobs:
|
||||||
|
|
||||||
# build 386
|
# build 386
|
||||||
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib
|
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends install gcc-multilib
|
||||||
|
- git status --porcelain
|
||||||
- go run build/ci.go install -dlgo -arch 386
|
- go run build/ci.go install -dlgo -arch 386
|
||||||
- go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
||||||
|
|
||||||
|
|
@ -158,12 +159,13 @@ jobs:
|
||||||
if: type = push
|
if: type = push
|
||||||
os: osx
|
os: osx
|
||||||
osx_image: xcode14.2
|
osx_image: xcode14.2
|
||||||
go: 1.22.x
|
go: 1.23.1 # See https://github.com/ethereum/go-ethereum/pull/30478
|
||||||
env:
|
env:
|
||||||
- azure-osx
|
- azure-osx
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
script:
|
script:
|
||||||
|
- ln -sf /Users/travis/gopath/bin/go1.23.1 /usr/local/bin/go # Work around travis go-setup bug
|
||||||
- go run build/ci.go install -dlgo
|
- go run build/ci.go install -dlgo
|
||||||
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
||||||
- go run build/ci.go install -dlgo -arch arm64
|
- go run build/ci.go install -dlgo -arch arm64
|
||||||
|
|
@ -174,8 +176,8 @@ jobs:
|
||||||
if: type = push
|
if: type = push
|
||||||
os: linux
|
os: linux
|
||||||
arch: amd64
|
arch: amd64
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
script:
|
script:
|
||||||
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
||||||
|
|
||||||
|
|
@ -190,7 +192,7 @@ jobs:
|
||||||
|
|
||||||
- stage: build
|
- stage: build
|
||||||
os: linux
|
os: linux
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.22.x
|
||||||
script:
|
script:
|
||||||
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
- travis_wait 45 go run build/ci.go test $TEST_PACKAGES
|
||||||
|
|
@ -199,8 +201,8 @@ jobs:
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = cron || (type = push && tag ~= /^v[0-9]/)
|
if: type = cron || (type = push && tag ~= /^v[0-9]/)
|
||||||
os: linux
|
os: linux
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- ubuntu-ppa
|
- ubuntu-ppa
|
||||||
git:
|
git:
|
||||||
|
|
@ -215,8 +217,8 @@ jobs:
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = cron
|
if: type = cron
|
||||||
os: linux
|
os: linux
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- azure-purge
|
- azure-purge
|
||||||
git:
|
git:
|
||||||
|
|
@ -228,8 +230,8 @@ jobs:
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = cron
|
if: type = cron
|
||||||
os: linux
|
os: linux
|
||||||
dist: noble
|
dist: focal
|
||||||
go: 1.22.x
|
go: 1.23.x
|
||||||
env:
|
env:
|
||||||
- racetests
|
- racetests
|
||||||
script:
|
script:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
FROM golang:latest
|
# Build Geth in a stock Go builder container
|
||||||
|
FROM golang:1.23-alpine as builder
|
||||||
|
|
||||||
ARG BOR_DIR=/var/lib/bor
|
ARG BOR_DIR=/var/lib/bor
|
||||||
ENV BOR_DIR=$BOR_DIR
|
ENV BOR_DIR=$BOR_DIR
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Build Geth in a stock Go builder container
|
# Build Geth in a stock Go builder container
|
||||||
FROM golang:1.22.1-alpine as builder
|
FROM golang:1.23-alpine as builder
|
||||||
|
|
||||||
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
RUN apk add --no-cache make gcc musl-dev linux-headers git
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1326,3 +1326,10 @@ func TestUnpackRevert(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInternalContractType(t *testing.T) {
|
||||||
|
jsonData := `[{"inputs":[{"components":[{"internalType":"uint256","name":"dailyLimit","type":"uint256"},{"internalType":"uint256","name":"txLimit","type":"uint256"},{"internalType":"uint256","name":"accountDailyLimit","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"},{"internalType":"bool","name":"onlyWhitelisted","type":"bool"}],"internalType":"struct IMessagePassingBridge.BridgeLimits","name":"bridgeLimits","type":"tuple"},{"components":[{"internalType":"uint256","name":"lastTransferReset","type":"uint256"},{"internalType":"uint256","name":"bridged24Hours","type":"uint256"}],"internalType":"struct IMessagePassingBridge.AccountLimit","name":"accountDailyLimit","type":"tuple"},{"components":[{"internalType":"uint256","name":"lastTransferReset","type":"uint256"},{"internalType":"uint256","name":"bridged24Hours","type":"uint256"}],"internalType":"struct IMessagePassingBridge.BridgeDailyLimit","name":"bridgeDailyLimit","type":"tuple"},{"internalType":"contract INameService","name":"nameService","type":"INameService"},{"internalType":"bool","name":"isClosed","type":"bool"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"canBridge","outputs":[{"internalType":"bool","name":"isWithinLimit","type":"bool"},{"internalType":"string","name":"error","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"normalizeFrom18ToTokenDecimals","outputs":[{"internalType":"uint256","name":"normalized","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"normalizeFromTokenTo18Decimals","outputs":[{"internalType":"uint256","name":"normalized","type":"uint256"}],"stateMutability":"pure","type":"function"}]`
|
||||||
|
if _, err := JSON(strings.NewReader(jsonData)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ func (b *SimulatedBackend) rollback(parent *types.Block) {
|
||||||
blocks, _ := core.GenerateChain(b.config, parent, ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
|
blocks, _ := core.GenerateChain(b.config, parent, ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
|
||||||
|
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.blockchain.StateCache(), nil)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.blockchain.StateCache())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fork creates a side-chain that can be used to simulate reorgs.
|
// Fork creates a side-chain that can be used to simulate reorgs.
|
||||||
|
|
@ -699,7 +699,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||||
GasTipCap: call.GasTipCap,
|
GasTipCap: call.GasTipCap,
|
||||||
Data: call.Data,
|
Data: call.Data,
|
||||||
AccessList: call.AccessList,
|
AccessList: call.AccessList,
|
||||||
SkipAccountChecks: true,
|
SkipNonceChecks: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new environment which holds all relevant information
|
// Create a new environment which holds all relevant information
|
||||||
|
|
@ -709,7 +709,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||||
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
|
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
|
||||||
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
|
|
||||||
return core.ApplyMessage(vmEnv, msg, gasPool, context.Background())
|
return core.ApplyMessage(vmEnv, msg, gasPool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTransaction updates the pending block to include the given transaction.
|
// SendTransaction updates the pending block to include the given transaction.
|
||||||
|
|
@ -744,7 +744,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database())
|
||||||
b.pendingReceipts = receipts[0]
|
b.pendingReceipts = receipts[0]
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -867,7 +867,7 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,9 @@ func TestWaitDeployed(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Send and mine the transaction.
|
// Send and mine the transaction.
|
||||||
backend.SendTransaction(ctx, tx)
|
if err := backend.SendTransaction(ctx, tx); err != nil {
|
||||||
|
t.Errorf("test %q: failed to send transaction: %v", name, err)
|
||||||
|
}
|
||||||
backend.Commit()
|
backend.Commit()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|
@ -122,7 +124,9 @@ func TestWaitDeployedCornerCases(t *testing.T) {
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
backend.SendTransaction(ctx, tx)
|
if err := backend.SendTransaction(ctx, tx); err != nil {
|
||||||
|
t.Errorf("failed to send transaction: %q", err)
|
||||||
|
}
|
||||||
backend.Commit()
|
backend.Commit()
|
||||||
notContractCreation := errors.New("tx is not contract creation")
|
notContractCreation := errors.New("tx is not contract creation")
|
||||||
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContractCreation.Error() {
|
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContractCreation.Error() {
|
||||||
|
|
@ -140,6 +144,8 @@ func TestWaitDeployedCornerCases(t *testing.T) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
backend.SendTransaction(ctx, tx)
|
if err := backend.SendTransaction(ctx, tx); err != nil {
|
||||||
|
t.Errorf("failed to send transaction: %q", err)
|
||||||
|
}
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -238,8 +238,13 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
typ.T = FunctionTy
|
typ.T = FunctionTy
|
||||||
typ.Size = 24
|
typ.Size = 24
|
||||||
default:
|
default:
|
||||||
|
if strings.HasPrefix(internalType, "contract ") {
|
||||||
|
typ.Size = 20
|
||||||
|
typ.T = AddressTy
|
||||||
|
} else {
|
||||||
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
0
beacon/blsync/block_sync.go
Executable file → Normal file
0
beacon/blsync/block_sync.go
Executable file → Normal file
|
|
@ -70,7 +70,10 @@ func TestBlockSync(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var expNumber, headNumber uint64
|
var expNumber, headNumber uint64
|
||||||
if expHead != nil {
|
if expHead != nil {
|
||||||
p, _ := expHead.ExecutionPayload()
|
p, err := expHead.ExecutionPayload()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("expHead.ExecutionPayload() failed: %v", err)
|
||||||
|
}
|
||||||
expNumber = p.NumberU64()
|
expNumber = p.NumberU64()
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
|
|
||||||
|
|
@ -56,32 +56,17 @@ var (
|
||||||
AddFork("DENEB", 132608, []byte{144, 0, 0, 115}),
|
AddFork("DENEB", 132608, []byte{144, 0, 0, 115}),
|
||||||
Checkpoint: common.HexToHash("0x1005a6d9175e96bfbce4d35b80f468e9bff0b674e1e861d16e09e10005a58e81"),
|
Checkpoint: common.HexToHash("0x1005a6d9175e96bfbce4d35b80f468e9bff0b674e1e861d16e09e10005a58e81"),
|
||||||
}
|
}
|
||||||
|
|
||||||
GoerliConfig = lightClientConfig{
|
|
||||||
ChainConfig: (&types.ChainConfig{
|
|
||||||
GenesisValidatorsRoot: common.HexToHash("0x043db0d9a83813551ee2f33450d23797757d430911a9320530ad8a0eabc43efb"),
|
|
||||||
GenesisTime: 1614588812,
|
|
||||||
}).
|
|
||||||
AddFork("GENESIS", 0, []byte{0, 0, 16, 32}).
|
|
||||||
AddFork("ALTAIR", 36660, []byte{1, 0, 16, 32}).
|
|
||||||
AddFork("BELLATRIX", 112260, []byte{2, 0, 16, 32}).
|
|
||||||
AddFork("CAPELLA", 162304, []byte{3, 0, 16, 32}).
|
|
||||||
AddFork("DENEB", 231680, []byte{4, 0, 16, 32}),
|
|
||||||
Checkpoint: common.HexToHash("0x53a0f4f0a378e2c4ae0a9ee97407eb69d0d737d8d8cd0a5fb1093f42f7b81c49"),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeChainConfig(ctx *cli.Context) lightClientConfig {
|
func makeChainConfig(ctx *cli.Context) lightClientConfig {
|
||||||
var config lightClientConfig
|
var config lightClientConfig
|
||||||
customConfig := ctx.IsSet(utils.BeaconConfigFlag.Name)
|
customConfig := ctx.IsSet(utils.BeaconConfigFlag.Name)
|
||||||
utils.CheckExclusive(ctx, utils.MainnetFlag, utils.GoerliFlag, utils.SepoliaFlag, utils.BeaconConfigFlag)
|
utils.CheckExclusive(ctx, utils.MainnetFlag, utils.SepoliaFlag, utils.BeaconConfigFlag)
|
||||||
switch {
|
switch {
|
||||||
case ctx.Bool(utils.MainnetFlag.Name):
|
case ctx.Bool(utils.MainnetFlag.Name):
|
||||||
config = MainnetConfig
|
config = MainnetConfig
|
||||||
case ctx.Bool(utils.SepoliaFlag.Name):
|
case ctx.Bool(utils.SepoliaFlag.Name):
|
||||||
config = SepoliaConfig
|
config = SepoliaConfig
|
||||||
case ctx.Bool(utils.GoerliFlag.Name):
|
|
||||||
config = GoerliConfig
|
|
||||||
default:
|
default:
|
||||||
if !customConfig {
|
if !customConfig {
|
||||||
config = MainnetConfig
|
config = MainnetConfig
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
|
Deposits types.Deposits `json:"depositRequests"`
|
||||||
|
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var enc ExecutableData
|
var enc ExecutableData
|
||||||
|
|
@ -61,6 +63,8 @@ func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
enc.Withdrawals = e.Withdrawals
|
enc.Withdrawals = e.Withdrawals
|
||||||
enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
|
enc.BlobGasUsed = (*hexutil.Uint64)(e.BlobGasUsed)
|
||||||
enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
|
enc.ExcessBlobGas = (*hexutil.Uint64)(e.ExcessBlobGas)
|
||||||
|
enc.Deposits = e.Deposits
|
||||||
|
enc.ExecutionWitness = e.ExecutionWitness
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,6 +88,8 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
BlobGasUsed *hexutil.Uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *hexutil.Uint64 `json:"excessBlobGas"`
|
||||||
|
Deposits *types.Deposits `json:"depositRequests"`
|
||||||
|
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var dec ExecutableData
|
var dec ExecutableData
|
||||||
|
|
@ -187,5 +193,11 @@ func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
if dec.ExcessBlobGas != nil {
|
if dec.ExcessBlobGas != nil {
|
||||||
e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
e.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
||||||
}
|
}
|
||||||
|
if dec.Deposits != nil {
|
||||||
|
e.Deposits = *dec.Deposits
|
||||||
|
}
|
||||||
|
if dec.ExecutionWitness != nil {
|
||||||
|
e.ExecutionWitness = dec.ExecutionWitness
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) {
|
||||||
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||||
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||||
Override bool `json:"shouldOverrideBuilder"`
|
Override bool `json:"shouldOverrideBuilder"`
|
||||||
|
Witness *hexutil.Bytes `json:"witness"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var enc ExecutionPayloadEnvelope
|
var enc ExecutionPayloadEnvelope
|
||||||
|
|
@ -26,6 +27,7 @@ func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) {
|
||||||
enc.BlockValue = (*hexutil.Big)(e.BlockValue)
|
enc.BlockValue = (*hexutil.Big)(e.BlockValue)
|
||||||
enc.BlobsBundle = e.BlobsBundle
|
enc.BlobsBundle = e.BlobsBundle
|
||||||
enc.Override = e.Override
|
enc.Override = e.Override
|
||||||
|
enc.Witness = e.Witness
|
||||||
return json.Marshal(&enc)
|
return json.Marshal(&enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,6 +38,7 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
|
||||||
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||||
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||||
Override *bool `json:"shouldOverrideBuilder"`
|
Override *bool `json:"shouldOverrideBuilder"`
|
||||||
|
Witness *hexutil.Bytes `json:"witness"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var dec ExecutionPayloadEnvelope
|
var dec ExecutionPayloadEnvelope
|
||||||
|
|
@ -60,5 +63,8 @@ func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
|
||||||
if dec.Override != nil {
|
if dec.Override != nil {
|
||||||
e.Override = *dec.Override
|
e.Override = *dec.Override
|
||||||
}
|
}
|
||||||
|
if dec.Witness != nil {
|
||||||
|
e.Witness = dec.Witness
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@ type ExecutableData struct {
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
BlobGasUsed *uint64 `json:"blobGasUsed"`
|
||||||
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
ExcessBlobGas *uint64 `json:"excessBlobGas"`
|
||||||
|
Deposits types.Deposits `json:"depositRequests"`
|
||||||
|
ExecutionWitness *types.ExecutionWitness `json:"executionWitness,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSON type overrides for executableData.
|
// JSON type overrides for executableData.
|
||||||
|
|
@ -92,6 +94,14 @@ type executableDataMarshaling struct {
|
||||||
ExcessBlobGas *hexutil.Uint64
|
ExcessBlobGas *hexutil.Uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatelessPayloadStatusV1 is the result of a stateless payload execution.
|
||||||
|
type StatelessPayloadStatusV1 struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
StateRoot common.Hash `json:"stateRoot"`
|
||||||
|
ReceiptsRoot common.Hash `json:"receiptsRoot"`
|
||||||
|
ValidationError *string `json:"validationError"`
|
||||||
|
}
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go
|
//go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go
|
||||||
|
|
||||||
type ExecutionPayloadEnvelope struct {
|
type ExecutionPayloadEnvelope struct {
|
||||||
|
|
@ -99,6 +109,7 @@ type ExecutionPayloadEnvelope struct {
|
||||||
BlockValue *big.Int `json:"blockValue" gencodec:"required"`
|
BlockValue *big.Int `json:"blockValue" gencodec:"required"`
|
||||||
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
BlobsBundle *BlobsBundleV1 `json:"blobsBundle"`
|
||||||
Override bool `json:"shouldOverrideBuilder"`
|
Override bool `json:"shouldOverrideBuilder"`
|
||||||
|
Witness *hexutil.Bytes `json:"witness"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlobsBundleV1 struct {
|
type BlobsBundleV1 struct {
|
||||||
|
|
@ -114,6 +125,7 @@ type executionPayloadEnvelopeMarshaling struct {
|
||||||
|
|
||||||
type PayloadStatusV1 struct {
|
type PayloadStatusV1 struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
|
Witness *hexutil.Bytes `json:"witness"`
|
||||||
LatestValidHash *common.Hash `json:"latestValidHash"`
|
LatestValidHash *common.Hash `json:"latestValidHash"`
|
||||||
ValidationError *string `json:"validationError"`
|
ValidationError *string `json:"validationError"`
|
||||||
}
|
}
|
||||||
|
|
@ -201,6 +213,20 @@ func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
||||||
// Withdrawals value will propagate through the returned block. Empty
|
// Withdrawals value will propagate through the returned block. Empty
|
||||||
// Withdrawals value must be passed via non-nil, length 0 value in data.
|
// Withdrawals value must be passed via non-nil, length 0 value in data.
|
||||||
func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
|
func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, beaconRoot *common.Hash) (*types.Block, error) {
|
||||||
|
block, err := ExecutableDataToBlockNoHash(data, versionedHashes, beaconRoot)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if block.Hash() != data.BlockHash {
|
||||||
|
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", data.BlockHash, block.Hash())
|
||||||
|
}
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecutableDataToBlockNoHash is analogous to ExecutableDataToBlock, but is used
|
||||||
|
// 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) (*types.Block, error) {
|
||||||
txs, err := decodeTransactions(data.Transactions)
|
txs, err := decodeTransactions(data.Transactions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -235,7 +261,19 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b
|
||||||
h := types.DeriveSha(types.Withdrawals(data.Withdrawals), trie.NewStackTrie(nil))
|
h := types.DeriveSha(types.Withdrawals(data.Withdrawals), trie.NewStackTrie(nil))
|
||||||
withdrawalsRoot = &h
|
withdrawalsRoot = &h
|
||||||
}
|
}
|
||||||
|
// Compute requestsHash if any requests are non-nil.
|
||||||
|
var (
|
||||||
|
requestsHash *common.Hash
|
||||||
|
requests types.Requests
|
||||||
|
)
|
||||||
|
if data.Deposits != nil {
|
||||||
|
requests = make(types.Requests, 0)
|
||||||
|
for _, d := range data.Deposits {
|
||||||
|
requests = append(requests, types.NewRequest(d))
|
||||||
|
}
|
||||||
|
h := types.DeriveSha(requests, trie.NewStackTrie(nil))
|
||||||
|
requestsHash = &h
|
||||||
|
}
|
||||||
header := &types.Header{
|
header := &types.Header{
|
||||||
ParentHash: data.ParentHash,
|
ParentHash: data.ParentHash,
|
||||||
UncleHash: types.EmptyUncleHash,
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
|
@ -256,13 +294,12 @@ func ExecutableDataToBlock(data ExecutableData, versionedHashes []common.Hash, b
|
||||||
ExcessBlobGas: data.ExcessBlobGas,
|
ExcessBlobGas: data.ExcessBlobGas,
|
||||||
BlobGasUsed: data.BlobGasUsed,
|
BlobGasUsed: data.BlobGasUsed,
|
||||||
ParentBeaconRoot: beaconRoot,
|
ParentBeaconRoot: beaconRoot,
|
||||||
|
RequestsHash: requestsHash,
|
||||||
}
|
}
|
||||||
block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals})
|
return types.NewBlockWithHeader(header).
|
||||||
if block.Hash() != data.BlockHash {
|
WithBody(types.Body{Transactions: txs, Uncles: nil, Withdrawals: data.Withdrawals, Requests: requests}).
|
||||||
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", data.BlockHash, block.Hash())
|
WithWitness(data.ExecutionWitness),
|
||||||
}
|
nil
|
||||||
|
|
||||||
return block, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
||||||
|
|
@ -286,6 +323,7 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
|
||||||
Withdrawals: block.Withdrawals(),
|
Withdrawals: block.Withdrawals(),
|
||||||
BlobGasUsed: block.BlobGasUsed(),
|
BlobGasUsed: block.BlobGasUsed(),
|
||||||
ExcessBlobGas: block.ExcessBlobGas(),
|
ExcessBlobGas: block.ExcessBlobGas(),
|
||||||
|
ExecutionWitness: block.ExecutionWitness(),
|
||||||
}
|
}
|
||||||
bundle := BlobsBundleV1{
|
bundle := BlobsBundleV1{
|
||||||
Commitments: make([]hexutil.Bytes, 0),
|
Commitments: make([]hexutil.Bytes, 0),
|
||||||
|
|
@ -299,13 +337,30 @@ func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.
|
||||||
bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(sidecar.Proofs[j][:]))
|
bundle.Proofs = append(bundle.Proofs, hexutil.Bytes(sidecar.Proofs[j][:]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
setRequests(block.Requests(), data)
|
||||||
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &bundle, Override: false}
|
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees, BlobsBundle: &bundle, Override: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1
|
// setRequests differentiates the different request types and
|
||||||
type ExecutionPayloadBodyV1 struct {
|
// assigns them to the associated fields in ExecutableData.
|
||||||
|
func setRequests(requests types.Requests, data *ExecutableData) {
|
||||||
|
if requests != nil {
|
||||||
|
// If requests is non-nil, it means deposits are available in block and we
|
||||||
|
// should return an empty slice instead of nil if there are no deposits.
|
||||||
|
data.Deposits = make(types.Deposits, 0)
|
||||||
|
}
|
||||||
|
for _, r := range requests {
|
||||||
|
if d, ok := r.Inner().(*types.Deposit); ok {
|
||||||
|
data.Deposits = append(data.Deposits, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecutionPayloadBody is used in the response to GetPayloadBodiesByHash and GetPayloadBodiesByRange
|
||||||
|
type ExecutionPayloadBody struct {
|
||||||
TransactionData []hexutil.Bytes `json:"transactions"`
|
TransactionData []hexutil.Bytes `json:"transactions"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
Deposits types.Deposits `json:"depositRequests"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client identifiers to support ClientVersionV1.
|
// Client identifiers to support ClientVersionV1.
|
||||||
|
|
|
||||||
0
beacon/light/api/api_server.go
Executable file → Normal file
0
beacon/light/api/api_server.go
Executable file → Normal file
49
beacon/light/api/light_api.go
Executable file → Normal file
49
beacon/light/api/light_api.go
Executable file → Normal file
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -120,8 +122,12 @@ func NewBeaconLightApi(url string, customHeaders map[string]string) *BeaconLight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *BeaconLightApi) httpGet(path string) ([]byte, error) {
|
func (api *BeaconLightApi) httpGet(path string, params url.Values) ([]byte, error) {
|
||||||
req, err := http.NewRequest("GET", api.url+path, nil)
|
uri, err := api.buildURL(path, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("GET", uri, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -145,17 +151,16 @@ func (api *BeaconLightApi) httpGet(path string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *BeaconLightApi) httpGetf(format string, params ...any) ([]byte, error) {
|
|
||||||
return api.httpGet(fmt.Sprintf(format, params...))
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBestUpdatesAndCommittees fetches and validates LightClientUpdate for given
|
// GetBestUpdatesAndCommittees fetches and validates LightClientUpdate for given
|
||||||
// period and full serialized committee for the next period (committee root hash
|
// period and full serialized committee for the next period (committee root hash
|
||||||
// equals update.NextSyncCommitteeRoot).
|
// equals update.NextSyncCommitteeRoot).
|
||||||
// Note that the results are validated but the update signature should be verified
|
// Note that the results are validated but the update signature should be verified
|
||||||
// by the caller as its validity depends on the update chain.
|
// by the caller as its validity depends on the update chain.
|
||||||
func (api *BeaconLightApi) GetBestUpdatesAndCommittees(firstPeriod, count uint64) ([]*types.LightClientUpdate, []*types.SerializedSyncCommittee, error) {
|
func (api *BeaconLightApi) GetBestUpdatesAndCommittees(firstPeriod, count uint64) ([]*types.LightClientUpdate, []*types.SerializedSyncCommittee, error) {
|
||||||
resp, err := api.httpGetf("/eth/v1/beacon/light_client/updates?start_period=%d&count=%d", firstPeriod, count)
|
resp, err := api.httpGet("/eth/v1/beacon/light_client/updates", map[string][]string{
|
||||||
|
"start_period": {strconv.FormatUint(firstPeriod, 10)},
|
||||||
|
"count": {strconv.FormatUint(count, 10)},
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +197,7 @@ func (api *BeaconLightApi) GetBestUpdatesAndCommittees(firstPeriod, count uint64
|
||||||
// See data structure definition here:
|
// See data structure definition here:
|
||||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientoptimisticupdate
|
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientoptimisticupdate
|
||||||
func (api *BeaconLightApi) GetOptimisticUpdate() (types.OptimisticUpdate, error) {
|
func (api *BeaconLightApi) GetOptimisticUpdate() (types.OptimisticUpdate, error) {
|
||||||
resp, err := api.httpGet("/eth/v1/beacon/light_client/optimistic_update")
|
resp, err := api.httpGet("/eth/v1/beacon/light_client/optimistic_update", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.OptimisticUpdate{}, err
|
return types.OptimisticUpdate{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -245,7 +250,7 @@ func decodeOptimisticUpdate(enc []byte) (types.OptimisticUpdate, error) {
|
||||||
// See data structure definition here:
|
// See data structure definition here:
|
||||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientfinalityupdate
|
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#lightclientfinalityupdate
|
||||||
func (api *BeaconLightApi) GetFinalityUpdate() (types.FinalityUpdate, error) {
|
func (api *BeaconLightApi) GetFinalityUpdate() (types.FinalityUpdate, error) {
|
||||||
resp, err := api.httpGet("/eth/v1/beacon/light_client/finality_update")
|
resp, err := api.httpGet("/eth/v1/beacon/light_client/finality_update", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.FinalityUpdate{}, err
|
return types.FinalityUpdate{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -311,7 +316,7 @@ func (api *BeaconLightApi) GetHeader(blockRoot common.Hash) (types.Header, bool,
|
||||||
} else {
|
} else {
|
||||||
blockId = blockRoot.Hex()
|
blockId = blockRoot.Hex()
|
||||||
}
|
}
|
||||||
resp, err := api.httpGetf("/eth/v1/beacon/headers/%s", blockId)
|
resp, err := api.httpGet(fmt.Sprintf("/eth/v1/beacon/headers/%s", blockId), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return types.Header{}, false, false, err
|
return types.Header{}, false, false, err
|
||||||
}
|
}
|
||||||
|
|
@ -342,7 +347,7 @@ func (api *BeaconLightApi) GetHeader(blockRoot common.Hash) (types.Header, bool,
|
||||||
|
|
||||||
// GetCheckpointData fetches and validates bootstrap data belonging to the given checkpoint.
|
// GetCheckpointData fetches and validates bootstrap data belonging to the given checkpoint.
|
||||||
func (api *BeaconLightApi) GetCheckpointData(checkpointHash common.Hash) (*types.BootstrapData, error) {
|
func (api *BeaconLightApi) GetCheckpointData(checkpointHash common.Hash) (*types.BootstrapData, error) {
|
||||||
resp, err := api.httpGetf("/eth/v1/beacon/light_client/bootstrap/0x%x", checkpointHash[:])
|
resp, err := api.httpGet(fmt.Sprintf("/eth/v1/beacon/light_client/bootstrap/0x%x", checkpointHash[:]), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -384,7 +389,7 @@ func (api *BeaconLightApi) GetCheckpointData(checkpointHash common.Hash) (*types
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *BeaconLightApi) GetBeaconBlock(blockRoot common.Hash) (*types.BeaconBlock, error) {
|
func (api *BeaconLightApi) GetBeaconBlock(blockRoot common.Hash) (*types.BeaconBlock, error) {
|
||||||
resp, err := api.httpGetf("/eth/v2/beacon/blocks/0x%x", blockRoot)
|
resp, err := api.httpGet(fmt.Sprintf("/eth/v2/beacon/blocks/0x%x", blockRoot), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -545,9 +550,13 @@ func (api *BeaconLightApi) StartHeadListener(listener HeadEventListener) func()
|
||||||
// established. It can only return nil when the context is canceled.
|
// established. It can only return nil when the context is canceled.
|
||||||
func (api *BeaconLightApi) startEventStream(ctx context.Context, listener *HeadEventListener) *eventsource.Stream {
|
func (api *BeaconLightApi) startEventStream(ctx context.Context, listener *HeadEventListener) *eventsource.Stream {
|
||||||
for retry := true; retry; retry = ctxSleep(ctx, 5*time.Second) {
|
for retry := true; retry; retry = ctxSleep(ctx, 5*time.Second) {
|
||||||
path := "/eth/v1/events?topics=head&topics=light_client_finality_update&topics=light_client_optimistic_update"
|
|
||||||
log.Trace("Sending event subscription request")
|
log.Trace("Sending event subscription request")
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", api.url+path, nil)
|
uri, err := api.buildURL("/eth/v1/events", map[string][]string{"topics": {"head", "light_client_finality_update", "light_client_optimistic_update"}})
|
||||||
|
if err != nil {
|
||||||
|
listener.OnError(fmt.Errorf("error creating event subscription URL: %v", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", uri, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
listener.OnError(fmt.Errorf("error creating event subscription request: %v", err))
|
listener.OnError(fmt.Errorf("error creating event subscription request: %v", err))
|
||||||
continue
|
continue
|
||||||
|
|
@ -576,3 +585,15 @@ func ctxSleep(ctx context.Context, timeout time.Duration) (ok bool) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *BeaconLightApi) buildURL(path string, params url.Values) (string, error) {
|
||||||
|
uri, err := url.Parse(api.url)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
uri = uri.JoinPath(path)
|
||||||
|
if params != nil {
|
||||||
|
uri.RawQuery = params.Encode()
|
||||||
|
}
|
||||||
|
return uri.String(), nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,34 @@ func TestUpdateSyncDifferentHeads(t *testing.T) {
|
||||||
chain.ExpNextSyncPeriod(t, 17)
|
chain.ExpNextSyncPeriod(t, 17)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRangeLock(t *testing.T) {
|
||||||
|
r := make(rangeLock)
|
||||||
|
|
||||||
|
// Lock from 0 to 99.
|
||||||
|
r.lock(0, 100, 1)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
if v, ok := r[uint64(i)]; v <= 0 || !ok {
|
||||||
|
t.Fatalf("integer space: %d not locked", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock from 0 to 99.
|
||||||
|
r.lock(0, 100, -1)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
if v, ok := r[uint64(i)]; v > 0 || ok {
|
||||||
|
t.Fatalf("integer space: %d is locked", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock from 0 to 99 then unlock from 10 to 59.
|
||||||
|
r.lock(0, 100, 1)
|
||||||
|
r.lock(10, 50, -1)
|
||||||
|
first, count := r.firstUnlocked(0, 100)
|
||||||
|
if first != 10 || count != 50 {
|
||||||
|
t.Fatalf("unexpected first: %d or count: %d", first, count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testRespUpdate(request requestWithID) request.Response {
|
func testRespUpdate(request requestWithID) request.Response {
|
||||||
var resp RespUpdates
|
var resp RespUpdates
|
||||||
if request.request == nil {
|
if request.request == nil {
|
||||||
|
|
|
||||||
|
|
@ -5,55 +5,56 @@
|
||||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
||||||
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
||||||
|
|
||||||
# version:golang 1.22.6
|
# version:golang 1.23.1
|
||||||
# https://go.dev/dl/
|
# https://go.dev/dl/
|
||||||
9e48d99d519882579917d8189c17e98c373ce25abaebb98772e2927088992a51 go1.22.6.src.tar.gz
|
6ee44e298379d146a5e5aa6b1c5b5d5f5d0a3365eabdd70741e6e21340ec3b0d go1.23.1.src.tar.gz
|
||||||
eeb0cc42120cbae6d3695dae2e5420fa0e93a5db957db139b55efdb879dd9856 go1.22.6.aix-ppc64.tar.gz
|
f17f2791717c15728ec63213a014e244c35f9c8846fb29f5a1b63d0c0556f756 go1.23.1.aix-ppc64.tar.gz
|
||||||
b47ac340f0b072943fed1f558a26eb260cc23bd21b8af175582e9103141d465b go1.22.6.darwin-amd64.pkg
|
dd9e772686ed908bcff94b6144322d4e2473a7dcd7c696b7e8b6d12f23c887fd go1.23.1.darwin-amd64.pkg
|
||||||
9c3c0124b01b5365f73a1489649f78f971ecf84844ad9ca58fde133096ddb61b go1.22.6.darwin-amd64.tar.gz
|
488d9e4ca3e3ed513ee4edd91bef3a2360c65fa6d6be59cf79640bf840130a58 go1.23.1.darwin-amd64.tar.gz
|
||||||
14d0355ec1c0eeb213a16efa8635fac1f16067ef78a8173abf9a8c7b805e551e go1.22.6.darwin-arm64.pkg
|
be34b488157ec69d94e26e1554558219a2c90789bcb7e3686965a7f9c8cfcbe7 go1.23.1.darwin-arm64.pkg
|
||||||
ebac39fd44fc22feed1bb519af431c84c55776e39b30f4fd62930da9c0cfd1e3 go1.22.6.darwin-arm64.tar.gz
|
e223795ca340e285a760a6446ce57a74500b30e57469a4109961d36184d3c05a go1.23.1.darwin-arm64.tar.gz
|
||||||
3695b10c722a4920c8a736284f8820c142e1e752f3a87f797a45c64366f7a173 go1.22.6.dragonfly-amd64.tar.gz
|
6af626176923a6ae6c5de6dc1c864f38365793c0e4ecd0d6eab847bdc23953e5 go1.23.1.dragonfly-amd64.tar.gz
|
||||||
a9b9570c80294a664d50b566d6bd1aa42465997d2d76a57936b32f55f5c69c63 go1.22.6.freebsd-386.tar.gz
|
cc957c1a019702e6cdc2e257202d42799011ebc1968b6c3bcd6b1965952607d5 go1.23.1.freebsd-386.tar.gz
|
||||||
424a5618406800365fe3ad96a795fb55ce394bea3ff48eaf56d292bf7a916d1e go1.22.6.freebsd-amd64.tar.gz
|
a7d57781c50bb80886a8f04066791956d45aa3eea0f83070c5268b6223afb2ff go1.23.1.freebsd-amd64.tar.gz
|
||||||
e0dce3a6dbe8e7e054d329dd4cb403935c63c0f7e22e693077aa60e12018b883 go1.22.6.freebsd-arm.tar.gz
|
c7b09f3fef456048e596db9bea746eb66796aeb82885622b0388feee18f36a3e go1.23.1.freebsd-arm.tar.gz
|
||||||
34930b01f58889c71f7a78c51c6c3bd2ce289ac7862c76dab691303cfa935fd1 go1.22.6.freebsd-arm64.tar.gz
|
b05cd6a77995a0c8439d88df124811c725fb78b942d0b6dd1643529d7ba62f1f go1.23.1.freebsd-arm64.tar.gz
|
||||||
4c9d630e55d4d600a5b4297e59620c3bdfe63a441981682b3638e2fdda228a44 go1.22.6.freebsd-riscv64.tar.gz
|
56236ae70be1613f2915943b94f53c96be5bffc0719314078facd778a89bc57e go1.23.1.freebsd-riscv64.tar.gz
|
||||||
9ed63feaf2ef56c56f1cf0d9d3fab4006efd22a38e2f1f5252e95c6ac09332f3 go1.22.6.illumos-amd64.tar.gz
|
8644c52df4e831202114fd67c9fcaf1f7233ad27bf945ac53fa7217cf1a0349f go1.23.1.illumos-amd64.tar.gz
|
||||||
9e680027b058beab10ce5938607660964b6d2c564bf50bdb01aa090dc5beda98 go1.22.6.linux-386.tar.gz
|
cdee2f4e2efa001f7ee75c90f2efc310b63346cfbba7b549987e9139527c6b17 go1.23.1.linux-386.tar.gz
|
||||||
999805bed7d9039ec3da1a53bfbcafc13e367da52aa823cb60b68ba22d44c616 go1.22.6.linux-amd64.tar.gz
|
49bbb517cfa9eee677e1e7897f7cf9cfdbcf49e05f61984a2789136de359f9bd go1.23.1.linux-amd64.tar.gz
|
||||||
c15fa895341b8eaf7f219fada25c36a610eb042985dc1a912410c1c90098eaf2 go1.22.6.linux-arm64.tar.gz
|
faec7f7f8ae53fda0f3d408f52182d942cc89ef5b7d3d9f23ff117437d4b2d2f go1.23.1.linux-arm64.tar.gz
|
||||||
b566484fe89a54c525dd1a4cbfec903c1f6e8f0b7b3dbaf94c79bc9145391083 go1.22.6.linux-armv6l.tar.gz
|
6c7832c7dcd8fb6d4eb308f672a725393403c74ee7be1aeccd8a443015df99de go1.23.1.linux-armv6l.tar.gz
|
||||||
1ee6e1896aea856142d2af7045cea118995b39404aa61afd12677d023d47ee69 go1.22.6.linux-loong64.tar.gz
|
649ce3856ddc808c00b14a46232eab0bf95e7911cdf497010b17d76656f5ca4e go1.23.1.linux-loong64.tar.gz
|
||||||
fdd0e1a3e178f9bc79adf6ff1e3de4554ce581b4c468fd6e113c43fbbbe1eec6 go1.22.6.linux-mips.tar.gz
|
201911048f234e5a0c51ec94b1a11d4e47062fee4398b1d2faa6c820dc026724 go1.23.1.linux-mips.tar.gz
|
||||||
d3e5a621fc5a07759e503a971af0b28ded6a7d6f5604ab511f51f930a18dd3e4 go1.22.6.linux-mips64.tar.gz
|
2bce3743df463915e45d2612f9476ffb03d0b3750b1cb3879347de08715b5fc6 go1.23.1.linux-mips64.tar.gz
|
||||||
01547606c5b5c1b0e5587b3afd65172860d2f4755e523785832905759ecce2d7 go1.22.6.linux-mips64le.tar.gz
|
54e301f266e33431b0703136e0bbd4cf02461b1ecedd37b7cbd90cb862a98e5f go1.23.1.linux-mips64le.tar.gz
|
||||||
2cd771416ae03c11240cfdb551d66ab9a941077664f3727b966f94386c23b0fa go1.22.6.linux-mipsle.tar.gz
|
8efd495e93d17408c0803595cdc3bf13cb28e0f957aeabd9cc18245fb8e64019 go1.23.1.linux-mipsle.tar.gz
|
||||||
6ef61d517777925e6bdb0321ea42d5f60acc20c1314dd902b9d0bfa3a5fd4fca go1.22.6.linux-ppc64.tar.gz
|
52bd68689095831ad9af7160844c23b28bb8d0acd268de7e300ff5f0662b7a07 go1.23.1.linux-ppc64.tar.gz
|
||||||
9d99fce3f6f72a76630fe91ec0884dfe3db828def4713368424900fa98bb2bd6 go1.22.6.linux-ppc64le.tar.gz
|
042888cae54b5fbfd9dd1e3b6bc4a5134879777fe6497fc4c62ec394b5ecf2da go1.23.1.linux-ppc64le.tar.gz
|
||||||
30be9c9b9cc4f044d4da9a33ee601ab7b3aff4246107d323a79e08888710754e go1.22.6.linux-riscv64.tar.gz
|
1a4a609f0391bea202d9095453cbfaf7368fa88a04c206bf9dd715a738664dc3 go1.23.1.linux-riscv64.tar.gz
|
||||||
82f3bae3ddb4ede45b848db48c5486fadb58551e74507bda45484257e7194a95 go1.22.6.linux-s390x.tar.gz
|
47dc49ad45c45e192efa0df7dc7bc5403f5f2d15b5d0dc74ef3018154b616f4d go1.23.1.linux-s390x.tar.gz
|
||||||
85b2eb9d40a930bd3e75d0096a6eb5847aac86c5085e6d13a5845e9ef03f8d4b go1.22.6.netbsd-386.tar.gz
|
fbfbd5efa6a5d581ea7f5e65015f927db0e52135cab057e43d39d5482da54b61 go1.23.1.netbsd-386.tar.gz
|
||||||
6e9acbdc34fb2a942d547c47c9c1989bb6e32b4a37d57fb312499e2bb33b46b7 go1.22.6.netbsd-amd64.tar.gz
|
e96e1cc5cf36113ee6099d1a7306b22cd9c3f975a36bdff954c59f104f22b853 go1.23.1.netbsd-amd64.tar.gz
|
||||||
e6eff3cf0038f2a9b0c9e01e228577a783bddcd8051222a3d949e24ee392e769 go1.22.6.netbsd-arm.tar.gz
|
c394dfc06bfc276a591209a37e09cd39089ec9a9cc3db30b94814ce2e39eb1d4 go1.23.1.netbsd-arm.tar.gz
|
||||||
43a7e2ba22da700b844f7561e3dd5434540ed6c9781be2e9c42e8a8cbf558f8e go1.22.6.netbsd-arm64.tar.gz
|
b3b35d64f32821a68b3e2994032dbefb81978f2ec3f218c7a770623b82d36b8e go1.23.1.netbsd-arm64.tar.gz
|
||||||
a90b758ccb45d8a17af8e140fafa1e97607de5a7ecd53a4c55f69258bfb043d0 go1.22.6.openbsd-386.tar.gz
|
3c775c4c16c182e33c2c4ac090d9a247a93b3fb18a3df01d87d490f29599faff go1.23.1.openbsd-386.tar.gz
|
||||||
cc13436c4a644e55bedcea65981eb80ca8317b39b129f5563ab3b6da1391bd47 go1.22.6.openbsd-amd64.tar.gz
|
5edbe53b47c57b32707fd7154536fbe9eaa79053fea01650c93b54cdba13fc0f go1.23.1.openbsd-amd64.tar.gz
|
||||||
aee34f61ba2b0a8f2618f5c7065e20da7714ce7651680509eda30728fe01ee88 go1.22.6.openbsd-arm.tar.gz
|
c30903dd8fa98b8aca8e9db0962ce9f55502aed93e0ef41e5ae148aaa0088de1 go1.23.1.openbsd-arm.tar.gz
|
||||||
c67d57daf8baada93c69c8fb02401270cd33159730b1f2d70d9e724ba1a918cf go1.22.6.openbsd-arm64.tar.gz
|
12da183489e58f9c6b357bc1b626f85ed7d4220cab31a49d6a49e6ac6a718b67 go1.23.1.openbsd-arm64.tar.gz
|
||||||
03e1f96002e94a6b381bcf66a0a62b9d5f63148682a780d727840ad540185c7c go1.22.6.openbsd-ppc64.tar.gz
|
9cc9aad37696a4a10c31dcec9e35a308de0b369dad354d54cf07406ac6fa7c6f go1.23.1.openbsd-ppc64.tar.gz
|
||||||
0ac2b5bbe2c8a293d284512630e629bf0578aaa7b7b1f39ac4ee182c7924aaad go1.22.6.plan9-386.tar.gz
|
e1d740dda062ce5a276a0c3ed7d8b6353238bc8ff405f63e2e3480bfd26a5ec5 go1.23.1.openbsd-riscv64.tar.gz
|
||||||
f9afdab8a72a8d874f023f5605482cc94160843ac768dbd840e6f772d16578c7 go1.22.6.plan9-amd64.tar.gz
|
da2a37f9987f01f096859230aa13ecc4ad2e7884465bce91004bc78c64435d65 go1.23.1.plan9-386.tar.gz
|
||||||
4b9f01a47e6a29d57cbb3097b6770583336cef9c8f0d51d3d1451e42a851002e go1.22.6.plan9-arm.tar.gz
|
fd8fff8b0697d55c4a4d02a8dc998192b80a9dc2a057647373d6ff607cad29de go1.23.1.plan9-amd64.tar.gz
|
||||||
46c2552ac7b8d6314a52e14e0a0761aaeebdd6aba5f531de386f4cf2b66ec723 go1.22.6.solaris-amd64.tar.gz
|
52efbc5804c1c86ba7868aa8ebbc31cc8c2a27b62a60fd57944970d48fc67525 go1.23.1.plan9-arm.tar.gz
|
||||||
a57821dab76af1ef7a6b62db1628f0caa74343e0c7cb829df9ce8ea0713a3e8e go1.22.6.windows-386.msi
|
f54205f21e2143f2ada1bf1c00ddf64590f5139d5c3fb77cc06175f0d8cc7567 go1.23.1.solaris-amd64.tar.gz
|
||||||
eb734bacc9aabca1273b61dd392bb84a9bb33783f5e2fff2cd6ab9885bbefbe6 go1.22.6.windows-386.zip
|
369a17f0cfd29e5c848e58ffe0d772da20abe334d1c7ca01dbcd55bb3db0b440 go1.23.1.windows-386.msi
|
||||||
1238a3e6892eb8a0eb3fe0640e18ab82ca21cc1a933f16897b2ad081f057b5da go1.22.6.windows-amd64.msi
|
ab866f47d7be56e6b1c67f1d529bf4c23331a339fb0785f435a0552d352cb257 go1.23.1.windows-386.zip
|
||||||
6023083a6e4d3199b44c37e9ba7b25d9674da20fd846a35ee5f9589d81c21a6a go1.22.6.windows-amd64.zip
|
e99dac215ee437b9bb8f8b14bbfe0e8756882c1ed291f30818e8363bc9c047a5 go1.23.1.windows-amd64.msi
|
||||||
6791218c568a3d000cb36317506541d7fd67e7cfe613baaf361ca36cad5e2cd5 go1.22.6.windows-arm.msi
|
32dedf277c86610e380e1765593edb66876f00223df71690bd6be68ee17675c0 go1.23.1.windows-amd64.zip
|
||||||
ee41ca83bb07c4fd46a1d6b2d083519bb8ca156fcd9db37ee711234d43126e2f go1.22.6.windows-arm.zip
|
23169c79dc6b54e0dffb25be6b67425ad9759392a58309bc057430a9bf4c8f6a go1.23.1.windows-arm.msi
|
||||||
91c6b3376612095315a0aeae4b03e3da34fabe9dfd4532d023e2a70f913cf22a go1.22.6.windows-arm64.msi
|
1a57615a09f13534f88e9f2d7efd5743535d1a5719b19e520eef965a634f8efb go1.23.1.windows-arm.zip
|
||||||
7cf55f357ba8116cd3bff992980e20a704ba451b3dab341cf1787b133d900512 go1.22.6.windows-arm64.zip
|
313e1a543931ad8735b4df8969e00f5f4c2ef07be21f54015ede961a70263d35 go1.23.1.windows-arm64.msi
|
||||||
|
64ad0954d2c33f556fb1018d62de091254aa6e3a94f1c8a8b16af0d3701d194e go1.23.1.windows-arm64.zip
|
||||||
|
|
||||||
# version:golangci 1.59.0
|
# version:golangci 1.59.0
|
||||||
# https://github.com/golangci/golangci-lint/releases/
|
# https://github.com/golangci/golangci-lint/releases/
|
||||||
|
|
|
||||||
82
build/ci.go
82
build/ci.go
|
|
@ -126,8 +126,6 @@ var (
|
||||||
"focal", // 20.04, EOL: 04/2030
|
"focal", // 20.04, EOL: 04/2030
|
||||||
"jammy", // 22.04, EOL: 04/2032
|
"jammy", // 22.04, EOL: 04/2032
|
||||||
"noble", // 24.04, EOL: 04/2034
|
"noble", // 24.04, EOL: 04/2034
|
||||||
|
|
||||||
"mantic", // 23.10, EOL: 07/2024
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is where the tests should be unpacked.
|
// This is where the tests should be unpacked.
|
||||||
|
|
@ -239,6 +237,10 @@ func doInstall(cmdline []string) {
|
||||||
// buildFlags returns the go tool flags for building.
|
// buildFlags returns the go tool flags for building.
|
||||||
func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (flags []string) {
|
func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (flags []string) {
|
||||||
var ld []string
|
var ld []string
|
||||||
|
// See https://github.com/golang/go/issues/33772#issuecomment-528176001
|
||||||
|
// We need to set --buildid to the linker here, and also pass --build-id to the
|
||||||
|
// cgo-linker further down.
|
||||||
|
ld = append(ld, "--buildid=none")
|
||||||
if env.Commit != "" {
|
if env.Commit != "" {
|
||||||
ld = append(ld, "-X", "github.com/maticnetwork/bor/internal/version.gitCommit="+env.Commit)
|
ld = append(ld, "-X", "github.com/maticnetwork/bor/internal/version.gitCommit="+env.Commit)
|
||||||
ld = append(ld, "-X", "github.com/maticnetwork/bor/internal/version.gitDate="+env.Date)
|
ld = append(ld, "-X", "github.com/maticnetwork/bor/internal/version.gitDate="+env.Date)
|
||||||
|
|
@ -251,7 +253,11 @@ func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
// Enforce the stacksize to 8M, which is the case on most platforms apart from
|
// Enforce the stacksize to 8M, which is the case on most platforms apart from
|
||||||
// alpine Linux.
|
// alpine Linux.
|
||||||
extld := []string{"-Wl,-z,stack-size=0x800000"}
|
// See https://sourceware.org/binutils/docs-2.23.1/ld/Options.html#Options
|
||||||
|
// regarding the options --build-id=none and --strip-all. It is needed for
|
||||||
|
// reproducible builds; removing references to temporary files in C-land, and
|
||||||
|
// making build-id reproducably absent.
|
||||||
|
extld := []string{"-Wl,-z,stack-size=0x800000,--build-id=none,--strip-all"}
|
||||||
if staticLinking {
|
if staticLinking {
|
||||||
extld = append(extld, "-static")
|
extld = append(extld, "-static")
|
||||||
// Under static linking, use of certain glibc features must be
|
// Under static linking, use of certain glibc features must be
|
||||||
|
|
@ -298,7 +304,7 @@ func doTest(cmdline []string) {
|
||||||
gotest := tc.Go("test")
|
gotest := tc.Go("test")
|
||||||
|
|
||||||
// CI needs a bit more time for the statetests (default 10m).
|
// CI needs a bit more time for the statetests (default 10m).
|
||||||
gotest.Args = append(gotest.Args, "-timeout=20m")
|
gotest.Args = append(gotest.Args, "-timeout=30m")
|
||||||
|
|
||||||
// Enable CKZG backend in CI.
|
// Enable CKZG backend in CI.
|
||||||
gotest.Args = append(gotest.Args, "-tags=ckzg")
|
gotest.Args = append(gotest.Args, "-tags=ckzg")
|
||||||
|
|
@ -349,10 +355,10 @@ func downloadSpecTestFixtures(csdb *build.ChecksumDB, cachedir string) string {
|
||||||
return filepath.Join(cachedir, base)
|
return filepath.Join(cachedir, base)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hashSourceFiles iterates all files under the top-level project directory
|
// hashAllSourceFiles iterates all files under the top-level project directory
|
||||||
// computing the hash of each file (excluding files within the tests
|
// computing the hash of each file (excluding files within the tests
|
||||||
// subrepo)
|
// subrepo)
|
||||||
func hashSourceFiles() (map[string]common.Hash, error) {
|
func hashAllSourceFiles() (map[string]common.Hash, error) {
|
||||||
res := make(map[string]common.Hash)
|
res := make(map[string]common.Hash)
|
||||||
err := filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error {
|
err := filepath.WalkDir(".", func(path string, d os.DirEntry, err error) error {
|
||||||
if strings.HasPrefix(path, filepath.FromSlash("tests/testdata")) {
|
if strings.HasPrefix(path, filepath.FromSlash("tests/testdata")) {
|
||||||
|
|
@ -379,6 +385,56 @@ func hashSourceFiles() (map[string]common.Hash, error) {
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hashSourceFiles iterates the provided set of filepaths (relative to the top-level geth project directory)
|
||||||
|
// computing the hash of each file.
|
||||||
|
func hashSourceFiles(files []string) (map[string]common.Hash, error) {
|
||||||
|
res := make(map[string]common.Hash)
|
||||||
|
for _, filePath := range files {
|
||||||
|
f, err := os.OpenFile(filePath, os.O_RDONLY, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hasher := sha256.New()
|
||||||
|
if _, err := io.Copy(hasher, f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res[filePath] = common.Hash(hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compareHashedFilesets compares two maps (key is relative file path to top-level geth directory, value is its hash)
|
||||||
|
// and returns the list of file paths whose hashes differed.
|
||||||
|
func compareHashedFilesets(preHashes map[string]common.Hash, postHashes map[string]common.Hash) []string {
|
||||||
|
updates := []string{}
|
||||||
|
for path, postHash := range postHashes {
|
||||||
|
preHash, ok := preHashes[path]
|
||||||
|
if !ok || preHash != postHash {
|
||||||
|
updates = append(updates, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updates
|
||||||
|
}
|
||||||
|
|
||||||
|
func doGoModTidy() {
|
||||||
|
targetFiles := []string{"go.mod", "go.sum"}
|
||||||
|
preHashes, err := hashSourceFiles(targetFiles)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("failed to hash go.mod/go.sum", "err", err)
|
||||||
|
}
|
||||||
|
tc := new(build.GoToolchain)
|
||||||
|
c := tc.Go("mod", "tidy")
|
||||||
|
build.MustRun(c)
|
||||||
|
postHashes, err := hashSourceFiles(targetFiles)
|
||||||
|
updates := compareHashedFilesets(preHashes, postHashes)
|
||||||
|
for _, updatedFile := range updates {
|
||||||
|
fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile)
|
||||||
|
}
|
||||||
|
if len(updates) != 0 {
|
||||||
|
log.Fatal("go.sum and/or go.mod were updated by running 'go mod tidy'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// doGenerate ensures that re-generating generated files does not cause
|
// doGenerate ensures that re-generating generated files does not cause
|
||||||
// any mutations in the source file tree: i.e. all generated files were
|
// any mutations in the source file tree: i.e. all generated files were
|
||||||
// updated and committed. Any stale generated files are updated.
|
// updated and committed. Any stale generated files are updated.
|
||||||
|
|
@ -395,7 +451,7 @@ func doGenerate() {
|
||||||
var preHashes map[string]common.Hash
|
var preHashes map[string]common.Hash
|
||||||
if *verify {
|
if *verify {
|
||||||
var err error
|
var err error
|
||||||
preHashes, err = hashSourceFiles()
|
preHashes, err = hashAllSourceFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("failed to compute map of source hashes", "err", err)
|
log.Fatal("failed to compute map of source hashes", "err", err)
|
||||||
}
|
}
|
||||||
|
|
@ -410,17 +466,11 @@ func doGenerate() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Check if files were changed.
|
// Check if files were changed.
|
||||||
postHashes, err := hashSourceFiles()
|
postHashes, err := hashAllSourceFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("error computing source tree file hashes", "err", err)
|
log.Fatal("error computing source tree file hashes", "err", err)
|
||||||
}
|
}
|
||||||
updates := []string{}
|
updates := compareHashedFilesets(preHashes, postHashes)
|
||||||
for path, postHash := range postHashes {
|
|
||||||
preHash, ok := preHashes[path]
|
|
||||||
if !ok || preHash != postHash {
|
|
||||||
updates = append(updates, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, updatedFile := range updates {
|
for _, updatedFile := range updates {
|
||||||
fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile)
|
fmt.Fprintf(os.Stderr, "changed file %s\n", updatedFile)
|
||||||
}
|
}
|
||||||
|
|
@ -443,6 +493,8 @@ func doLint(cmdline []string) {
|
||||||
linter := downloadLinter(*cachedir)
|
linter := downloadLinter(*cachedir)
|
||||||
lflags := []string{"run", "--config", ".golangci.yml"}
|
lflags := []string{"run", "--config", ".golangci.yml"}
|
||||||
build.MustRunCommandWithOutput(linter, append(lflags, packages...)...)
|
build.MustRunCommandWithOutput(linter, append(lflags, packages...)...)
|
||||||
|
|
||||||
|
doGoModTidy()
|
||||||
fmt.Println("You have achieved perfection.")
|
fmt.Println("You have achieved perfection.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
0
build/deb/ethereum/completions/bash/geth
Executable file → Normal file
0
build/deb/ethereum/completions/bash/geth
Executable file → Normal file
0
build/goimports.sh
Executable file → Normal file
0
build/goimports.sh
Executable file → Normal file
0
build/travis_keepalive.sh
Executable file → Normal file
0
build/travis_keepalive.sh
Executable file → Normal file
|
|
@ -8,7 +8,8 @@ chain = "mainnet"
|
||||||
# vmdebug = false
|
# vmdebug = false
|
||||||
datadir = "/var/lib/bor/data"
|
datadir = "/var/lib/bor/data"
|
||||||
# ancient = ""
|
# ancient = ""
|
||||||
# "db.engine" = "leveldb"
|
# "db.engine" = "pebble"
|
||||||
|
# "state.scheme" = "path"
|
||||||
# keystore = "/var/lib/bor/keystore"
|
# keystore = "/var/lib/bor/keystore"
|
||||||
# "rpc.batchlimit" = 100
|
# "rpc.batchlimit" = 100
|
||||||
# "rpc.returndatalimit" = 100000
|
# "rpc.returndatalimit" = 100000
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ func main() {
|
||||||
//TODO datadir for optional permanent database
|
//TODO datadir for optional permanent database
|
||||||
utils.MainnetFlag,
|
utils.MainnetFlag,
|
||||||
utils.SepoliaFlag,
|
utils.SepoliaFlag,
|
||||||
utils.GoerliFlag,
|
|
||||||
utils.BlsyncApiFlag,
|
utils.BlsyncApiFlag,
|
||||||
utils.BlsyncJWTSecretFlag,
|
utils.BlsyncJWTSecretFlag,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ GLOBAL OPTIONS:
|
||||||
--loglevel value log level to emit to the screen (default: 4)
|
--loglevel value log level to emit to the screen (default: 4)
|
||||||
--keystore value Directory for the keystore (default: "$HOME/.ethereum/keystore")
|
--keystore value Directory for the keystore (default: "$HOME/.ethereum/keystore")
|
||||||
--configdir value Directory for Clef configuration (default: "$HOME/.clef")
|
--configdir value Directory for Clef configuration (default: "$HOME/.clef")
|
||||||
--chainid value Chain id to use for signing (1=mainnet, 5=Goerli) (default: 1)
|
--chainid value Chain id to use for signing (1=mainnet, 17000=Holesky) (default: 1)
|
||||||
--lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength
|
--lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength
|
||||||
--nousb Disables monitoring for and managing USB hardware wallets
|
--nousb Disables monitoring for and managing USB hardware wallets
|
||||||
--pcscdpath value Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm")
|
--pcscdpath value Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm")
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ var (
|
||||||
chainIdFlag = &cli.Int64Flag{
|
chainIdFlag = &cli.Int64Flag{
|
||||||
Name: "chainid",
|
Name: "chainid",
|
||||||
Value: params.MainnetChainConfig.ChainID.Int64(),
|
Value: params.MainnetChainConfig.ChainID.Int64(),
|
||||||
Usage: "Chain id to use for signing (1=mainnet, 5=Goerli)",
|
Usage: "Chain id to use for signing (1=mainnet, 17000=Holesky)",
|
||||||
}
|
}
|
||||||
rpcPortFlag = &cli.IntFlag{
|
rpcPortFlag = &cli.IntFlag{
|
||||||
Name: "http.port",
|
Name: "http.port",
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ set to standard output. The following filters are supported:
|
||||||
- `-limit <N>` limits the output set to N entries, taking the top N nodes by score
|
- `-limit <N>` limits the output set to N entries, taking the top N nodes by score
|
||||||
- `-ip <CIDR>` filters nodes by IP subnet
|
- `-ip <CIDR>` filters nodes by IP subnet
|
||||||
- `-min-age <duration>` filters nodes by 'first seen' time
|
- `-min-age <duration>` filters nodes by 'first seen' time
|
||||||
- `-eth-network <mainnet/goerli/sepolia/holesky>` filters nodes by "eth" ENR entry
|
- `-eth-network <mainnet/sepolia/holesky>` filters nodes by "eth" ENR entry
|
||||||
- `-les-server` filters nodes by LES server support
|
- `-les-server` filters nodes by LES server support
|
||||||
- `-snap` filters nodes by snap protocol support
|
- `-snap` filters nodes by snap protocol support
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,8 +98,8 @@ func (c *cloudflareClient) checkZone(name string) error {
|
||||||
if !strings.HasSuffix(name, "."+zone.Name) {
|
if !strings.HasSuffix(name, "."+zone.Name) {
|
||||||
return fmt.Errorf("CloudFlare zone name %q does not match name %q to be deployed", zone.Name, name)
|
return fmt.Errorf("CloudFlare zone name %q does not match name %q to be deployed", zone.Name, name)
|
||||||
}
|
}
|
||||||
|
// Necessary permissions for Cloudlare management - Zone:Read, DNS:Read, Zone:Edit, DNS:Edit
|
||||||
needPerms := map[string]bool{"#zone:edit": false, "#zone:read": false}
|
needPerms := map[string]bool{"#zone:edit": false, "#zone:read": false, "#dns_records:read": false, "#dns_records:edit": false}
|
||||||
for _, perm := range zone.Permissions {
|
for _, perm := range zone.Permissions {
|
||||||
if _, ok := needPerms[perm]; ok {
|
if _, ok := needPerms[perm]; ok {
|
||||||
needPerms[perm] = true
|
needPerms[perm] = true
|
||||||
|
|
|
||||||
0
cmd/devp2p/internal/ethtest/testdata/chain.rlp.gz
vendored
Executable file → Normal file
0
cmd/devp2p/internal/ethtest/testdata/chain.rlp.gz
vendored
Executable file → Normal file
|
|
@ -259,8 +259,6 @@ func ethFilter(args []string) (nodeFilter, error) {
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "mainnet":
|
case "mainnet":
|
||||||
filter = forkid.NewStaticFilter(params.MainnetChainConfig, core.DefaultGenesisBlock().ToBlock())
|
filter = forkid.NewStaticFilter(params.MainnetChainConfig, core.DefaultGenesisBlock().ToBlock())
|
||||||
case "goerli":
|
|
||||||
filter = forkid.NewStaticFilter(params.GoerliChainConfig, core.DefaultGoerliGenesisBlock().ToBlock())
|
|
||||||
case "sepolia":
|
case "sepolia":
|
||||||
filter = forkid.NewStaticFilter(params.SepoliaChainConfig, core.DefaultSepoliaGenesisBlock().ToBlock())
|
filter = forkid.NewStaticFilter(params.SepoliaChainConfig, core.DefaultSepoliaGenesisBlock().ToBlock())
|
||||||
case "bor-mumbai":
|
case "bor-mumbai":
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,8 @@ type ExecutionResult struct {
|
||||||
WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"`
|
WithdrawalsRoot *common.Hash `json:"withdrawalsRoot,omitempty"`
|
||||||
CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"`
|
CurrentExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas,omitempty"`
|
||||||
CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"`
|
CurrentBlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed,omitempty"`
|
||||||
|
RequestsHash *common.Hash `json:"requestsRoot,omitempty"`
|
||||||
|
DepositRequests *types.Deposits `json:"depositRequests,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ommer struct {
|
type ommer struct {
|
||||||
|
|
@ -201,7 +203,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
|
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
|
||||||
}
|
}
|
||||||
|
if pre.Env.BlockHashes != nil && chainConfig.IsPrague(new(big.Int).SetUint64(pre.Env.Number)) {
|
||||||
|
var (
|
||||||
|
prevNumber = pre.Env.Number - 1
|
||||||
|
prevHash = pre.Env.BlockHashes[math.HexOrDecimal64(prevNumber)]
|
||||||
|
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
||||||
|
)
|
||||||
|
core.ProcessParentBlockHash(prevHash, evm, statedb)
|
||||||
|
}
|
||||||
for i := 0; txIt.Next(); i++ {
|
for i := 0; txIt.Next(); i++ {
|
||||||
tx, err := txIt.Tx()
|
tx, err := txIt.Tx()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -253,7 +262,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
}
|
}
|
||||||
// (ret []byte, usedGas uint64, failed bool, err error)
|
// (ret []byte, usedGas uint64, failed bool, err error)
|
||||||
msgResult, err := core.ApplyMessage(evm, msg, gaspool, nil)
|
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
statedb.RevertToSnapshot(snapshot)
|
statedb.RevertToSnapshot(snapshot)
|
||||||
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
log.Info("rejected tx", "index", i, "hash", tx.Hash(), "from", msg.From, "error", err)
|
||||||
|
|
@ -385,9 +394,31 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(&excessBlobGas)
|
execRs.CurrentExcessBlobGas = (*math.HexOrDecimal64)(&excessBlobGas)
|
||||||
execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed)
|
execRs.CurrentBlobGasUsed = (*math.HexOrDecimal64)(&blobGasUsed)
|
||||||
}
|
}
|
||||||
|
if chainConfig.IsPrague(vmContext.BlockNumber) {
|
||||||
|
// Parse the requests from the logs
|
||||||
|
var allLogs []*types.Log
|
||||||
|
for _, receipt := range receipts {
|
||||||
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
|
}
|
||||||
|
requests, err := core.ParseDepositLogs(allLogs, chainConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
|
||||||
|
}
|
||||||
|
// Calculate the requests root
|
||||||
|
h := types.DeriveSha(requests, trie.NewStackTrie(nil))
|
||||||
|
execRs.RequestsHash = &h
|
||||||
|
// Get the deposits from the requests
|
||||||
|
deposits := make(types.Deposits, 0)
|
||||||
|
for _, req := range requests {
|
||||||
|
if dep, ok := req.Inner().(*types.Deposit); ok {
|
||||||
|
deposits = append(deposits, dep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
execRs.DepositRequests = &deposits
|
||||||
|
}
|
||||||
// Re-create statedb instance with new root upon the updated database
|
// Re-create statedb instance with new root upon the updated database
|
||||||
// for accessing latest states.
|
// for accessing latest states.
|
||||||
statedb, err = state.New(root, statedb.Database(), nil)
|
statedb, err = state.New(root, statedb.Database())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not reopen state: %v", err))
|
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not reopen state: %v", err))
|
||||||
}
|
}
|
||||||
|
|
@ -396,8 +427,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB {
|
func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB {
|
||||||
sdb := state.NewDatabaseWithConfig(db, &triedb.Config{Preimages: true})
|
tdb := triedb.NewDatabase(db, &triedb.Config{Preimages: true})
|
||||||
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
|
sdb := state.NewDatabase(tdb, nil)
|
||||||
|
statedb, _ := state.New(types.EmptyRootHash, sdb)
|
||||||
for addr, a := range accounts {
|
for addr, a := range accounts {
|
||||||
statedb.SetCode(addr, a.Code)
|
statedb.SetCode(addr, a.Code)
|
||||||
statedb.SetNonce(addr, a.Nonce)
|
statedb.SetNonce(addr, a.Nonce)
|
||||||
|
|
@ -408,8 +440,7 @@ func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB
|
||||||
}
|
}
|
||||||
// Commit and re-open to start with a clean state.
|
// Commit and re-open to start with a clean state.
|
||||||
root, _ := statedb.Commit(0, false)
|
root, _ := statedb.Commit(0, false)
|
||||||
statedb, _ = state.New(root, sdb, nil)
|
statedb, _ = state.New(root, sdb)
|
||||||
|
|
||||||
return statedb
|
return statedb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -163,8 +163,8 @@ func runCmd(ctx *cli.Context) error {
|
||||||
})
|
})
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
genesis := genesisConfig.MustCommit(db, triedb)
|
genesis := genesisConfig.MustCommit(db, triedb)
|
||||||
sdb := state.NewDatabaseWithNodeDB(db, triedb)
|
sdb := state.NewDatabase(triedb, nil)
|
||||||
statedb, _ = state.New(genesis.Root(), sdb, nil)
|
statedb, _ = state.New(genesis.Root(), sdb)
|
||||||
chainConfig = genesisConfig.Config
|
chainConfig = genesisConfig.Config
|
||||||
|
|
||||||
if ctx.String(SenderFlag.Name) != "" {
|
if ctx.String(SenderFlag.Name) != "" {
|
||||||
|
|
@ -297,7 +297,7 @@ func runCmd(ctx *cli.Context) error {
|
||||||
fmt.Printf("Failed to commit changes %v\n", err)
|
fmt.Printf("Failed to commit changes %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
dumpdb, err := state.New(root, sdb, nil)
|
dumpdb, err := state.New(root, sdb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to open statedb %v\n", err)
|
fmt.Printf("Failed to open statedb %v\n", err)
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ func runStateTest(fname string, cfg vm.Config, dump bool) error {
|
||||||
result.Root = &root
|
result.Root = &root
|
||||||
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
|
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
|
||||||
if dump { // Dump any state to aid debugging
|
if dump { // Dump any state to aid debugging
|
||||||
cpy, _ := state.New(root, tstate.StateDB.Database(), nil)
|
cpy, _ := state.New(root, tstate.StateDB.Database())
|
||||||
dump := cpy.RawDump(nil)
|
dump := cpy.RawDump(nil)
|
||||||
result.State = &dump
|
result.State = &dump
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -451,8 +451,6 @@ func importHistory(ctx *cli.Context) error {
|
||||||
network = "mainnet"
|
network = "mainnet"
|
||||||
case ctx.Bool(utils.SepoliaFlag.Name):
|
case ctx.Bool(utils.SepoliaFlag.Name):
|
||||||
network = "sepolia"
|
network = "sepolia"
|
||||||
case ctx.Bool(utils.GoerliFlag.Name):
|
|
||||||
network = "goerli"
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No network flag set, try to determine network based on files
|
// No network flag set, try to determine network based on files
|
||||||
|
|
@ -625,7 +623,7 @@ func dump(ctx *cli.Context) error {
|
||||||
triedb := utils.MakeTrieDatabase(ctx, db, true, true, false) // always enable preimage lookup
|
triedb := utils.MakeTrieDatabase(ctx, db, true, true, false) // always enable preimage lookup
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
state, err := state.New(root, state.NewDatabase(triedb, nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,8 @@ var tomlSettings = toml.Config{
|
||||||
},
|
},
|
||||||
MissingField: func(rt reflect.Type, field string) error {
|
MissingField: func(rt reflect.Type, field string) error {
|
||||||
id := fmt.Sprintf("%s.%s", rt.String(), field)
|
id := fmt.Sprintf("%s.%s", rt.String(), field)
|
||||||
if deprecated(id) {
|
if deprecatedConfigFields[id] {
|
||||||
log.Warn("Config field is deprecated and won't have an effect", "name", id)
|
log.Warn(fmt.Sprintf("Config field '%s' is deprecated and won't have any effect.", id))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var link string
|
var link string
|
||||||
|
|
@ -91,6 +91,19 @@ var tomlSettings = toml.Config{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var deprecatedConfigFields = map[string]bool{
|
||||||
|
"ethconfig.Config.EVMInterpreter": true,
|
||||||
|
"ethconfig.Config.EWASMInterpreter": true,
|
||||||
|
"ethconfig.Config.TrieCleanCacheJournal": true,
|
||||||
|
"ethconfig.Config.TrieCleanCacheRejournal": true,
|
||||||
|
"ethconfig.Config.LightServ": true,
|
||||||
|
"ethconfig.Config.LightIngress": true,
|
||||||
|
"ethconfig.Config.LightEgress": true,
|
||||||
|
"ethconfig.Config.LightPeers": true,
|
||||||
|
"ethconfig.Config.LightNoPrune": true,
|
||||||
|
"ethconfig.Config.LightNoSyncServe": true,
|
||||||
|
}
|
||||||
|
|
||||||
type ethstatsConfig struct {
|
type ethstatsConfig struct {
|
||||||
URL string `toml:",omitempty"`
|
URL string `toml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
@ -335,21 +348,6 @@ func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func deprecated(field string) bool {
|
|
||||||
switch field {
|
|
||||||
case "ethconfig.Config.EVMInterpreter":
|
|
||||||
return true
|
|
||||||
case "ethconfig.Config.EWASMInterpreter":
|
|
||||||
return true
|
|
||||||
case "ethconfig.Config.TrieCleanCacheJournal":
|
|
||||||
return true
|
|
||||||
case "ethconfig.Config.TrieCleanCacheRejournal":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setAccountManagerBackends(conf *node.Config, am *accounts.Manager, keydir string) error {
|
func setAccountManagerBackends(conf *node.Config, am *accounts.Manager, keydir string) error {
|
||||||
scryptN := keystore.StandardScryptN
|
scryptN := keystore.StandardScryptN
|
||||||
scryptP := keystore.StandardScryptP
|
scryptP := keystore.StandardScryptP
|
||||||
|
|
|
||||||
|
|
@ -129,9 +129,7 @@ func remoteConsole(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if path != "" {
|
if path != "" {
|
||||||
if ctx.Bool(utils.GoerliFlag.Name) {
|
if ctx.Bool(utils.MumbaiFlag.Name) || ctx.Bool(utils.AmoyFlag.Name) || ctx.Bool(utils.BorMainnetFlag.Name) {
|
||||||
path = filepath.Join(path, "goerli")
|
|
||||||
} else if ctx.Bool(utils.MumbaiFlag.Name) || ctx.Bool(utils.AmoyFlag.Name) || ctx.Bool(utils.BorMainnetFlag.Name) {
|
|
||||||
homeDir, _ := os.UserHomeDir()
|
homeDir, _ := os.UserHomeDir()
|
||||||
path = filepath.Join(homeDir, "/.bor/data")
|
path = filepath.Join(homeDir, "/.bor/data")
|
||||||
} else if ctx.Bool(utils.SepoliaFlag.Name) {
|
} else if ctx.Bool(utils.SepoliaFlag.Name) {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ipcAPIs = "admin:1.0 bor:1.0 clique: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 bor: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"
|
||||||
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
|
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -38,10 +38,10 @@ const (
|
||||||
// memory and disk IO. If the args don't set --datadir, the
|
// memory and disk IO. If the args don't set --datadir, the
|
||||||
// child g gets a temporary data directory.
|
// child g gets a temporary data directory.
|
||||||
func runMinimalGeth(t *testing.T, args ...string) *testgeth {
|
func runMinimalGeth(t *testing.T, args ...string) *testgeth {
|
||||||
// --goerli to make the 'writing genesis to disk' faster (no accounts)
|
// --holesky to make the 'writing genesis to disk' faster (no accounts)
|
||||||
// --networkid=1337 to avoid cache bump
|
// --networkid=1337 to avoid cache bump
|
||||||
// --syncmode=full to avoid allocating fast sync bloom
|
// --syncmode=full to avoid allocating fast sync bloom
|
||||||
allArgs := []string{"--goerli", "--networkid", "1337", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
|
allArgs := []string{"--holesky", "--networkid", "1337", "--authrpc.port", "0", "--syncmode=full", "--port", "0",
|
||||||
"--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64",
|
"--nat", "none", "--nodiscover", "--maxpeers", "0", "--cache", "64",
|
||||||
"--datadir.minfreedisk", "0"}
|
"--datadir.minfreedisk", "0"}
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ func TestConsoleWelcome(t *testing.T) {
|
||||||
geth.SetTemplateFunc("gover", runtime.Version)
|
geth.SetTemplateFunc("gover", runtime.Version)
|
||||||
geth.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
geth.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
||||||
geth.SetTemplateFunc("niltime", func() string {
|
geth.SetTemplateFunc("niltime", func() string {
|
||||||
return time.Unix(1548854791, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
return time.Unix(1695902100, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
||||||
})
|
})
|
||||||
geth.SetTemplateFunc("apis", func() string { return ipcAPIs })
|
geth.SetTemplateFunc("apis", func() string { return ipcAPIs })
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
|
||||||
attach.SetTemplateFunc("gover", runtime.Version)
|
attach.SetTemplateFunc("gover", runtime.Version)
|
||||||
attach.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
attach.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
|
||||||
attach.SetTemplateFunc("niltime", func() string {
|
attach.SetTemplateFunc("niltime", func() string {
|
||||||
return time.Unix(1548854791, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
return time.Unix(1695902100, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
|
||||||
})
|
})
|
||||||
attach.SetTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
|
attach.SetTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
|
||||||
attach.SetTemplateFunc("datadir", func() string { return geth.Datadir })
|
attach.SetTemplateFunc("datadir", func() string { return geth.Datadir })
|
||||||
|
|
|
||||||
|
|
@ -164,7 +164,6 @@ var (
|
||||||
utils.BeaconGenesisRootFlag,
|
utils.BeaconGenesisRootFlag,
|
||||||
utils.BeaconGenesisTimeFlag,
|
utils.BeaconGenesisTimeFlag,
|
||||||
utils.BeaconCheckpointFlag,
|
utils.BeaconCheckpointFlag,
|
||||||
utils.CollectWitnessFlag,
|
|
||||||
}, utils.NetworkFlags, utils.DatabaseFlags)
|
}, utils.NetworkFlags, utils.DatabaseFlags)
|
||||||
|
|
||||||
rpcFlags = []cli.Flag{
|
rpcFlags = []cli.Flag{
|
||||||
|
|
@ -299,9 +298,6 @@ func main() {
|
||||||
func prepare(ctx *cli.Context) {
|
func prepare(ctx *cli.Context) {
|
||||||
// If we're running a known preset, log it for convenience.
|
// If we're running a known preset, log it for convenience.
|
||||||
switch {
|
switch {
|
||||||
case ctx.IsSet(utils.GoerliFlag.Name):
|
|
||||||
log.Info("Starting Geth on Görli testnet...")
|
|
||||||
|
|
||||||
case ctx.IsSet(utils.SepoliaFlag.Name):
|
case ctx.IsSet(utils.SepoliaFlag.Name):
|
||||||
log.Info("Starting Geth on Sepolia testnet...")
|
log.Info("Starting Geth on Sepolia testnet...")
|
||||||
|
|
||||||
|
|
@ -339,8 +335,8 @@ func prepare(ctx *cli.Context) {
|
||||||
if !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) {
|
if !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) {
|
||||||
// Make sure we're not on any supported preconfigured testnet either
|
// Make sure we're not on any supported preconfigured testnet either
|
||||||
if !ctx.IsSet(utils.SepoliaFlag.Name) &&
|
if !ctx.IsSet(utils.SepoliaFlag.Name) &&
|
||||||
!ctx.IsSet(utils.GoerliFlag.Name) &&
|
|
||||||
!ctx.IsSet(utils.MumbaiFlag.Name) &&
|
!ctx.IsSet(utils.MumbaiFlag.Name) &&
|
||||||
|
!ctx.IsSet(utils.HoleskyFlag.Name) &&
|
||||||
!ctx.IsSet(utils.AmoyFlag.Name) &&
|
!ctx.IsSet(utils.AmoyFlag.Name) &&
|
||||||
!ctx.IsSet(utils.DeveloperFlag.Name) {
|
!ctx.IsSet(utils.DeveloperFlag.Name) {
|
||||||
// Nope, we're really on mainnet. Bump that cache up!
|
// Nope, we're really on mainnet. Bump that cache up!
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ var (
|
||||||
}
|
}
|
||||||
NetworkIdFlag = &cli.Uint64Flag{
|
NetworkIdFlag = &cli.Uint64Flag{
|
||||||
Name: "networkid",
|
Name: "networkid",
|
||||||
Usage: "Explicitly set network id (integer)(For testnets: use --goerli, --sepolia, --holesky instead)",
|
Usage: "Explicitly set network id (integer)(For testnets: use --sepolia, --holesky instead)",
|
||||||
Value: ethconfig.Defaults.NetworkId,
|
Value: ethconfig.Defaults.NetworkId,
|
||||||
Category: flags.EthCategory,
|
Category: flags.EthCategory,
|
||||||
}
|
}
|
||||||
|
|
@ -144,11 +144,6 @@ var (
|
||||||
Usage: "Ethereum mainnet",
|
Usage: "Ethereum mainnet",
|
||||||
Category: flags.EthCategory,
|
Category: flags.EthCategory,
|
||||||
}
|
}
|
||||||
GoerliFlag = &cli.BoolFlag{
|
|
||||||
Name: "goerli",
|
|
||||||
Usage: "Görli network: pre-configured proof-of-authority test network",
|
|
||||||
Category: flags.EthCategory,
|
|
||||||
}
|
|
||||||
SepoliaFlag = &cli.BoolFlag{
|
SepoliaFlag = &cli.BoolFlag{
|
||||||
Name: "sepolia",
|
Name: "sepolia",
|
||||||
Usage: "Sepolia network: pre-configured proof-of-work test network",
|
Usage: "Sepolia network: pre-configured proof-of-work test network",
|
||||||
|
|
@ -644,11 +639,6 @@ var (
|
||||||
Usage: "Disables db compaction after import",
|
Usage: "Disables db compaction after import",
|
||||||
Category: flags.LoggingCategory,
|
Category: flags.LoggingCategory,
|
||||||
}
|
}
|
||||||
CollectWitnessFlag = &cli.BoolFlag{
|
|
||||||
Name: "collectwitness",
|
|
||||||
Usage: "Enable state witness generation during block execution. Work in progress flag, don't use.",
|
|
||||||
Category: flags.MiscCategory,
|
|
||||||
}
|
|
||||||
|
|
||||||
// MISC settings
|
// MISC settings
|
||||||
SyncTargetFlag = &cli.StringFlag{
|
SyncTargetFlag = &cli.StringFlag{
|
||||||
|
|
@ -851,8 +841,9 @@ var (
|
||||||
DiscoveryV5Flag = &cli.BoolFlag{
|
DiscoveryV5Flag = &cli.BoolFlag{
|
||||||
Name: "discovery.v5",
|
Name: "discovery.v5",
|
||||||
Aliases: []string{"discv5"},
|
Aliases: []string{"discv5"},
|
||||||
Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism",
|
Usage: "Enables the V5 discovery mechanism",
|
||||||
Category: flags.NetworkingCategory,
|
Category: flags.NetworkingCategory,
|
||||||
|
Value: true,
|
||||||
}
|
}
|
||||||
NetrestrictFlag = &cli.StringFlag{
|
NetrestrictFlag = &cli.StringFlag{
|
||||||
Name: "netrestrict",
|
Name: "netrestrict",
|
||||||
|
|
@ -1004,7 +995,6 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
||||||
var (
|
var (
|
||||||
// TestnetFlags is the flag group of all built-in supported testnets.
|
// TestnetFlags is the flag group of all built-in supported testnets.
|
||||||
TestnetFlags = []cli.Flag{
|
TestnetFlags = []cli.Flag{
|
||||||
GoerliFlag,
|
|
||||||
SepoliaFlag,
|
SepoliaFlag,
|
||||||
}
|
}
|
||||||
// NetworkFlags is the flag group of all built-in supported networks.
|
// NetworkFlags is the flag group of all built-in supported networks.
|
||||||
|
|
@ -1026,10 +1016,6 @@ var (
|
||||||
// then a subdirectory of the specified datadir will be used.
|
// then a subdirectory of the specified datadir will be used.
|
||||||
func MakeDataDir(ctx *cli.Context) string {
|
func MakeDataDir(ctx *cli.Context) string {
|
||||||
if path := ctx.String(DataDirFlag.Name); path != "" {
|
if path := ctx.String(DataDirFlag.Name); path != "" {
|
||||||
if ctx.Bool(GoerliFlag.Name) {
|
|
||||||
return filepath.Join(path, "goerli")
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.Bool(SepoliaFlag.Name) {
|
if ctx.Bool(SepoliaFlag.Name) {
|
||||||
return filepath.Join(path, "sepolia")
|
return filepath.Join(path, "sepolia")
|
||||||
}
|
}
|
||||||
|
|
@ -1083,7 +1069,7 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
|
||||||
//
|
//
|
||||||
// 1. --bootnodes flag
|
// 1. --bootnodes flag
|
||||||
// 2. Config file
|
// 2. Config file
|
||||||
// 3. Network preset flags (e.g. --goerli)
|
// 3. Network preset flags (e.g. --holesky)
|
||||||
// 4. default to mainnet nodes
|
// 4. default to mainnet nodes
|
||||||
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
urls := params.MainnetBootnodes
|
urls := params.MainnetBootnodes
|
||||||
|
|
@ -1096,8 +1082,6 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
switch {
|
switch {
|
||||||
case ctx.Bool(SepoliaFlag.Name):
|
case ctx.Bool(SepoliaFlag.Name):
|
||||||
urls = params.SepoliaBootnodes
|
urls = params.SepoliaBootnodes
|
||||||
case ctx.Bool(GoerliFlag.Name):
|
|
||||||
urls = params.GoerliBootnodes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cfg.BootstrapNodes = mustParseBootnodes(urls)
|
cfg.BootstrapNodes = mustParseBootnodes(urls)
|
||||||
|
|
@ -1378,8 +1362,8 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error
|
||||||
|
|
||||||
// setEtherbase retrieves the etherbase from the directly specified command line flags.
|
// setEtherbase retrieves the etherbase from the directly specified command line flags.
|
||||||
func setEtherbase(ctx *cli.Context, cfg *ethconfig.Config) {
|
func setEtherbase(ctx *cli.Context, cfg *ethconfig.Config) {
|
||||||
if !ctx.IsSet(MinerEtherbaseFlag.Name) {
|
if ctx.IsSet(MinerEtherbaseFlag.Name) {
|
||||||
return
|
log.Warn("Option --miner.etherbase is deprecated as the etherbase is set by the consensus client post-merge")
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := ctx.String(MinerEtherbaseFlag.Name)
|
addr := ctx.String(MinerEtherbaseFlag.Name)
|
||||||
|
|
@ -1556,8 +1540,6 @@ func SetDataDir(ctx *cli.Context, cfg *node.Config) {
|
||||||
cfg.DataDir = ctx.String(DataDirFlag.Name)
|
cfg.DataDir = ctx.String(DataDirFlag.Name)
|
||||||
case ctx.Bool(DeveloperFlag.Name):
|
case ctx.Bool(DeveloperFlag.Name):
|
||||||
cfg.DataDir = "" // unless explicitly requested, use memory databases
|
cfg.DataDir = "" // unless explicitly requested, use memory databases
|
||||||
case ctx.Bool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
|
||||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
|
|
||||||
case ctx.Bool(SepoliaFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
case ctx.Bool(SepoliaFlag.Name) && cfg.DataDir == node.DefaultDataDir():
|
||||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia")
|
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "sepolia")
|
||||||
}
|
}
|
||||||
|
|
@ -1752,7 +1734,7 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) {
|
||||||
// SetEthConfig applies eth-related command line flags to the config.
|
// SetEthConfig applies eth-related command line flags to the config.
|
||||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
// Avoid conflicting network flags
|
// Avoid conflicting network flags
|
||||||
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, GoerliFlag, SepoliaFlag, HoleskyFlag)
|
CheckExclusive(ctx, MainnetFlag, DeveloperFlag, SepoliaFlag, HoleskyFlag)
|
||||||
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
|
||||||
|
|
||||||
// Set configurations from CLI flags
|
// Set configurations from CLI flags
|
||||||
|
|
@ -1889,9 +1871,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
// TODO(fjl): force-enable this in --dev mode
|
// TODO(fjl): force-enable this in --dev mode
|
||||||
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
cfg.EnablePreimageRecording = ctx.Bool(VMEnableDebugFlag.Name)
|
||||||
}
|
}
|
||||||
if ctx.IsSet(CollectWitnessFlag.Name) {
|
|
||||||
cfg.EnableWitnessCollection = ctx.Bool(CollectWitnessFlag.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.IsSet(RPCGlobalGasCapFlag.Name) {
|
if ctx.IsSet(RPCGlobalGasCapFlag.Name) {
|
||||||
cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name)
|
cfg.RPCGasCap = ctx.Uint64(RPCGlobalGasCapFlag.Name)
|
||||||
|
|
@ -1937,13 +1916,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
|
|
||||||
cfg.Genesis = core.DefaultSepoliaGenesisBlock()
|
cfg.Genesis = core.DefaultSepoliaGenesisBlock()
|
||||||
SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash)
|
SetDNSDiscoveryDefaults(cfg, params.SepoliaGenesisHash)
|
||||||
case ctx.Bool(GoerliFlag.Name):
|
|
||||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
|
||||||
cfg.NetworkId = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg.Genesis = core.DefaultGoerliGenesisBlock()
|
|
||||||
SetDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash)
|
|
||||||
case ctx.Bool(DeveloperFlag.Name):
|
case ctx.Bool(DeveloperFlag.Name):
|
||||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||||
cfg.NetworkId = 1337
|
cfg.NetworkId = 1337
|
||||||
|
|
@ -2290,8 +2262,6 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
||||||
genesis = core.DefaultGenesisBlock()
|
genesis = core.DefaultGenesisBlock()
|
||||||
case ctx.Bool(SepoliaFlag.Name):
|
case ctx.Bool(SepoliaFlag.Name):
|
||||||
genesis = core.DefaultSepoliaGenesisBlock()
|
genesis = core.DefaultSepoliaGenesisBlock()
|
||||||
case ctx.Bool(GoerliFlag.Name):
|
|
||||||
genesis = core.DefaultGoerliGenesisBlock()
|
|
||||||
case ctx.Bool(DeveloperFlag.Name):
|
case ctx.Bool(DeveloperFlag.Name):
|
||||||
Fatalf("Developer chains are ephemeral")
|
Fatalf("Developer chains are ephemeral")
|
||||||
}
|
}
|
||||||
|
|
@ -2367,7 +2337,6 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
|
||||||
}
|
}
|
||||||
vmcfg := vm.Config{
|
vmcfg := vm.Config{
|
||||||
EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name),
|
EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name),
|
||||||
EnableWitnessCollection: ctx.Bool(CollectWitnessFlag.Name),
|
|
||||||
}
|
}
|
||||||
if ctx.IsSet(VMTraceFlag.Name) {
|
if ctx.IsSet(VMTraceFlag.Name) {
|
||||||
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||||
|
|
|
||||||
|
|
@ -92,25 +92,21 @@ var (
|
||||||
LightServeFlag = &cli.IntFlag{
|
LightServeFlag = &cli.IntFlag{
|
||||||
Name: "light.serve",
|
Name: "light.serve",
|
||||||
Usage: "Maximum percentage of time allowed for serving LES requests (deprecated)",
|
Usage: "Maximum percentage of time allowed for serving LES requests (deprecated)",
|
||||||
Value: ethconfig.Defaults.LightServ,
|
|
||||||
Category: flags.DeprecatedCategory,
|
Category: flags.DeprecatedCategory,
|
||||||
}
|
}
|
||||||
LightIngressFlag = &cli.IntFlag{
|
LightIngressFlag = &cli.IntFlag{
|
||||||
Name: "light.ingress",
|
Name: "light.ingress",
|
||||||
Usage: "Incoming bandwidth limit for serving light clients (deprecated)",
|
Usage: "Incoming bandwidth limit for serving light clients (deprecated)",
|
||||||
Value: ethconfig.Defaults.LightIngress,
|
|
||||||
Category: flags.DeprecatedCategory,
|
Category: flags.DeprecatedCategory,
|
||||||
}
|
}
|
||||||
LightEgressFlag = &cli.IntFlag{
|
LightEgressFlag = &cli.IntFlag{
|
||||||
Name: "light.egress",
|
Name: "light.egress",
|
||||||
Usage: "Outgoing bandwidth limit for serving light clients (deprecated)",
|
Usage: "Outgoing bandwidth limit for serving light clients (deprecated)",
|
||||||
Value: ethconfig.Defaults.LightEgress,
|
|
||||||
Category: flags.DeprecatedCategory,
|
Category: flags.DeprecatedCategory,
|
||||||
}
|
}
|
||||||
LightMaxPeersFlag = &cli.IntFlag{
|
LightMaxPeersFlag = &cli.IntFlag{
|
||||||
Name: "light.maxpeers",
|
Name: "light.maxpeers",
|
||||||
Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated)",
|
Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated)",
|
||||||
Value: ethconfig.Defaults.LightPeers,
|
|
||||||
Category: flags.DeprecatedCategory,
|
Category: flags.DeprecatedCategory,
|
||||||
}
|
}
|
||||||
LightNoPruneFlag = &cli.BoolFlag{
|
LightNoPruneFlag = &cli.BoolFlag{
|
||||||
|
|
|
||||||
0
common/prque/prque.go
Executable file → Normal file
0
common/prque/prque.go
Executable file → Normal file
0
common/prque/sstack.go
Executable file → Normal file
0
common/prque/sstack.go
Executable file → Normal file
|
|
@ -389,8 +389,39 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea
|
||||||
// Assign the final state root to header.
|
// Assign the final state root to header.
|
||||||
header.Root = state.IntermediateRoot(true)
|
header.Root = state.IntermediateRoot(true)
|
||||||
|
|
||||||
// Assemble and return the final block.
|
// Assemble the final block.
|
||||||
return types.NewBlock(header, body, receipts, trie.NewStackTrie(nil)), nil
|
block := types.NewBlock(header, body, receipts, trie.NewStackTrie(nil))
|
||||||
|
|
||||||
|
// Create the block witness and attach to block.
|
||||||
|
// This step needs to happen as late as possible to catch all access events.
|
||||||
|
if chain.Config().IsVerkle(header.Number) {
|
||||||
|
keys := state.AccessEvents().Keys()
|
||||||
|
|
||||||
|
// Open the pre-tree to prove the pre-state against
|
||||||
|
parent := chain.GetHeaderByNumber(header.Number.Uint64() - 1)
|
||||||
|
if parent == nil {
|
||||||
|
return nil, fmt.Errorf("nil parent header for block %d", header.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
preTrie, err := state.Database().OpenTrie(parent.Root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error opening pre-state tree root: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
vktPreTrie, okpre := preTrie.(*trie.VerkleTrie)
|
||||||
|
vktPostTrie, okpost := state.GetTrie().(*trie.VerkleTrie)
|
||||||
|
if okpre && okpost {
|
||||||
|
if len(keys) > 0 {
|
||||||
|
verkleProof, stateDiff, err := vktPreTrie.Proof(vktPostTrie, keys, vktPreTrie.FlatdbNodeResolver)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err)
|
||||||
|
}
|
||||||
|
block = block.WithWitness(&types.ExecutionWitness{StateDiff: stateDiff, VerkleProof: verkleProof})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return block, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seal generates a new sealing request for the given input block and pushes
|
// Seal generates a new sealing request for the given input block and pushes
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ func TestGenesisContractChange(t *testing.T) {
|
||||||
|
|
||||||
genesis := genspec.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
genesis := genspec.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||||
|
|
||||||
statedb, err := state.New(genesis.Root(), state.NewDatabase(db), nil)
|
statedb, err := state.New(genesis.Root(), state.NewDatabase(triedb.NewDatabase(db, triedb.HashDefaults), nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, genspec, nil, b, vm.Config{}, nil, nil, nil)
|
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, genspec, nil, b, vm.Config{}, nil, nil, nil)
|
||||||
|
|
@ -84,7 +84,7 @@ func TestGenesisContractChange(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, statedb.Database().TrieDB().Commit(root, true))
|
require.NoError(t, statedb.Database().TrieDB().Commit(root, true))
|
||||||
|
|
||||||
statedb, err := state.New(h.Root, state.NewDatabase(db), nil)
|
statedb, err := state.New(h.Root, state.NewDatabase(triedb.NewDatabase(db, triedb.HashDefaults), nil))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return root, statedb
|
return root, statedb
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,6 @@ func ApplyMessage(
|
||||||
msg.Data(),
|
msg.Data(),
|
||||||
msg.Gas(),
|
msg.Gas(),
|
||||||
uint256.NewInt(msg.Value().Uint64()),
|
uint256.NewInt(msg.Value().Uint64()),
|
||||||
nil,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
success := big.NewInt(5).SetBytes(ret)
|
success := big.NewInt(5).SetBytes(ret)
|
||||||
|
|
@ -100,6 +99,11 @@ func ApplyMessage(
|
||||||
log.Error("message execution failed on contract", "msgData", msg.Data)
|
log.Error("message execution failed on contract", "msgData", msg.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there's error committing span, log it here. It won't be reported before because the return value is empty.
|
||||||
|
if bytes.Equal(msg.To().Bytes(), validatorContract.Bytes()) && err != nil {
|
||||||
|
log.Error("message execution failed on contract", "err", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Update the state with pending changes
|
// Update the state with pending changes
|
||||||
if err != nil {
|
if err != nil {
|
||||||
state.Finalise(true)
|
state.Finalise(true)
|
||||||
|
|
@ -120,7 +124,6 @@ func ApplyBorMessage(vmenv *vm.EVM, msg Callmsg) (*core.ExecutionResult, error)
|
||||||
msg.Data(),
|
msg.Data(),
|
||||||
msg.Gas(),
|
msg.Gas(),
|
||||||
uint256.NewInt(msg.Value().Uint64()),
|
uint256.NewInt(msg.Value().Uint64()),
|
||||||
nil,
|
|
||||||
)
|
)
|
||||||
// Update the state with pending changes
|
// Update the state with pending changes
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// This test case is a repro of an annoying bug that took us forever to catch.
|
// This test case is a repro of an annoying bug that took us forever to catch.
|
||||||
// In Clique PoA networks (Görli, etc), consecutive blocks might have
|
// In Clique PoA networks, consecutive blocks might have the same state root (no
|
||||||
// the same state root (no block subsidy, empty block). If a node crashes, the
|
// block subsidy, empty block). If a node crashes, the chain ends up losing the
|
||||||
// chain ends up losing the recent state and needs to regenerate it from blocks
|
// recent state and needs to regenerate it from blocks already in the database.
|
||||||
// already in the database. The bug was that processing the block *prior* to an
|
// The bug was that processing the block *prior* to an empty one **also
|
||||||
// empty one **also completes** the empty one, ending up in a known-block error.
|
// completes** the empty one, ending up in a known-block error.
|
||||||
func TestReimportMirroredState(t *testing.T) {
|
func TestReimportMirroredState(t *testing.T) {
|
||||||
// Initialize a Clique chain with a single signer
|
// Initialize a Clique chain with a single signer
|
||||||
var (
|
var (
|
||||||
|
|
@ -90,7 +90,6 @@ func TestReimportMirroredState(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Insert the first two blocks and make sure the chain is valid
|
// Insert the first two blocks and make sure the chain is valid
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
|
||||||
chain, _ = core.NewBlockChain(db, nil, genspec, nil, engine, vm.Config{}, nil, nil, nil)
|
chain, _ = core.NewBlockChain(db, nil, genspec, nil, engine, vm.Config{}, nil, nil, nil)
|
||||||
defer chain.Stop()
|
defer chain.Stop()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -331,7 +331,6 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
chain, err := NewBlockChain(db, &cacheConfig, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
chain, err := NewBlockChain(db, &cacheConfig, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error creating chain: %v", err)
|
b.Fatalf("error creating chain: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/stateless"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
|
@ -127,15 +125,17 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||||
|
|
||||||
// ValidateState validates the various changes that happen after a state transition,
|
// ValidateState validates the various changes that happen after a state transition,
|
||||||
// such as amount of used gas, the receipt roots and the state root itself.
|
// such as amount of used gas, the receipt roots and the state root itself.
|
||||||
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64, stateless bool) error {
|
func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateDB, res *ProcessResult, stateless bool) error {
|
||||||
|
if res == nil {
|
||||||
|
return fmt.Errorf("nil ProcessResult value")
|
||||||
|
}
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
|
if block.GasUsed() != res.GasUsed {
|
||||||
if block.GasUsed() != usedGas {
|
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), res.GasUsed)
|
||||||
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas)
|
|
||||||
}
|
}
|
||||||
// Validate the received block's bloom with the one derived from the generated receipts.
|
// Validate the received block's bloom with the one derived from the generated receipts.
|
||||||
// For valid blocks this should always validate to true.
|
// For valid blocks this should always validate to true.
|
||||||
rbloom := types.CreateBloom(receipts)
|
rbloom := types.CreateBloom(res.Receipts)
|
||||||
if rbloom != header.Bloom {
|
if rbloom != header.Bloom {
|
||||||
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
|
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
|
||||||
}
|
}
|
||||||
|
|
@ -145,10 +145,17 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// The receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
|
// The receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
|
||||||
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
|
receiptSha := types.DeriveSha(res.Receipts, trie.NewStackTrie(nil))
|
||||||
if receiptSha != header.ReceiptHash {
|
if receiptSha != header.ReceiptHash {
|
||||||
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
|
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
|
||||||
}
|
}
|
||||||
|
// Validate the parsed requests match the expected header value.
|
||||||
|
if header.RequestsHash != nil {
|
||||||
|
depositSha := types.DeriveSha(res.Requests, trie.NewStackTrie(nil))
|
||||||
|
if depositSha != *header.RequestsHash {
|
||||||
|
return fmt.Errorf("invalid deposit root hash (remote: %x local: %x)", *header.RequestsHash, depositSha)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Validate the state root against the received state root and throw
|
// Validate the state root against the received state root and throw
|
||||||
// an error if they don't match.
|
// an error if they don't match.
|
||||||
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
|
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
|
||||||
|
|
@ -158,28 +165,6 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateWitness cross validates a block execution with stateless remote clients.
|
|
||||||
//
|
|
||||||
// Normally we'd distribute the block witness to remote cross validators, wait
|
|
||||||
// for them to respond and then merge the results. For now, however, it's only
|
|
||||||
// Geth, so do an internal stateless run.
|
|
||||||
func (v *BlockValidator) ValidateWitness(witness *stateless.Witness, receiptRoot common.Hash, stateRoot common.Hash) error {
|
|
||||||
// Run the cross client stateless execution
|
|
||||||
// TODO(karalabe): Self-stateless for now, swap with other clients
|
|
||||||
crossReceiptRoot, crossStateRoot, err := ExecuteStateless(v.config, witness)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("stateless execution failed: %v", err)
|
|
||||||
}
|
|
||||||
// Stateless cross execution suceeeded, validate the withheld computed fields
|
|
||||||
if crossReceiptRoot != receiptRoot {
|
|
||||||
return fmt.Errorf("cross validator receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, receiptRoot)
|
|
||||||
}
|
|
||||||
if crossStateRoot != stateRoot {
|
|
||||||
return fmt.Errorf("cross validator state root mismatch (cross: %x local: %x)", crossStateRoot, stateRoot)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CalcGasLimit computes the gas limit of the next block after parent. It aims
|
// CalcGasLimit computes the gas limit of the next block after parent. It aims
|
||||||
// to keep the baseline gas close to the provided target, and increase it towards
|
// to keep the baseline gas close to the provided target, and increase it towards
|
||||||
// the target if the baseline gas is lower.
|
// the target if the baseline gas is lower.
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
||||||
t.Fatalf("post-block %d: unexpected result returned: %v", i, result)
|
t.Fatalf("post-block %d: unexpected result returned: %v", i, result)
|
||||||
case <-time.After(25 * time.Millisecond):
|
case <-time.After(25 * time.Millisecond):
|
||||||
}
|
}
|
||||||
chain.InsertBlockWithoutSetHead(postBlocks[i])
|
chain.InsertBlockWithoutSetHead(postBlocks[i], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the blocks with pre-merge blocks and post-merge blocks
|
// Verify the blocks with pre-merge blocks and post-merge blocks
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -42,6 +41,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||||
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -78,17 +78,21 @@ var (
|
||||||
storageUpdateTimer = metrics.NewRegisteredResettingTimer("chain/storage/updates", nil)
|
storageUpdateTimer = metrics.NewRegisteredResettingTimer("chain/storage/updates", nil)
|
||||||
storageCommitTimer = metrics.NewRegisteredResettingTimer("chain/storage/commits", nil)
|
storageCommitTimer = metrics.NewRegisteredResettingTimer("chain/storage/commits", nil)
|
||||||
|
|
||||||
|
accountReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/account/single/reads", nil) //nolint:golint,unused
|
||||||
|
storageReadSingleTimer = metrics.NewRegisteredResettingTimer("chain/storage/single/reads", nil) //nolint:golint,unused
|
||||||
|
|
||||||
|
snapshotCommitTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/commits", nil)
|
||||||
|
triedbCommitTimer = metrics.NewRegisteredResettingTimer("chain/triedb/commits", nil)
|
||||||
snapshotAccountReadTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/account/reads", nil)
|
snapshotAccountReadTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/account/reads", nil)
|
||||||
snapshotStorageReadTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/storage/reads", nil)
|
snapshotStorageReadTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/storage/reads", nil)
|
||||||
snapshotCommitTimer = metrics.NewRegisteredResettingTimer("chain/snapshot/commits", nil)
|
|
||||||
|
|
||||||
borConsensusTime = metrics.NewRegisteredTimer("chain/bor/consensus", nil)
|
borConsensusTime = metrics.NewRegisteredTimer("chain/bor/consensus", nil)
|
||||||
|
|
||||||
blockImportTimer = metrics.NewRegisteredMeter("chain/imports", nil)
|
blockImportTimer = metrics.NewRegisteredMeter("chain/imports", nil)
|
||||||
triedbCommitTimer = metrics.NewRegisteredTimer("chain/triedb/commits", nil)
|
|
||||||
|
|
||||||
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
|
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
|
||||||
blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil)
|
blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil)
|
||||||
|
blockCrossValidationTimer = metrics.NewRegisteredResettingTimer("chain/crossvalidation", nil) //nolint:golint,unused
|
||||||
blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil)
|
blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil)
|
||||||
blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil)
|
blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil)
|
||||||
blockExecutionParallelCounter = metrics.NewRegisteredCounter("chain/execution/parallel", nil)
|
blockExecutionParallelCounter = metrics.NewRegisteredCounter("chain/execution/parallel", nil)
|
||||||
|
|
@ -237,7 +241,7 @@ type BlockChain struct {
|
||||||
lastWrite uint64 // Last block when the state was flushed
|
lastWrite uint64 // Last block when the state was flushed
|
||||||
flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state
|
flushInterval atomic.Int64 // Time interval (processing time) after which to flush a state
|
||||||
triedb *triedb.Database // The database handler for maintaining trie nodes.
|
triedb *triedb.Database // The database handler for maintaining trie nodes.
|
||||||
stateCache state.Database // State database to reuse between imports (contains state cache)
|
statedb *state.CachingDB // State database to reuse between imports (contains state cache)
|
||||||
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
|
||||||
|
|
||||||
hc *HeaderChain
|
hc *HeaderChain
|
||||||
|
|
@ -356,7 +360,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
|
|
||||||
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
|
bc.flushInterval.Store(int64(cacheConfig.TrieTimeLimit))
|
||||||
bc.forker = NewForkChoice(bc, shouldPreserve, checker)
|
bc.forker = NewForkChoice(bc, shouldPreserve, checker)
|
||||||
bc.stateCache = state.NewDatabaseWithNodeDB(bc.db, bc.triedb)
|
bc.statedb = state.NewDatabase(bc.triedb, nil)
|
||||||
bc.validator = NewBlockValidator(chainConfig, bc)
|
bc.validator = NewBlockValidator(chainConfig, bc)
|
||||||
bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
|
bc.prefetcher = newStatePrefetcher(chainConfig, bc.hc)
|
||||||
bc.processor = NewStateProcessor(chainConfig, bc, bc.hc)
|
bc.processor = NewStateProcessor(chainConfig, bc, bc.hc)
|
||||||
|
|
@ -524,11 +528,10 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||||
AsyncBuild: !bc.cacheConfig.SnapshotWait,
|
AsyncBuild: !bc.cacheConfig.SnapshotWait,
|
||||||
}
|
}
|
||||||
bc.snaps, _ = snapshot.New(snapconfig, bc.db, bc.triedb, head.Root)
|
bc.snaps, _ = snapshot.New(snapconfig, bc.db, bc.triedb, head.Root)
|
||||||
}
|
|
||||||
|
|
||||||
// Start future block processor.
|
// Re-initialize the state database with snapshot
|
||||||
// bc.wg.Add(1)
|
bc.statedb = state.NewDatabase(bc.triedb, bc.snaps)
|
||||||
// go bc.updateFutureBlocks()
|
}
|
||||||
|
|
||||||
// Rewind the chain in case of an incompatible config upgrade.
|
// Rewind the chain in case of an incompatible config upgrade.
|
||||||
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
if compat, ok := genesisErr.(*params.ConfigCompatError); ok {
|
||||||
|
|
@ -575,8 +578,6 @@ func NewParallelBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis
|
||||||
|
|
||||||
func (bc *BlockChain) ProcessBlock(block *types.Block, parent *types.Header) (_ types.Receipts, _ []*types.Log, _ uint64, _ *state.StateDB, vtime time.Duration, blockEndErr error) {
|
func (bc *BlockChain) ProcessBlock(block *types.Block, parent *types.Header) (_ types.Receipts, _ []*types.Log, _ uint64, _ *state.StateDB, vtime time.Duration, blockEndErr error) {
|
||||||
// Process the block using processor and parallelProcessor at the same time, take the one which finishes first, cancel the other, and return the result
|
// Process the block using processor and parallelProcessor at the same time, take the one which finishes first, cancel the other, and return the result
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
||||||
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||||
|
|
@ -609,7 +610,7 @@ func (bc *BlockChain) ProcessBlock(block *types.Block, parent *types.Header) (_
|
||||||
processorCount := 0
|
processorCount := 0
|
||||||
|
|
||||||
if bc.parallelProcessor != nil {
|
if bc.parallelProcessor != nil {
|
||||||
parallelStatedb, err := state.New(parent.Root, bc.stateCache, bc.snaps)
|
parallelStatedb, err := state.New(parent.Root, bc.statedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, nil, 0, err
|
return nil, nil, 0, nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -620,19 +621,22 @@ func (bc *BlockChain) ProcessBlock(block *types.Block, parent *types.Header) (_
|
||||||
go func() {
|
go func() {
|
||||||
parallelStatedb.StartPrefetcher("chain", nil)
|
parallelStatedb.StartPrefetcher("chain", nil)
|
||||||
pstart := time.Now()
|
pstart := time.Now()
|
||||||
receipts, logs, usedGas, err := bc.parallelProcessor.Process(block, parallelStatedb, bc.vmConfig, ctx)
|
res, err := bc.parallelProcessor.Process(block, parallelStatedb, bc.vmConfig)
|
||||||
blockExecutionParallelTimer.UpdateSince(pstart)
|
blockExecutionParallelTimer.UpdateSince(pstart)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
vstart := time.Now()
|
vstart := time.Now()
|
||||||
err = bc.validator.ValidateState(block, parallelStatedb, receipts, usedGas, false)
|
err = bc.validator.ValidateState(block, parallelStatedb, res, false)
|
||||||
vtime = time.Since(vstart)
|
vtime = time.Since(vstart)
|
||||||
}
|
}
|
||||||
resultChan <- Result{receipts, logs, usedGas, err, parallelStatedb, blockExecutionParallelCounter, true}
|
if res == nil {
|
||||||
|
res = &ProcessResult{}
|
||||||
|
}
|
||||||
|
resultChan <- Result{res.Receipts, res.Logs, res.GasUsed, err, parallelStatedb, blockExecutionParallelCounter, true}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
if bc.processor != nil {
|
if bc.processor != nil {
|
||||||
statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps)
|
statedb, err := state.New(parent.Root, bc.statedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, nil, 0, err
|
return nil, nil, 0, nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -643,14 +647,17 @@ func (bc *BlockChain) ProcessBlock(block *types.Block, parent *types.Header) (_
|
||||||
go func() {
|
go func() {
|
||||||
statedb.StartPrefetcher("chain", nil)
|
statedb.StartPrefetcher("chain", nil)
|
||||||
pstart := time.Now()
|
pstart := time.Now()
|
||||||
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig, ctx)
|
res, err := bc.processor.Process(block, statedb, bc.vmConfig)
|
||||||
blockExecutionSerialTimer.UpdateSince(pstart)
|
blockExecutionSerialTimer.UpdateSince(pstart)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
vstart := time.Now()
|
vstart := time.Now()
|
||||||
err = bc.validator.ValidateState(block, statedb, receipts, usedGas, false)
|
err = bc.validator.ValidateState(block, statedb, res, false)
|
||||||
vtime = time.Since(vstart)
|
vtime = time.Since(vstart)
|
||||||
}
|
}
|
||||||
resultChan <- Result{receipts, logs, usedGas, err, statedb, blockExecutionSerialCounter, false}
|
if res == nil {
|
||||||
|
res = &ProcessResult{}
|
||||||
|
}
|
||||||
|
resultChan <- Result{res.Receipts, res.Logs, res.GasUsed, err, statedb, blockExecutionSerialCounter, false}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1922,18 +1929,18 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||||
|
|
||||||
// WriteBlockAndSetHead writes the given block and all associated state to the database,
|
// WriteBlockAndSetHead writes the given block and all associated state to the database,
|
||||||
// and applies the block as the new chain head.
|
// and applies the block as the new chain head.
|
||||||
func (bc *BlockChain) WriteBlockAndSetHead(ctx context.Context, block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
func (bc *BlockChain) WriteBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||||
if !bc.chainmu.TryLock() {
|
if !bc.chainmu.TryLock() {
|
||||||
return NonStatTy, errChainStopped
|
return NonStatTy, errChainStopped
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
return bc.writeBlockAndSetHead(ctx, block, receipts, logs, state, emitHeadEvent)
|
return bc.writeBlockAndSetHead(block, receipts, logs, state, emitHeadEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeBlockAndSetHead is the internal implementation of WriteBlockAndSetHead.
|
// writeBlockAndSetHead is the internal implementation of WriteBlockAndSetHead.
|
||||||
// This function expects the chain mutex to be held.
|
// This function expects the chain mutex to be held.
|
||||||
func (bc *BlockChain) writeBlockAndSetHead(ctx context.Context, block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
|
||||||
stateSyncLogs, err := bc.writeBlockWithState(block, receipts, logs, state)
|
stateSyncLogs, err := bc.writeBlockWithState(block, receipts, logs, state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return NonStatTy, err
|
return NonStatTy, err
|
||||||
|
|
@ -2056,7 +2063,9 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
return 0, errChainStopped
|
return 0, errChainStopped
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
return bc.insertChain(chain, true)
|
|
||||||
|
_, n, err := bc.insertChain(chain, true, false) // No witness collection for mass inserts (would get super large)
|
||||||
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertChain is the internal implementation of InsertChain, which assumes that
|
// insertChain is the internal implementation of InsertChain, which assumes that
|
||||||
|
|
@ -2067,10 +2076,10 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
// racey behaviour. If a sidechain import is in progress, and the historic state
|
// 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
|
// is imported, but then new canon-head is added before the actual sidechain
|
||||||
// completes, then the historic state could be pruned again
|
// completes, then the historic state could be pruned again
|
||||||
func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error) {
|
func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness bool) (*stateless.Witness, int, error) {
|
||||||
// If the chain is terminating, don't even bother starting up.
|
// If the chain is terminating, don't even bother starting up.
|
||||||
if bc.insertStopped() {
|
if bc.insertStopped() {
|
||||||
return 0, nil
|
return nil, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
|
||||||
|
|
@ -2106,13 +2115,13 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
// Check the validity of incoming chain
|
// Check the validity of incoming chain
|
||||||
isValid, err1 := bc.forker.ValidateReorg(bc.CurrentBlock(), headers)
|
isValid, err1 := bc.forker.ValidateReorg(bc.CurrentBlock(), headers)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
return it.index, err1
|
return nil, it.index, err1
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isValid {
|
if !isValid {
|
||||||
// The chain to be imported is invalid as the blocks doesn't match with
|
// The chain to be imported is invalid as the blocks doesn't match with
|
||||||
// the whitelisted block number.
|
// the whitelisted block number.
|
||||||
return it.index, whitelist.ErrMismatch
|
return nil, it.index, whitelist.ErrMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
// Left-trim all the known blocks that don't need to build snapshot
|
// Left-trim all the known blocks that don't need to build snapshot
|
||||||
|
|
@ -2130,7 +2139,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
for block != nil && bc.skipBlock(err, it) {
|
for block != nil && bc.skipBlock(err, it) {
|
||||||
reorg, err = bc.forker.ReorgNeeded(current, block.Header())
|
reorg, err = bc.forker.ReorgNeeded(current, block.Header())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if reorg {
|
if reorg {
|
||||||
|
|
@ -2162,7 +2171,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
log.Debug("Writing previously known block", "number", block.Number(), "hash", block.Hash())
|
log.Debug("Writing previously known block", "number", block.Number(), "hash", block.Hash())
|
||||||
|
|
||||||
if err := bc.writeKnownBlock(block); err != nil {
|
if err := bc.writeKnownBlock(block); err != nil {
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lastCanon = block
|
lastCanon = block
|
||||||
|
|
@ -2178,13 +2187,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
if setHead {
|
if setHead {
|
||||||
// First block is pruned, insert as sidechain and reorg only if TD grows enough
|
// 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())
|
log.Debug("Pruned ancestor, inserting as sidechain", "number", block.Number(), "hash", block.Hash())
|
||||||
return bc.insertSideChain(block, it)
|
return bc.insertSideChain(block, it, makeWitness)
|
||||||
} else {
|
} else {
|
||||||
// We're post-merge and the parent is pruned, try to recover the parent state
|
// 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())
|
log.Debug("Pruned ancestor", "number", block.Number(), "hash", block.Hash())
|
||||||
_, err := bc.recoverAncestors(block)
|
_, err := bc.recoverAncestors(block, makeWitness)
|
||||||
|
return nil, it.index, err
|
||||||
return it.index, err
|
|
||||||
}
|
}
|
||||||
// First block is future, shove it (and all children) to the future queue (unknown ancestor)
|
// First block is future, shove it (and all children) to the future queue (unknown ancestor)
|
||||||
case errors.Is(err, consensus.ErrFutureBlock) || (errors.Is(err, consensus.ErrUnknownAncestor) && bc.futureBlocks.Contains(it.first().ParentHash())):
|
case errors.Is(err, consensus.ErrFutureBlock) || (errors.Is(err, consensus.ErrUnknownAncestor) && bc.futureBlocks.Contains(it.first().ParentHash())):
|
||||||
|
|
@ -2192,7 +2200,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
log.Debug("Future block, postponing import", "number", block.Number(), "hash", block.Hash())
|
log.Debug("Future block, postponing import", "number", block.Number(), "hash", block.Hash())
|
||||||
|
|
||||||
if err := bc.addFutureBlock(block); err != nil {
|
if err := bc.addFutureBlock(block); err != nil {
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err = it.next()
|
block, err = it.next()
|
||||||
|
|
@ -2202,7 +2210,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
stats.ignored += it.remaining()
|
stats.ignored += it.remaining()
|
||||||
|
|
||||||
// If there are any still remaining, mark as ignored
|
// If there are any still remaining, mark as ignored
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
|
|
||||||
// Some other error(except ErrKnownBlock) occurred, abort.
|
// Some other error(except ErrKnownBlock) occurred, abort.
|
||||||
// ErrKnownBlock is allowed here since some known blocks
|
// ErrKnownBlock is allowed here since some known blocks
|
||||||
|
|
@ -2211,8 +2219,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
stats.ignored += len(it.chain)
|
stats.ignored += len(it.chain)
|
||||||
|
|
||||||
bc.reportBlock(block, nil, err)
|
bc.reportBlock(block, nil, err)
|
||||||
|
return nil, it.index, err
|
||||||
return it.index, err
|
|
||||||
}
|
}
|
||||||
// No validation errors for the first block (or chain prefix skipped)
|
// No validation errors for the first block (or chain prefix skipped)
|
||||||
var activeState *state.StateDB
|
var activeState *state.StateDB
|
||||||
|
|
@ -2226,6 +2233,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Track the singleton witness from this chain insertion (if any)
|
||||||
|
var witness *stateless.Witness
|
||||||
|
|
||||||
// accumulator for canonical blocks
|
// accumulator for canonical blocks
|
||||||
var canonAccum []*types.Block
|
var canonAccum []*types.Block
|
||||||
|
|
||||||
|
|
@ -2254,7 +2264,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
// If the header is a banned one, straight out abort
|
// If the header is a banned one, straight out abort
|
||||||
if BadHashes[block.Hash()] {
|
if BadHashes[block.Hash()] {
|
||||||
bc.reportBlock(block, nil, ErrBannedHash)
|
bc.reportBlock(block, nil, ErrBannedHash)
|
||||||
return it.index, ErrBannedHash
|
return nil, it.index, ErrBannedHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the block is known (in the middle of the chain), it's a special case for
|
// If the block is known (in the middle of the chain), it's a special case for
|
||||||
|
|
@ -2289,7 +2299,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := bc.writeKnownBlock(block); err != nil {
|
if err := bc.writeKnownBlock(block); err != nil {
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.processed++
|
stats.processed++
|
||||||
|
|
@ -2301,14 +2311,12 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
Safe: bc.CurrentSafeBlock(),
|
Safe: bc.CurrentSafeBlock(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can assume that logs are empty here, since the only way for consecutive
|
// We can assume that logs are empty here, since the only way for consecutive
|
||||||
// Clique blocks to have the same state is if there are no transactions.
|
// Clique blocks to have the same state is if there are no transactions.
|
||||||
lastCanon = block
|
lastCanon = block
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the parent block and it's state to execute on top
|
// Retrieve the parent block and it's state to execute on top
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
|
@ -2316,6 +2324,28 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||||
}
|
}
|
||||||
|
statedb, err := state.New(parent.Root, bc.statedb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, it.index, err
|
||||||
|
}
|
||||||
|
statedb.SetLogger(bc.logger)
|
||||||
|
|
||||||
|
// If we are past Byzantium, enable prefetching to pull in trie node paths
|
||||||
|
// while processing transactions. Before Byzantium the prefetcher is mostly
|
||||||
|
// useless due to the intermediate root hashing after each transaction.
|
||||||
|
if bc.chainConfig.IsByzantium(block.Number()) {
|
||||||
|
// 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.vmConfig.StatelessSelfValidation || (makeWitness && len(chain) == 1) {
|
||||||
|
witness, err = stateless.NewWitness(block.Header(), bc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, it.index, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
statedb.StartPrefetcher("chain", witness)
|
||||||
|
}
|
||||||
|
activeState = statedb
|
||||||
|
|
||||||
// If we have a followup block, run that against the current state to pre-cache
|
// If we have a followup block, run that against the current state to pre-cache
|
||||||
// transactions and probabilistically some of the account/storage trie nodes.
|
// transactions and probabilistically some of the account/storage trie nodes.
|
||||||
|
|
@ -2323,7 +2353,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
|
|
||||||
if !bc.cacheConfig.TrieCleanNoPrefetch {
|
if !bc.cacheConfig.TrieCleanNoPrefetch {
|
||||||
if followup, err := it.peek(); followup != nil && err == nil {
|
if followup, err := it.peek(); followup != nil && err == nil {
|
||||||
throwaway, _ := state.New(parent.Root, bc.stateCache, bc.snaps)
|
throwaway, _ := state.New(parent.Root, bc.statedb)
|
||||||
|
|
||||||
go func(start time.Time, followup *types.Block, throwaway *state.StateDB) {
|
go func(start time.Time, followup *types.Block, throwaway *state.StateDB) {
|
||||||
// Disable tracing for prefetcher executions.
|
// Disable tracing for prefetcher executions.
|
||||||
|
|
@ -2346,10 +2376,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
activeState = statedb
|
activeState = statedb
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bc.reportBlock(block, receipts, err)
|
bc.reportBlock(block, &ProcessResult{Receipts: receipts}, err)
|
||||||
followupInterrupt.Store(true)
|
followupInterrupt.Store(true)
|
||||||
|
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOR state sync feed related changes
|
// BOR state sync feed related changes
|
||||||
|
|
@ -2391,23 +2421,24 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
// so that it's considered as a `past` chain and the validation doesn't get bypassed.
|
// so that it's considered as a `past` chain and the validation doesn't get bypassed.
|
||||||
isValid, err = bc.forker.ValidateReorg(block.Header(), []*types.Header{block.Header()})
|
isValid, err = bc.forker.ValidateReorg(block.Header(), []*types.Header{block.Header()})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isValid {
|
if !isValid {
|
||||||
return it.index, whitelist.ErrMismatch
|
return nil, it.index, whitelist.ErrMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
if !setHead {
|
if !setHead {
|
||||||
// Don't set the head, only insert the block
|
// Don't set the head, only insert the block
|
||||||
_, err = bc.writeBlockWithState(block, receipts, logs, statedb)
|
_, err = bc.writeBlockWithState(block, receipts, logs, statedb)
|
||||||
} else {
|
} else {
|
||||||
status, err = bc.writeBlockAndSetHead(context.Background(), block, receipts, logs, statedb, false)
|
status, err = bc.writeBlockAndSetHead(block, receipts, logs, statedb, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
followupInterrupt.Store(true)
|
followupInterrupt.Store(true)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the metrics touched during block commit
|
// Update the metrics touched during block commit
|
||||||
|
|
@ -2434,7 +2465,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
// After merge we expect few side chains. Simply count
|
// After merge we expect few side chains. Simply count
|
||||||
// all blocks the CL gives us for GC processing time
|
// all blocks the CL gives us for GC processing time
|
||||||
bc.gcproc += proctime
|
bc.gcproc += proctime
|
||||||
return it.index, nil // Direct block insertion of a single block
|
return witness, it.index, nil // Direct block insertion of a single block
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOR
|
// BOR
|
||||||
|
|
@ -2480,14 +2511,14 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
// Any blocks remaining here? The only ones we care about are the future ones
|
// Any blocks remaining here? The only ones we care about are the future ones
|
||||||
if block != nil && errors.Is(err, consensus.ErrFutureBlock) {
|
if block != nil && errors.Is(err, consensus.ErrFutureBlock) {
|
||||||
if err := bc.addFutureBlock(block); err != nil {
|
if err := bc.addFutureBlock(block); err != nil {
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err = it.next()
|
block, err = it.next()
|
||||||
|
|
||||||
for ; block != nil && errors.Is(err, consensus.ErrUnknownAncestor); block, err = it.next() {
|
for ; block != nil && errors.Is(err, consensus.ErrUnknownAncestor); block, err = it.next() {
|
||||||
if err := bc.addFutureBlock(block); err != nil {
|
if err := bc.addFutureBlock(block); err != nil {
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.queued++
|
stats.queued++
|
||||||
|
|
@ -2495,7 +2526,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.ignored += it.remaining()
|
stats.ignored += it.remaining()
|
||||||
return it.index, err
|
return witness, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// blockProcessingResult is a summary of block processing
|
// blockProcessingResult is a summary of block processing
|
||||||
|
|
@ -2528,42 +2559,68 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||||
|
|
||||||
// Process block using the parent state as reference point
|
// Process block using the parent state as reference point
|
||||||
pstart := time.Now()
|
pstart := time.Now()
|
||||||
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig, context.Background())
|
res, err := bc.processor.Process(block, statedb, bc.vmConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bc.reportBlock(block, receipts, err)
|
bc.reportBlock(block, res, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ptime := time.Since(pstart)
|
ptime := time.Since(pstart)
|
||||||
|
|
||||||
vstart := time.Now()
|
vstart := time.Now()
|
||||||
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas, false); err != nil {
|
if err := bc.validator.ValidateState(block, statedb, res, false); err != nil {
|
||||||
bc.reportBlock(block, receipts, err)
|
bc.reportBlock(block, res, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
vtime := time.Since(vstart)
|
vtime := time.Since(vstart)
|
||||||
|
|
||||||
if witness := statedb.Witness(); witness != nil {
|
// If witnesses was generated and stateless self-validation requested, do
|
||||||
if err = bc.validator.ValidateWitness(witness, block.ReceiptHash(), block.Root()); err != nil {
|
// that now. Self validation should *never* run in production, it's more of
|
||||||
bc.reportBlock(block, receipts, err)
|
// a tight integration to enable running *all* consensus tests through the
|
||||||
return nil, fmt.Errorf("cross verification failed: %v", err)
|
// 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.vmConfig.StatelessSelfValidation {
|
||||||
|
log.Warn("Running stateless self-validation", "block", block.Number(), "hash", block.Hash())
|
||||||
|
|
||||||
|
// Remove critical computed fields from the block to force true recalculation
|
||||||
|
context := block.Header()
|
||||||
|
context.Root = common.Hash{}
|
||||||
|
context.ReceiptHash = common.Hash{}
|
||||||
|
|
||||||
|
task := types.NewBlockWithHeader(context).WithBody(*block.Body())
|
||||||
|
|
||||||
|
// Run the stateless self-cross-validation
|
||||||
|
crossStateRoot, crossReceiptRoot, err := ExecuteStateless(bc.chainConfig, task, witness)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("stateless self-validation failed: %v", err)
|
||||||
|
}
|
||||||
|
if crossStateRoot != block.Root() {
|
||||||
|
return nil, fmt.Errorf("stateless self-validation root mismatch (cross: %x local: %x)", crossStateRoot, block.Root())
|
||||||
|
}
|
||||||
|
if crossReceiptRoot != block.ReceiptHash() {
|
||||||
|
return nil, fmt.Errorf("stateless self-validation receipt root mismatch (cross: %x local: %x)", crossReceiptRoot, block.ReceiptHash())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
proctime := time.Since(start) // processing + validation
|
xvtime := time.Since(xvstart)
|
||||||
|
proctime := time.Since(start) // processing + validation + cross validation
|
||||||
|
|
||||||
// Update the metrics touched during block processing and validation
|
// Update the metrics touched during block processing and validation
|
||||||
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
|
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete(in processing)
|
||||||
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
|
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete(in processing)
|
||||||
snapshotAccountReadTimer.Update(statedb.SnapshotAccountReads) // Account reads are complete(in processing)
|
if statedb.AccountLoaded != 0 {
|
||||||
snapshotStorageReadTimer.Update(statedb.SnapshotStorageReads) // Storage reads are complete(in processing)
|
accountReadSingleTimer.Update(statedb.AccountReads / time.Duration(statedb.AccountLoaded))
|
||||||
|
}
|
||||||
|
if statedb.StorageLoaded != 0 {
|
||||||
|
storageReadSingleTimer.Update(statedb.StorageReads / time.Duration(statedb.StorageLoaded))
|
||||||
|
}
|
||||||
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
|
accountUpdateTimer.Update(statedb.AccountUpdates) // Account updates are complete(in validation)
|
||||||
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
|
storageUpdateTimer.Update(statedb.StorageUpdates) // Storage updates are complete(in validation)
|
||||||
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
|
accountHashTimer.Update(statedb.AccountHashes) // Account hashes are complete(in validation)
|
||||||
triehash := statedb.AccountHashes // The time spent on tries hashing
|
triehash := statedb.AccountHashes // The time spent on tries hashing
|
||||||
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
|
trieUpdate := statedb.AccountUpdates + statedb.StorageUpdates // The time spent on tries update
|
||||||
trieRead := statedb.SnapshotAccountReads + statedb.AccountReads // The time spent on account read
|
blockExecutionTimer.Update(ptime - (statedb.AccountReads + statedb.StorageReads)) // The time spent on EVM processing
|
||||||
trieRead += statedb.SnapshotStorageReads + statedb.StorageReads // The time spent on storage read
|
|
||||||
blockExecutionTimer.Update(ptime - trieRead) // The time spent on EVM processing
|
|
||||||
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
|
blockValidationTimer.Update(vtime - (triehash + trieUpdate)) // The time spent on block validation
|
||||||
|
blockCrossValidationTimer.Update(xvtime) // The time spent on stateless cross validation
|
||||||
|
|
||||||
// Write the block to the chain and get the status.
|
// Write the block to the chain and get the status.
|
||||||
var (
|
var (
|
||||||
|
|
@ -2572,9 +2629,9 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||||
)
|
)
|
||||||
if !setHead {
|
if !setHead {
|
||||||
// Don't set the head, only insert the block
|
// Don't set the head, only insert the block
|
||||||
_, err = bc.writeBlockWithState(block, receipts, logs, statedb)
|
_, err = bc.writeBlockWithState(block, res.Receipts, res.Logs, statedb)
|
||||||
} else {
|
} else {
|
||||||
status, err = bc.writeBlockAndSetHead(context.Background(), block, receipts, logs, statedb, false)
|
status, err = bc.writeBlockAndSetHead(block, res.Receipts, res.Logs, statedb, false)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -2588,7 +2645,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||||
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
blockWriteTimer.Update(time.Since(wstart) - max(statedb.AccountCommits, statedb.StorageCommits) /* concurrent */ - statedb.SnapshotCommits - statedb.TrieDBCommits)
|
||||||
blockInsertTimer.UpdateSince(start)
|
blockInsertTimer.UpdateSince(start)
|
||||||
|
|
||||||
return &blockProcessingResult{usedGas: usedGas, procTime: proctime, status: status}, nil
|
return &blockProcessingResult{usedGas: res.GasUsed, procTime: proctime, status: status}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertSideChain is called when an import batch hits upon a pruned ancestor
|
// insertSideChain is called when an import batch hits upon a pruned ancestor
|
||||||
|
|
@ -2598,7 +2655,7 @@ func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, s
|
||||||
// The method writes all (header-and-body-valid) blocks to disk, then tries to
|
// 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.
|
// switch over to the new chain if the TD exceeded the current chain.
|
||||||
// insertSideChain is only used pre-merge.
|
// insertSideChain is only used pre-merge.
|
||||||
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (int, error) {
|
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, makeWitness bool) (*stateless.Witness, int, error) {
|
||||||
var (
|
var (
|
||||||
externTd *big.Int
|
externTd *big.Int
|
||||||
lastBlock = block
|
lastBlock = block
|
||||||
|
|
@ -2637,7 +2694,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
// If someone legitimately side-mines blocks, they would still be imported as usual. However,
|
// If someone legitimately side-mines blocks, they would still be imported as usual. However,
|
||||||
// we cannot risk writing unverified blocks to disk when they obviously target the pruning
|
// we cannot risk writing unverified blocks to disk when they obviously target the pruning
|
||||||
// mechanism.
|
// mechanism.
|
||||||
return it.index, errors.New("sidechain ghost-state attack")
|
return nil, it.index, errors.New("sidechain ghost-state attack")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2651,7 +2708,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
if err := bc.writeBlockWithoutState(block, externTd); err != nil {
|
if err := bc.writeBlockWithoutState(block, externTd); err != nil {
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(),
|
log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(),
|
||||||
|
|
@ -2670,19 +2727,19 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
// blocks to regenerate the required state
|
// blocks to regenerate the required state
|
||||||
reorg, err := bc.forker.ReorgNeeded(current, lastBlock.Header())
|
reorg, err := bc.forker.ReorgNeeded(current, lastBlock.Header())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid, err := bc.forker.ValidateReorg(current, headers)
|
isValid, err := bc.forker.ValidateReorg(current, headers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reorg || !isValid {
|
if !reorg || !isValid {
|
||||||
localTd := bc.GetTd(current.Hash(), current.Number.Uint64())
|
localTd := bc.GetTd(current.Hash(), current.Number.Uint64())
|
||||||
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
|
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
|
||||||
|
|
||||||
return it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
||||||
var (
|
var (
|
||||||
|
|
@ -2694,7 +2751,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
for parent != nil && !bc.HasState(parent.Root) {
|
for parent != nil && !bc.HasState(parent.Root) {
|
||||||
if bc.stateRecoverable(parent.Root) {
|
if bc.stateRecoverable(parent.Root) {
|
||||||
if err := bc.triedb.Recover(parent.Root); err != nil {
|
if err := bc.triedb.Recover(parent.Root); err != nil {
|
||||||
return 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -2705,7 +2762,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
}
|
}
|
||||||
|
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return it.index, errors.New("missing parent")
|
return nil, it.index, errors.New("missing parent")
|
||||||
}
|
}
|
||||||
// Import all the pruned blocks to make the state available
|
// Import all the pruned blocks to make the state available
|
||||||
var (
|
var (
|
||||||
|
|
@ -2725,8 +2782,8 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
// memory here.
|
// memory here.
|
||||||
if len(blocks) >= 2048 || memory > 64*1024*1024 {
|
if len(blocks) >= 2048 || memory > 64*1024*1024 {
|
||||||
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
|
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
|
||||||
if _, err := bc.insertChain(blocks, true); err != nil {
|
if _, _, err := bc.insertChain(blocks, true, false); err != nil {
|
||||||
return 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks, memory = blocks[:0], 0
|
blocks, memory = blocks[:0], 0
|
||||||
|
|
@ -2734,24 +2791,23 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||||
// If the chain is terminating, stop processing blocks
|
// If the chain is terminating, stop processing blocks
|
||||||
if bc.insertStopped() {
|
if bc.insertStopped() {
|
||||||
log.Debug("Abort during blocks processing")
|
log.Debug("Abort during blocks processing")
|
||||||
return 0, nil
|
return nil, 0, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(blocks) > 0 {
|
if len(blocks) > 0 {
|
||||||
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
|
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
|
||||||
return bc.insertChain(blocks, true)
|
return bc.insertChain(blocks, true, makeWitness)
|
||||||
}
|
}
|
||||||
|
return nil, 0, nil
|
||||||
return 0, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// recoverAncestors finds the closest ancestor with available state and re-execute
|
// recoverAncestors finds the closest ancestor with available state and re-execute
|
||||||
// all the ancestor blocks since that.
|
// all the ancestor blocks since that.
|
||||||
// recoverAncestors is only used post-merge.
|
// recoverAncestors is only used post-merge.
|
||||||
// We return the hash of the latest block that we could correctly validate.
|
// We return the hash of the latest block that we could correctly validate.
|
||||||
func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error) {
|
func (bc *BlockChain) recoverAncestors(block *types.Block, makeWitness bool) (common.Hash, error) {
|
||||||
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
// Gather all the sidechain hashes (full blocks may be memory heavy)
|
||||||
var (
|
var (
|
||||||
hashes []common.Hash
|
hashes []common.Hash
|
||||||
|
|
@ -2794,7 +2850,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error)
|
||||||
} else {
|
} else {
|
||||||
b = bc.GetBlock(hashes[i], numbers[i])
|
b = bc.GetBlock(hashes[i], numbers[i])
|
||||||
}
|
}
|
||||||
if _, err := bc.insertChain(types.Blocks{b}, false); err != nil {
|
if _, _, err := bc.insertChain(types.Blocks{b}, false, makeWitness && i == 0); err != nil {
|
||||||
return b.ParentHash(), err
|
return b.ParentHash(), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3115,14 +3171,14 @@ func ExportN(w io.Writer, blocks []*types.Block) error {
|
||||||
// The key difference between the InsertChain is it won't do the canonical chain
|
// 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
|
// updating. It relies on the additional SetCanonical call to finalize the entire
|
||||||
// procedure.
|
// procedure.
|
||||||
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block) error {
|
func (bc *BlockChain) InsertBlockWithoutSetHead(block *types.Block, makeWitness bool) (*stateless.Witness, error) {
|
||||||
if !bc.chainmu.TryLock() {
|
if !bc.chainmu.TryLock() {
|
||||||
return errChainStopped
|
return nil, errChainStopped
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
|
|
||||||
_, err := bc.insertChain(types.Blocks{block}, false)
|
witness, _, err := bc.insertChain(types.Blocks{block}, false, makeWitness)
|
||||||
return err
|
return witness, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCanonical rewinds the chain to set the new head block as the specified
|
// SetCanonical rewinds the chain to set the new head block as the specified
|
||||||
|
|
@ -3136,7 +3192,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) {
|
||||||
|
|
||||||
// Re-execute the reorged chain in case the head state is missing.
|
// Re-execute the reorged chain in case the head state is missing.
|
||||||
if !bc.HasState(head.Root()) {
|
if !bc.HasState(head.Root()) {
|
||||||
if latestValidHash, err := bc.recoverAncestors(head); err != nil {
|
if latestValidHash, err := bc.recoverAncestors(head, false); err != nil {
|
||||||
return latestValidHash, err
|
return latestValidHash, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3223,7 +3279,11 @@ func (bc *BlockChain) skipBlock(err error, it *insertIterator) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reportBlock logs a bad block error.
|
// reportBlock logs a bad block error.
|
||||||
func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
|
func (bc *BlockChain) reportBlock(block *types.Block, res *ProcessResult, err error) {
|
||||||
|
var receipts types.Receipts
|
||||||
|
if res != nil {
|
||||||
|
receipts = res.Receipts
|
||||||
|
}
|
||||||
rawdb.WriteBadBlock(bc.db, block)
|
rawdb.WriteBadBlock(bc.db, block)
|
||||||
log.Error(summarizeBadBlock(block, receipts, bc.Config(), err))
|
log.Error(summarizeBadBlock(block, receipts, bc.Config(), err))
|
||||||
}
|
}
|
||||||
|
|
@ -3274,7 +3334,6 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header) (int, error) {
|
||||||
}
|
}
|
||||||
defer bc.chainmu.Unlock()
|
defer bc.chainmu.Unlock()
|
||||||
_, err := bc.hc.InsertHeaderChain(chain, start, bc.forker)
|
_, err := bc.hc.InsertHeaderChain(chain, start, bc.forker)
|
||||||
|
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,7 @@ func (bc *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||||
|
|
||||||
// HasState checks if state trie is fully present in the database or not.
|
// HasState checks if state trie is fully present in the database or not.
|
||||||
func (bc *BlockChain) HasState(hash common.Hash) bool {
|
func (bc *BlockChain) HasState(hash common.Hash) bool {
|
||||||
_, err := bc.stateCache.OpenTrie(hash)
|
_, err := bc.statedb.OpenTrie(hash)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,12 +377,9 @@ func (bc *BlockChain) stateRecoverable(root common.Hash) bool {
|
||||||
// If the code doesn't exist in the in-memory cache, check the storage with
|
// If the code doesn't exist in the in-memory cache, check the storage with
|
||||||
// new code scheme.
|
// new code scheme.
|
||||||
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
|
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
|
||||||
type codeReader interface {
|
|
||||||
ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error)
|
|
||||||
}
|
|
||||||
// TODO(rjl493456442) The associated account address is also required
|
// TODO(rjl493456442) The associated account address is also required
|
||||||
// in Verkle scheme. Fix it once snap-sync is supported for Verkle.
|
// in Verkle scheme. Fix it once snap-sync is supported for Verkle.
|
||||||
return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Address{}, hash)
|
return bc.statedb.ContractCodeWithPrefix(common.Address{}, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// State returns a new mutable state based on the current HEAD block.
|
// State returns a new mutable state based on the current HEAD block.
|
||||||
|
|
@ -392,7 +389,7 @@ func (bc *BlockChain) State() (*state.StateDB, error) {
|
||||||
|
|
||||||
// StateAt returns a new mutable state based on a particular point in time.
|
// StateAt returns a new mutable state based on a particular point in time.
|
||||||
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
||||||
return state.New(root, bc.stateCache, bc.snaps)
|
return state.New(root, bc.statedb)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config retrieves the chain's fork configuration.
|
// Config retrieves the chain's fork configuration.
|
||||||
|
|
@ -418,7 +415,7 @@ func (bc *BlockChain) Processor() Processor {
|
||||||
|
|
||||||
// StateCache returns the caching database underpinning the blockchain instance.
|
// StateCache returns the caching database underpinning the blockchain instance.
|
||||||
func (bc *BlockChain) StateCache() state.Database {
|
func (bc *BlockChain) StateCache() state.Database {
|
||||||
return bc.stateCache
|
return bc.statedb
|
||||||
}
|
}
|
||||||
|
|
||||||
// GasLimit returns the gas limit of the current HEAD block.
|
// GasLimit returns the gas limit of the current HEAD block.
|
||||||
|
|
|
||||||
|
|
@ -1797,8 +1797,8 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||||
|
|
||||||
func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme string) {
|
func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme string) {
|
||||||
// It's hard to follow the test case, visualize the input
|
// It's hard to follow the test case, visualize the input
|
||||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||||
// fmt.Println(tt.dump(true))
|
// fmt.Println(tt.dump(false))
|
||||||
|
|
||||||
// Create a temporary persistent database
|
// Create a temporary persistent database
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
|
|
@ -1952,7 +1952,7 @@ func TestIssue23496(t *testing.T) {
|
||||||
|
|
||||||
func testIssue23496(t *testing.T, scheme string) {
|
func testIssue23496(t *testing.T, scheme string) {
|
||||||
// It's hard to follow the test case, visualize the input
|
// It's hard to follow the test case, visualize the input
|
||||||
//log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||||
|
|
||||||
// Create a temporary persistent database
|
// Create a temporary persistent database
|
||||||
datadir := t.TempDir()
|
datadir := t.TempDir()
|
||||||
|
|
|
||||||
|
|
@ -1961,7 +1961,7 @@ func testSetHead(t *testing.T, tt *rewindTest, snapshots bool) {
|
||||||
|
|
||||||
func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme string) {
|
func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme string) {
|
||||||
// It's hard to follow the test case, visualize the input
|
// It's hard to follow the test case, visualize the input
|
||||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||||
// fmt.Println(tt.dump(false))
|
// fmt.Println(tt.dump(false))
|
||||||
|
|
||||||
// Create a temporary persistent database
|
// Create a temporary persistent database
|
||||||
|
|
@ -2042,7 +2042,7 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||||
dbconfig.HashDB = hashdb.Defaults
|
dbconfig.HashDB = hashdb.Defaults
|
||||||
}
|
}
|
||||||
chain.triedb = triedb.NewDatabase(chain.db, dbconfig)
|
chain.triedb = triedb.NewDatabase(chain.db, dbconfig)
|
||||||
chain.stateCache = state.NewDatabaseWithNodeDB(chain.db, chain.triedb)
|
chain.statedb = state.NewDatabase(chain.triedb, chain.snaps)
|
||||||
|
|
||||||
// Force run a freeze cycle
|
// Force run a freeze cycle
|
||||||
type freezer interface {
|
type freezer interface {
|
||||||
|
|
|
||||||
|
|
@ -226,8 +226,8 @@ type snapshotTest struct {
|
||||||
|
|
||||||
func (snaptest *snapshotTest) test(t *testing.T) {
|
func (snaptest *snapshotTest) test(t *testing.T) {
|
||||||
// It's hard to follow the test case, visualize the input
|
// It's hard to follow the test case, visualize the input
|
||||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||||
// fmt.Println(tt.dump())
|
// fmt.Println(snaptest.dump())
|
||||||
chain, blocks := snaptest.prepare(t)
|
chain, blocks := snaptest.prepare(t)
|
||||||
|
|
||||||
// Restart the chain normally
|
// Restart the chain normally
|
||||||
|
|
@ -249,8 +249,8 @@ type crashSnapshotTest struct {
|
||||||
|
|
||||||
func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
func (snaptest *crashSnapshotTest) test(t *testing.T) {
|
||||||
// It's hard to follow the test case, visualize the input
|
// It's hard to follow the test case, visualize the input
|
||||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||||
// fmt.Println(tt.dump())
|
// fmt.Println(snaptest.dump())
|
||||||
chain, blocks := snaptest.prepare(t)
|
chain, blocks := snaptest.prepare(t)
|
||||||
|
|
||||||
// Pull the plug on the database, simulating a hard crash
|
// Pull the plug on the database, simulating a hard crash
|
||||||
|
|
@ -303,8 +303,8 @@ type gappedSnapshotTest struct {
|
||||||
|
|
||||||
func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
func (snaptest *gappedSnapshotTest) test(t *testing.T) {
|
||||||
// It's hard to follow the test case, visualize the input
|
// It's hard to follow the test case, visualize the input
|
||||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||||
// fmt.Println(tt.dump())
|
// fmt.Println(snaptest.dump())
|
||||||
chain, blocks := snaptest.prepare(t)
|
chain, blocks := snaptest.prepare(t)
|
||||||
|
|
||||||
// Insert blocks without enabling snapshot if gapping is required.
|
// Insert blocks without enabling snapshot if gapping is required.
|
||||||
|
|
@ -347,8 +347,8 @@ type setHeadSnapshotTest struct {
|
||||||
|
|
||||||
func (snaptest *setHeadSnapshotTest) test(t *testing.T) {
|
func (snaptest *setHeadSnapshotTest) test(t *testing.T) {
|
||||||
// It's hard to follow the test case, visualize the input
|
// It's hard to follow the test case, visualize the input
|
||||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||||
// fmt.Println(tt.dump())
|
// fmt.Println(snaptest.dump())
|
||||||
chain, blocks := snaptest.prepare(t)
|
chain, blocks := snaptest.prepare(t)
|
||||||
|
|
||||||
// Rewind the chain if setHead operation is required.
|
// Rewind the chain if setHead operation is required.
|
||||||
|
|
@ -376,8 +376,8 @@ type wipeCrashSnapshotTest struct {
|
||||||
|
|
||||||
func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
func (snaptest *wipeCrashSnapshotTest) test(t *testing.T) {
|
||||||
// It's hard to follow the test case, visualize the input
|
// It's hard to follow the test case, visualize the input
|
||||||
// log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||||
// fmt.Println(tt.dump())
|
// fmt.Println(snaptest.dump())
|
||||||
chain, blocks := snaptest.prepare(t)
|
chain, blocks := snaptest.prepare(t)
|
||||||
|
|
||||||
// Firstly, stop the chain properly, with all snapshot journal
|
// Firstly, stop the chain properly, with all snapshot journal
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,12 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -170,15 +170,18 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
statedb, err := state.New(blockchain.GetBlockByHash(block.ParentHash()).Root(), blockchain.statedb)
|
||||||
receipts, _, usedGas, statedb, _, err := blockchain.ProcessBlock(block, blockchain.GetBlockByHash(block.ParentHash()).Header())
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
blockchain.reportBlock(block, receipts, err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = blockchain.validator.ValidateState(block, statedb, receipts, usedGas, false); err != nil {
|
res, err := blockchain.processor.Process(block, statedb, vm.Config{})
|
||||||
blockchain.reportBlock(block, receipts, err)
|
if err != nil {
|
||||||
|
blockchain.reportBlock(block, res, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = blockchain.validator.ValidateState(block, statedb, res, false)
|
||||||
|
if err != nil {
|
||||||
|
blockchain.reportBlock(block, res, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,8 +223,8 @@ func testParallelBlockChainImport(t *testing.T, scheme string) {
|
||||||
type AlwaysFailParallelStateProcessor struct {
|
type AlwaysFailParallelStateProcessor struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *AlwaysFailParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error) {
|
func (p *AlwaysFailParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
|
||||||
return nil, nil, 0, errors.New("always fail")
|
return nil, errors.New("always fail")
|
||||||
}
|
}
|
||||||
|
|
||||||
type SlowSerialStateProcessor struct {
|
type SlowSerialStateProcessor struct {
|
||||||
|
|
@ -232,9 +235,9 @@ func NewSlowSerialStateProcessor(s Processor) *SlowSerialStateProcessor {
|
||||||
return &SlowSerialStateProcessor{s: s}
|
return &SlowSerialStateProcessor{s: s}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *SlowSerialStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error) {
|
func (p *SlowSerialStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
return p.s.Process(block, statedb, cfg, interruptCtx)
|
return p.s.Process(block, statedb, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSuccessfulBlockImportParallelFailed(t *testing.T) {
|
func TestSuccessfulBlockImportParallelFailed(t *testing.T) {
|
||||||
|
|
@ -1437,7 +1440,6 @@ func testSideLogRebirth(t *testing.T, scheme string) {
|
||||||
if _, err := blockchain.InsertChain(sideChain); err != nil {
|
if _, err := blockchain.InsertChain(sideChain); err != nil {
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
||||||
|
|
||||||
// Generate a new block based on side chain.
|
// Generate a new block based on side chain.
|
||||||
|
|
@ -1445,7 +1447,6 @@ func testSideLogRebirth(t *testing.T, scheme string) {
|
||||||
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
|
checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2000,7 +2001,6 @@ func testLargeReorgTrieGC(t *testing.T, scheme string) {
|
||||||
if _, err := chain.InsertChain(competitor[:len(competitor)-2]); err != nil {
|
if _, err := chain.InsertChain(competitor[:len(competitor)-2]); err != nil {
|
||||||
t.Fatalf("failed to insert competitor chain: %v", err)
|
t.Fatalf("failed to insert competitor chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, block := range competitor[:len(competitor)-2] {
|
for i, block := range competitor[:len(competitor)-2] {
|
||||||
if chain.HasState(block.Root()) {
|
if chain.HasState(block.Root()) {
|
||||||
t.Fatalf("competitor %d: low TD chain became processed", i)
|
t.Fatalf("competitor %d: low TD chain became processed", i)
|
||||||
|
|
@ -2371,9 +2371,9 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||||
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
|
// [ Cn, Cn+1, Cc, Sn+3 ... Sm]
|
||||||
// ^ ^ ^ pruned
|
// ^ ^ ^ pruned
|
||||||
func TestPrunedImportSide(t *testing.T) {
|
func TestPrunedImportSide(t *testing.T) {
|
||||||
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
|
// glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
|
||||||
// glogger.Verbosity(3)
|
// glogger.Verbosity(3)
|
||||||
//log.Root().SetHandler(log.Handler(glogger))
|
// log.SetDefault(log.NewLogger(glogger))
|
||||||
testSideImport(t, 3, 3, -1)
|
testSideImport(t, 3, 3, -1)
|
||||||
testSideImport(t, 3, -3, -1)
|
testSideImport(t, 3, -3, -1)
|
||||||
testSideImport(t, 10, 0, -1)
|
testSideImport(t, 10, 0, -1)
|
||||||
|
|
@ -2382,9 +2382,9 @@ func TestPrunedImportSide(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPrunedImportSideWithMerging(t *testing.T) {
|
func TestPrunedImportSideWithMerging(t *testing.T) {
|
||||||
//glogger := log.NewGlogHandler(log.StreamHandler(os.Stdout, log.TerminalFormat(false)))
|
// glogger := log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
|
||||||
// glogger.Verbosity(3)
|
// glogger.Verbosity(3)
|
||||||
//log.Root().SetHandler(log.Handler(glogger))
|
// log.SetDefault(log.NewLogger(glogger))
|
||||||
testSideImport(t, 3, 3, 0)
|
testSideImport(t, 3, 3, 0)
|
||||||
testSideImport(t, 3, -3, 0)
|
testSideImport(t, 3, -3, 0)
|
||||||
testSideImport(t, 10, 0, 0)
|
testSideImport(t, 10, 0, 0)
|
||||||
|
|
@ -3044,7 +3044,21 @@ func testSideImportPrunedBlocks(t *testing.T, scheme string) {
|
||||||
// Generate and import the canonical chain
|
// Generate and import the canonical chain
|
||||||
_, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*state.TriesInMemory, nil)
|
_, blocks, _ := GenerateChainWithGenesis(genesis, engine, 2*state.TriesInMemory, nil)
|
||||||
|
|
||||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil, nil)
|
// Construct a database with freezer enabled
|
||||||
|
datadir := t.TempDir()
|
||||||
|
ancient := path.Join(datadir, "ancient")
|
||||||
|
|
||||||
|
db, err := rawdb.Open(rawdb.OpenOptions{
|
||||||
|
Directory: datadir,
|
||||||
|
AncientsDirectory: ancient,
|
||||||
|
Ephemeral: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create persistent database: %v", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -3967,7 +3981,7 @@ func TestSetCanonical(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSetCanonical(t *testing.T, scheme string) {
|
func testSetCanonical(t *testing.T, scheme string) {
|
||||||
//log.Root().SetHandler(log.LvlFilterHandler(log.LvlDebug, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
|
// log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true)))
|
||||||
|
|
||||||
var (
|
var (
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
|
@ -4019,7 +4033,7 @@ func testSetCanonical(t *testing.T, scheme string) {
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, block := range side {
|
for _, block := range side {
|
||||||
err := chain.InsertBlockWithoutSetHead(block)
|
_, err := chain.InsertBlockWithoutSetHead(block, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to insert into chain: %v", err)
|
t.Fatalf("Failed to insert into chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -4578,7 +4592,6 @@ func TestEIP3651(t *testing.T) {
|
||||||
b.AddTx(tx)
|
b.AddTx(tx)
|
||||||
})
|
})
|
||||||
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Hooks()}, nil, nil, nil)
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{}, os.Stderr).Hooks()}, nil, nil, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -4615,3 +4628,90 @@ func TestEIP3651(t *testing.T) {
|
||||||
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEIP6110(t *testing.T) {
|
||||||
|
var (
|
||||||
|
engine = beacon.NewFaker()
|
||||||
|
|
||||||
|
// A sender who makes transactions, has some funds
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
addr = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
|
||||||
|
config = *params.AllEthashProtocolChanges
|
||||||
|
gspec = &Genesis{
|
||||||
|
Config: &config,
|
||||||
|
Alloc: types.GenesisAlloc{
|
||||||
|
addr: {Balance: funds},
|
||||||
|
config.DepositContractAddress: {
|
||||||
|
// Simple deposit generator, source: https://gist.github.com/lightclient/54abb2af2465d6969fa6d1920b9ad9d7
|
||||||
|
Code: common.Hex2Bytes("6080604052366103aa575f603067ffffffffffffffff811115610025576100246103ae565b5b6040519080825280601f01601f1916602001820160405280156100575781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061007d5761007c6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f602067ffffffffffffffff8111156100c7576100c66103ae565b5b6040519080825280601f01601f1916602001820160405280156100f95781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f8151811061011f5761011e6103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff811115610169576101686103ae565b5b6040519080825280601f01601f19166020018201604052801561019b5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f815181106101c1576101c06103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f606067ffffffffffffffff81111561020b5761020a6103ae565b5b6040519080825280601f01601f19166020018201604052801561023d5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610263576102626103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f600867ffffffffffffffff8111156102ad576102ac6103ae565b5b6040519080825280601f01601f1916602001820160405280156102df5781602001600182028036833780820191505090505b5090505f8054906101000a900460ff1660f81b815f81518110610305576103046103db565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690815f1a9053505f8081819054906101000a900460ff168092919061035090610441565b91906101000a81548160ff021916908360ff160217905550507f649bbc62d0e31342afea4e5cd82d4049e7e1ee912fc0889aa790803be39038c585858585856040516103a09594939291906104d9565b60405180910390a1005b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f60ff82169050919050565b5f61044b82610435565b915060ff820361045e5761045d610408565b5b600182019050919050565b5f81519050919050565b5f82825260208201905092915050565b8281835e5f83830152505050565b5f601f19601f8301169050919050565b5f6104ab82610469565b6104b58185610473565b93506104c5818560208601610483565b6104ce81610491565b840191505092915050565b5f60a0820190508181035f8301526104f181886104a1565b9050818103602083015261050581876104a1565b9050818103604083015261051981866104a1565b9050818103606083015261052d81856104a1565b9050818103608083015261054181846104a1565b9050969550505050505056fea26469706673582212208569967e58690162d7d6fe3513d07b393b4c15e70f41505cbbfd08f53eba739364736f6c63430008190033"),
|
||||||
|
Nonce: 0,
|
||||||
|
Balance: big.NewInt(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
gspec.Config.BerlinBlock = common.Big0
|
||||||
|
gspec.Config.LondonBlock = common.Big0
|
||||||
|
gspec.Config.TerminalTotalDifficulty = common.Big0
|
||||||
|
gspec.Config.TerminalTotalDifficultyPassed = true
|
||||||
|
gspec.Config.ShanghaiBlock = big.NewInt(0)
|
||||||
|
gspec.Config.CancunBlock = big.NewInt(0)
|
||||||
|
gspec.Config.PragueBlock = big.NewInt(0)
|
||||||
|
signer := types.LatestSigner(gspec.Config)
|
||||||
|
|
||||||
|
_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
txdata := &types.DynamicFeeTx{
|
||||||
|
ChainID: gspec.Config.ChainID,
|
||||||
|
Nonce: uint64(i),
|
||||||
|
To: &config.DepositContractAddress,
|
||||||
|
Gas: 500000,
|
||||||
|
GasFeeCap: newGwei(5),
|
||||||
|
GasTipCap: big.NewInt(2),
|
||||||
|
AccessList: nil,
|
||||||
|
Data: []byte{},
|
||||||
|
}
|
||||||
|
tx := types.NewTx(txdata)
|
||||||
|
tx, _ = types.SignTx(tx, signer, key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
chain, err := NewBlockChain(rawdb.NewMemoryDatabase(), nil, gspec, nil, engine, vm.Config{Tracer: logger.NewMarkdownLogger(&logger.Config{DisableStack: true}, os.Stderr).Hooks()}, nil, nil, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
|
}
|
||||||
|
defer chain.Stop()
|
||||||
|
if n, err := chain.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
block := chain.GetBlockByNumber(1)
|
||||||
|
if len(block.Requests()) != 5 {
|
||||||
|
t.Fatalf("failed to retrieve deposits: have %d, want %d", len(block.Requests()), 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify each index is correct.
|
||||||
|
for want, req := range block.Requests() {
|
||||||
|
d, ok := req.Inner().(*types.Deposit)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("expected deposit object")
|
||||||
|
}
|
||||||
|
if got := int(d.PublicKey[0]); got != want {
|
||||||
|
t.Fatalf("invalid pubkey: have %d, want %d", got, want)
|
||||||
|
}
|
||||||
|
if got := int(d.WithdrawalCredentials[0]); got != want {
|
||||||
|
t.Fatalf("invalid withdrawal credentials: have %d, want %d", got, want)
|
||||||
|
}
|
||||||
|
if d.Amount != uint64(want) {
|
||||||
|
t.Fatalf("invalid amounbt: have %d, want %d", d.Amount, want)
|
||||||
|
}
|
||||||
|
if got := int(d.Signature[0]); got != want {
|
||||||
|
t.Fatalf("invalid signature: have %d, want %d", got, want)
|
||||||
|
}
|
||||||
|
if d.Index != uint64(want) {
|
||||||
|
t.Fatalf("invalid index: have %d, want %d", d.Index, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -359,7 +359,18 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
gen(i, b)
|
gen(i, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals}
|
var requests types.Requests
|
||||||
|
if config.IsPrague(b.header.Number) {
|
||||||
|
for _, r := range b.receipts {
|
||||||
|
d, err := ParseDepositLogs(r.Logs, config)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to parse deposit log: %v", err))
|
||||||
|
}
|
||||||
|
requests = append(requests, d...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body := types.Body{Transactions: b.txs, Uncles: b.uncles, Withdrawals: b.withdrawals, Requests: requests}
|
||||||
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts)
|
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, &body, b.receipts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -381,7 +392,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
statedb, err := state.New(parent.Root(), state.NewDatabaseWithNodeDB(db, triedb), nil)
|
statedb, err := state.New(parent.Root(), state.NewDatabase(triedb, nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -482,15 +493,14 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
|
||||||
panic(fmt.Sprintf("trie write error: %v", err))
|
panic(fmt.Sprintf("trie write error: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO uncomment when proof generation is merged
|
proofs = append(proofs, block.ExecutionWitness().VerkleProof)
|
||||||
// proofs = append(proofs, block.ExecutionWitness().VerkleProof)
|
keyvals = append(keyvals, block.ExecutionWitness().StateDiff)
|
||||||
// keyvals = append(keyvals, block.ExecutionWitness().StateDiff)
|
|
||||||
|
|
||||||
return block, b.receipts
|
return block, b.receipts
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
statedb, err := state.New(parent.Root(), state.NewDatabaseWithNodeDB(db, trdb), nil)
|
statedb, err := state.New(parent.Root(), state.NewDatabase(trdb, nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
Config: &proConf,
|
Config: &proConf,
|
||||||
}
|
}
|
||||||
|
|
||||||
proBc, _ := NewBlockChain(proDb, nil, progspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
proBc, _ := NewBlockChain(proDb, nil, progspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer proBc.Stop()
|
defer proBc.Stop()
|
||||||
|
|
||||||
|
|
@ -63,7 +62,6 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
Config: &conConf,
|
Config: &conConf,
|
||||||
}
|
}
|
||||||
|
|
||||||
conBc, _ := NewBlockChain(conDb, nil, congspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
conBc, _ := NewBlockChain(conDb, nil, congspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer conBc.Stop()
|
defer conBc.Stop()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,25 +81,6 @@ func TestCreation(t *testing.T) {
|
||||||
// {50000000, 2000000000, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}}, // Future Cancun block
|
// {50000000, 2000000000, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}}, // Future Cancun block
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// Goerli test cases
|
|
||||||
{
|
|
||||||
params.GoerliChainConfig,
|
|
||||||
core.DefaultGoerliGenesisBlock().ToBlock(),
|
|
||||||
[]testcase{
|
|
||||||
{0, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Unsynced, last Frontier, Homestead, Tangerine, Spurious, Byzantium, Constantinople and first Petersburg block
|
|
||||||
{1561650, 0, ID{Hash: checksumToBytes(0xa3f5ab08), Next: 1561651}}, // Last Petersburg block
|
|
||||||
{1561651, 0, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // First Istanbul block
|
|
||||||
{4460643, 0, ID{Hash: checksumToBytes(0xc25efa5c), Next: 4460644}}, // Last Istanbul block
|
|
||||||
{4460644, 0, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // First Berlin block
|
|
||||||
{5000000, 0, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // Last Berlin block
|
|
||||||
// {5062605, 0, ID{Hash: checksumToBytes(0xB8C6299D), Next: 1678832736}}, // First London block
|
|
||||||
// {6000000, 1678832735, ID{Hash: checksumToBytes(0xB8C6299D), Next: 1678832736}}, // Last London block
|
|
||||||
// {6000001, 1678832736, ID{Hash: checksumToBytes(0xf9843abf), Next: 1705473120}}, // First Shanghai block
|
|
||||||
// {6500002, 1705473119, ID{Hash: checksumToBytes(0xf9843abf), Next: 1705473120}}, // Last Shanghai block
|
|
||||||
// {6500003, 1705473120, ID{Hash: checksumToBytes(0x70cc14e2), Next: 0}}, // First Cancun block
|
|
||||||
// {6500003, 2705473120, ID{Hash: checksumToBytes(0x70cc14e2), Next: 0}}, // Future Cancun block
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// Sepolia test cases
|
// Sepolia test cases
|
||||||
{
|
{
|
||||||
params.SepoliaChainConfig,
|
params.SepoliaChainConfig,
|
||||||
|
|
|
||||||
101
core/genesis.go
101
core/genesis.go
|
|
@ -141,8 +141,8 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
||||||
}
|
}
|
||||||
// Create an ephemeral in-memory database for computing hash,
|
// Create an ephemeral in-memory database for computing hash,
|
||||||
// all the derived states will be discarded to not pollute disk.
|
// all the derived states will be discarded to not pollute disk.
|
||||||
db := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), config)
|
db := rawdb.NewMemoryDatabase()
|
||||||
statedb, err := state.New(types.EmptyRootHash, db, nil)
|
statedb, err := state.New(types.EmptyRootHash, state.NewDatabase(triedb.NewDatabase(db, config), nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -161,13 +161,12 @@ func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
||||||
return statedb.Commit(0, false)
|
return statedb.Commit(0, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// flushAlloc is very similar with hash, but the main difference is all the generated
|
// flushAlloc is very similar with hash, but the main difference is all the
|
||||||
// states will be persisted into the given database. Also, the genesis state
|
// generated states will be persisted into the given database.
|
||||||
// specification will be flushed as well.
|
func flushAlloc(ga *types.GenesisAlloc, triedb *triedb.Database) (common.Hash, error) {
|
||||||
func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Database, blockhash common.Hash) error {
|
statedb, err := state.New(types.EmptyRootHash, state.NewDatabase(triedb, nil))
|
||||||
statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for addr, account := range *ga {
|
for addr, account := range *ga {
|
||||||
|
|
@ -185,23 +184,15 @@ func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Databa
|
||||||
}
|
}
|
||||||
root, err := statedb.Commit(0, false)
|
root, err := statedb.Commit(0, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
// Commit newly generated states into disk if it's not empty.
|
// Commit newly generated states into disk if it's not empty.
|
||||||
if root != types.EmptyRootHash {
|
if root != types.EmptyRootHash {
|
||||||
if err := triedb.Commit(root, true); err != nil {
|
if err := triedb.Commit(root, true); err != nil {
|
||||||
return err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Marshal the genesis state specification and persist.
|
return root, nil
|
||||||
blob, err := json.Marshal(ga)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rawdb.WriteGenesisStateSpec(db, blockhash, blob)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.GenesisAlloc, err error) {
|
func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.GenesisAlloc, err error) {
|
||||||
|
|
@ -223,8 +214,6 @@ func getGenesisState(db ethdb.Database, blockhash common.Hash) (alloc types.Gene
|
||||||
switch blockhash {
|
switch blockhash {
|
||||||
case params.MainnetGenesisHash:
|
case params.MainnetGenesisHash:
|
||||||
genesis = DefaultGenesisBlock()
|
genesis = DefaultGenesisBlock()
|
||||||
case params.GoerliGenesisHash:
|
|
||||||
genesis = DefaultGoerliGenesisBlock()
|
|
||||||
case params.SepoliaGenesisHash:
|
case params.SepoliaGenesisHash:
|
||||||
genesis = DefaultSepoliaGenesisBlock()
|
genesis = DefaultSepoliaGenesisBlock()
|
||||||
case params.HoleskyGenesisHash:
|
case params.HoleskyGenesisHash:
|
||||||
|
|
@ -464,7 +453,11 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
return g.toBlockWithRoot(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
// toBlockWithRoot constructs the genesis block with the given genesis state root.
|
||||||
|
func (g *Genesis) toBlockWithRoot(root common.Hash) *types.Block {
|
||||||
head := &types.Header{
|
head := &types.Header{
|
||||||
Number: new(big.Int).SetUint64(g.Number),
|
Number: new(big.Int).SetUint64(g.Number),
|
||||||
Nonce: types.EncodeNonce(g.Nonce),
|
Nonce: types.EncodeNonce(g.Nonce),
|
||||||
|
|
@ -494,28 +487,43 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||||
head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee)
|
head.BaseFee = new(big.Int).SetUint64(params.InitialBaseFee)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var (
|
||||||
var withdrawals []*types.Withdrawal
|
withdrawals []*types.Withdrawal
|
||||||
|
requests types.Requests
|
||||||
|
)
|
||||||
if conf := g.Config; conf != nil {
|
if conf := g.Config; conf != nil {
|
||||||
num := big.NewInt(int64(g.Number))
|
num := big.NewInt(int64(g.Number))
|
||||||
if conf.IsShanghai(num) {
|
if conf.IsShanghai(num) {
|
||||||
head.WithdrawalsHash = nil
|
head.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
||||||
withdrawals = nil
|
withdrawals = make([]*types.Withdrawal, 0)
|
||||||
}
|
}
|
||||||
if conf.IsCancun(num) {
|
if conf.IsCancun(num) {
|
||||||
head.ParentBeaconRoot = nil
|
// EIP-4788: The parentBeaconBlockRoot of the genesis block is always
|
||||||
head.ExcessBlobGas = nil
|
// the zero hash. This is because the genesis block does not have a parent
|
||||||
head.BlobGasUsed = nil
|
// by definition.
|
||||||
|
head.ParentBeaconRoot = new(common.Hash)
|
||||||
|
// EIP-4844 fields
|
||||||
|
head.ExcessBlobGas = g.ExcessBlobGas
|
||||||
|
head.BlobGasUsed = g.BlobGasUsed
|
||||||
|
if head.ExcessBlobGas == nil {
|
||||||
|
head.ExcessBlobGas = new(uint64)
|
||||||
|
}
|
||||||
|
if head.BlobGasUsed == nil {
|
||||||
|
head.BlobGasUsed = new(uint64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals}, nil, trie.NewStackTrie(nil))
|
if conf.IsPrague(num) {
|
||||||
|
head.RequestsHash = &types.EmptyRequestsHash
|
||||||
|
requests = make(types.Requests, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.NewBlock(head, &types.Body{Withdrawals: withdrawals, Requests: requests}, nil, trie.NewStackTrie(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit writes the block and state of a genesis specification to the database.
|
// Commit writes the block and state of a genesis specification to the database.
|
||||||
// The block is committed as the canonical head block.
|
// The block is committed as the canonical head block.
|
||||||
func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Block, error) {
|
func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Block, error) {
|
||||||
block := g.ToBlock()
|
if g.Number != 0 {
|
||||||
if block.Number().Sign() != 0 {
|
|
||||||
return nil, errors.New("can't commit genesis block with number > 0")
|
return nil, errors.New("can't commit genesis block with number > 0")
|
||||||
}
|
}
|
||||||
config := g.Config
|
config := g.Config
|
||||||
|
|
@ -525,15 +533,22 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo
|
||||||
if err := config.CheckConfigForkOrder(); err != nil {
|
if err := config.CheckConfigForkOrder(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if config.Clique != nil && len(block.Extra()) < 32+crypto.SignatureLength {
|
if config.Clique != nil && len(g.ExtraData) < 32+crypto.SignatureLength {
|
||||||
return nil, errors.New("can't start clique chain without signers")
|
return nil, errors.New("can't start clique chain without signers")
|
||||||
}
|
}
|
||||||
// All the checks has passed, flushAlloc the states derived from the genesis
|
// flush the data to disk and compute the state root
|
||||||
// specification as well as the specification itself into the provided
|
root, err := flushAlloc(&g.Alloc, triedb)
|
||||||
// database.
|
if err != nil {
|
||||||
if err := flushAlloc(&g.Alloc, db, triedb, block.Hash()); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
block := g.toBlockWithRoot(root)
|
||||||
|
|
||||||
|
// Marshal the genesis state specification and persist.
|
||||||
|
blob, err := json.Marshal(g.Alloc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rawdb.WriteGenesisStateSpec(db, block.Hash(), blob)
|
||||||
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
|
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
|
||||||
rawdb.WriteBlock(db, block)
|
rawdb.WriteBlock(db, block)
|
||||||
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
|
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
|
||||||
|
|
@ -577,18 +592,6 @@ func DefaultGenesisBlock() *Genesis {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultGoerliGenesisBlock returns the Görli network genesis block.
|
|
||||||
func DefaultGoerliGenesisBlock() *Genesis {
|
|
||||||
return &Genesis{
|
|
||||||
Config: params.GoerliChainConfig,
|
|
||||||
Timestamp: 1548854791,
|
|
||||||
ExtraData: hexutil.MustDecode("0x22466c6578692069732061207468696e6722202d204166726900000000000000e0a2bd4258d2768837baa26a28fe71dc079f84c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
|
||||||
GasLimit: 10485760,
|
|
||||||
Difficulty: big.NewInt(1),
|
|
||||||
Alloc: decodePrealloc(goerliAllocData),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultSepoliaGenesisBlock returns the Sepolia network genesis block.
|
// DefaultSepoliaGenesisBlock returns the Sepolia network genesis block.
|
||||||
func DefaultSepoliaGenesisBlock() *Genesis {
|
func DefaultSepoliaGenesisBlock() *Genesis {
|
||||||
return &Genesis{
|
return &Genesis{
|
||||||
|
|
@ -679,6 +682,8 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
||||||
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
|
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
|
||||||
// Pre-deploy EIP-4788 system contract
|
// Pre-deploy EIP-4788 system contract
|
||||||
params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0},
|
params.BeaconRootsAddress: {Nonce: 1, Code: params.BeaconRootsCode, Balance: common.Big0},
|
||||||
|
// Pre-deploy EIP-2935 history contract.
|
||||||
|
params.HistoryStorageAddress: {Nonce: 1, Code: params.HistoryStorageCode, Balance: common.Big0},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if faucet != nil {
|
if faucet != nil {
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -35,15 +35,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInvalidCliqueConfig(t *testing.T) {
|
|
||||||
block := DefaultGoerliGenesisBlock()
|
|
||||||
block.ExtraData = []byte{}
|
|
||||||
db := rawdb.NewMemoryDatabase()
|
|
||||||
if _, err := block.Commit(db, triedb.NewDatabase(db, nil)); err == nil {
|
|
||||||
t.Fatal("Expected error on invalid clique config")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetupGenesis(t *testing.T) {
|
func TestSetupGenesis(t *testing.T) {
|
||||||
testSetupGenesis(t, rawdb.HashScheme)
|
testSetupGenesis(t, rawdb.HashScheme)
|
||||||
testSetupGenesis(t, rawdb.PathScheme)
|
testSetupGenesis(t, rawdb.PathScheme)
|
||||||
|
|
@ -106,15 +97,15 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||||
wantConfig: customg.Config,
|
wantConfig: customg.Config,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "custom block in DB, genesis == goerli",
|
name: "custom block in DB, genesis == sepolia",
|
||||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||||
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||||
customg.Commit(db, tdb)
|
customg.Commit(db, tdb)
|
||||||
return SetupGenesisBlock(db, tdb, DefaultGoerliGenesisBlock())
|
return SetupGenesisBlock(db, tdb, DefaultSepoliaGenesisBlock())
|
||||||
},
|
},
|
||||||
wantErr: &GenesisMismatchError{Stored: customghash, New: params.GoerliGenesisHash},
|
wantErr: &GenesisMismatchError{Stored: customghash, New: params.SepoliaGenesisHash},
|
||||||
wantHash: params.GoerliGenesisHash,
|
wantHash: params.SepoliaGenesisHash,
|
||||||
wantConfig: params.GoerliChainConfig,
|
wantConfig: params.SepoliaChainConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "compatible config in DB",
|
name: "compatible config in DB",
|
||||||
|
|
@ -187,7 +178,6 @@ func TestGenesisHashes(t *testing.T) {
|
||||||
want common.Hash
|
want common.Hash
|
||||||
}{
|
}{
|
||||||
{DefaultGenesisBlock(), params.MainnetGenesisHash},
|
{DefaultGenesisBlock(), params.MainnetGenesisHash},
|
||||||
{DefaultGoerliGenesisBlock(), params.GoerliGenesisHash},
|
|
||||||
{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
|
{DefaultSepoliaGenesisBlock(), params.SepoliaGenesisHash},
|
||||||
} {
|
} {
|
||||||
// Test via MustCommit
|
// Test via MustCommit
|
||||||
|
|
@ -313,7 +303,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := common.FromHex("14398d42be3394ff8d50681816a4b7bf8d8283306f577faba2d5bc57498de23b")
|
expected := common.FromHex("4a83dc39eb688dbcfaf581d60e82de18f875e38786ebce5833342011d6fef37b")
|
||||||
got := genesis.ToBlock().Root().Bytes()
|
got := genesis.ToBlock().Root().Bytes()
|
||||||
if !bytes.Equal(got, expected) {
|
if !bytes.Equal(got, expected) {
|
||||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ func (task *ExecutionTask) Execute(mvh *blockstm.MVHashMap, incarnation int) (er
|
||||||
|
|
||||||
// Apply the transaction to the current state (included in the env).
|
// Apply the transaction to the current state (included in the env).
|
||||||
if *task.shouldDelayFeeCal {
|
if *task.shouldDelayFeeCal {
|
||||||
task.result, err = ApplyMessageNoFeeBurnOrTip(evm, task.msg, new(GasPool).AddGas(task.gasLimit), nil)
|
task.result, err = ApplyMessageNoFeeBurnOrTip(evm, task.msg, new(GasPool).AddGas(task.gasLimit))
|
||||||
|
|
||||||
if task.result == nil || err != nil {
|
if task.result == nil || err != nil {
|
||||||
return blockstm.ErrExecAbortError{Dependency: task.statedb.DepTxIndex(), OriginError: err}
|
return blockstm.ErrExecAbortError{Dependency: task.statedb.DepTxIndex(), OriginError: err}
|
||||||
|
|
@ -138,7 +138,7 @@ func (task *ExecutionTask) Execute(mvh *blockstm.MVHashMap, incarnation int) (er
|
||||||
task.shouldRerunWithoutFeeDelay = true
|
task.shouldRerunWithoutFeeDelay = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
task.result, err = ApplyMessage(evm, &task.msg, new(GasPool).AddGas(task.gasLimit), nil)
|
task.result, err = ApplyMessage(evm, &task.msg, new(GasPool).AddGas(task.gasLimit))
|
||||||
}
|
}
|
||||||
|
|
||||||
if task.statedb.HadInvalidRead() || err != nil {
|
if task.statedb.HadInvalidRead() || err != nil {
|
||||||
|
|
@ -264,7 +264,7 @@ var parallelizabilityTimer = metrics.NewRegisteredTimer("block/parallelizability
|
||||||
// returns the amount of gas that was used in the process. If any of the
|
// returns the amount of gas that was used in the process. If any of the
|
||||||
// transactions failed to execute due to insufficient gas it will return an error.
|
// transactions failed to execute due to insufficient gas it will return an error.
|
||||||
// nolint:gocognit
|
// nolint:gocognit
|
||||||
func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error) {
|
func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
|
||||||
var (
|
var (
|
||||||
receipts types.Receipts
|
receipts types.Receipts
|
||||||
header = block.Header()
|
header = block.Header()
|
||||||
|
|
@ -306,7 +306,7 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
|
||||||
msg, err := TransactionToMessage(tx, types.MakeSigner(p.config, header.Number, header.Time), header.BaseFee)
|
msg, err := TransactionToMessage(tx, types.MakeSigner(p.config, header.Number, header.Time), header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("error creating message", "err", err)
|
log.Error("error creating message", "err", err)
|
||||||
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cleansdb := statedb.Copy()
|
cleansdb := statedb.Copy()
|
||||||
|
|
@ -343,8 +343,9 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
|
||||||
|
|
||||||
backupStateDB := statedb.Copy()
|
backupStateDB := statedb.Copy()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
profile := false
|
profile := false
|
||||||
result, err := blockstm.ExecuteParallel(tasks, profile, metadata, p.bc.parallelSpeculativeProcesses, interruptCtx)
|
result, err := blockstm.ExecuteParallel(tasks, profile, metadata, p.bc.parallelSpeculativeProcesses, ctx)
|
||||||
|
|
||||||
if err == nil && profile && result.Deps != nil {
|
if err == nil && profile && result.Deps != nil {
|
||||||
_, weight := result.Deps.LongestPath(*result.Stats)
|
_, weight := result.Deps.LongestPath(*result.Stats)
|
||||||
|
|
@ -378,20 +379,34 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
|
||||||
t.totalUsedGas = usedGas
|
t.totalUsedGas = usedGas
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = blockstm.ExecuteParallel(tasks, false, metadata, p.bc.parallelSpeculativeProcesses, interruptCtx)
|
_, err = blockstm.ExecuteParallel(tasks, false, metadata, p.bc.parallelSpeculativeProcesses, ctx)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, err
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read requests if Prague is enabled.
|
||||||
|
var requests types.Requests
|
||||||
|
if p.config.IsPrague(block.Number()) {
|
||||||
|
requests, err = ParseDepositLogs(allLogs, p.config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
p.engine.Finalize(p.bc, header, statedb, block.Body())
|
p.engine.Finalize(p.bc, header, statedb, block.Body())
|
||||||
|
|
||||||
return receipts, allLogs, *usedGas, nil
|
return &ProcessResult{
|
||||||
|
Receipts: receipts,
|
||||||
|
Requests: requests,
|
||||||
|
Logs: allLogs,
|
||||||
|
GasUsed: *usedGas,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetDeps(txDependency [][]uint64) map[int][]int {
|
func GetDeps(txDependency [][]uint64) map[int][]int {
|
||||||
|
|
|
||||||
|
|
@ -303,6 +303,10 @@ func ParseStateScheme(provided string, disk ethdb.Database) (string, error) {
|
||||||
log.Info("State scheme set to already existing", "scheme", stored)
|
log.Info("State scheme set to already existing", "scheme", stored)
|
||||||
return stored, nil // reuse scheme of persistent scheme
|
return stored, nil // reuse scheme of persistent scheme
|
||||||
}
|
}
|
||||||
|
// If state scheme is specified, ensure it's valid.
|
||||||
|
if provided != HashScheme && provided != PathScheme {
|
||||||
|
return "", fmt.Errorf("invalid state scheme %s", provided)
|
||||||
|
}
|
||||||
// If state scheme is specified, ensure it's compatible with
|
// If state scheme is specified, ensure it's compatible with
|
||||||
// persistent state.
|
// persistent state.
|
||||||
if stored == "" || provided == stored {
|
if stored == "" || provided == stored {
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,16 @@ func InitDatabaseFromFreezer(db ethdb.Database) {
|
||||||
log.Info("Initialized database from freezer", "blocks", frozen, "elapsed", common.PrettyDuration(time.Since(start)))
|
log.Info("Initialized database from freezer", "blocks", frozen, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adjustRangeForBor updates the range to index transactions if the ancient data was pruned before. This is
|
||||||
|
// to avoid indexing/unindexing data which is already pruned (i.e. before the `offset` block number).
|
||||||
|
func adjustRangeForBor(db ethdb.Database, from uint64) uint64 {
|
||||||
|
if offset := db.AncientOffSet(); offset > from {
|
||||||
|
from = offset
|
||||||
|
}
|
||||||
|
|
||||||
|
return from
|
||||||
|
}
|
||||||
|
|
||||||
type blockTxHashes struct {
|
type blockTxHashes struct {
|
||||||
number uint64
|
number uint64
|
||||||
hashes []common.Hash
|
hashes []common.Hash
|
||||||
|
|
@ -108,10 +118,6 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
|
||||||
rlp rlp.RawValue
|
rlp rlp.RawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
if offset := db.AncientOffSet(); offset > from {
|
|
||||||
from = offset
|
|
||||||
}
|
|
||||||
|
|
||||||
if to <= from {
|
if to <= from {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -206,6 +212,9 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
|
||||||
// There is a passed channel, the whole procedure will be interrupted if any
|
// There is a passed channel, the whole procedure will be interrupted if any
|
||||||
// signal received.
|
// signal received.
|
||||||
func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
|
func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
|
||||||
|
// Adjust range if needed
|
||||||
|
from = adjustRangeForBor(db, from)
|
||||||
|
|
||||||
// short circuit for invalid range
|
// short circuit for invalid range
|
||||||
if from >= to {
|
if from >= to {
|
||||||
return
|
return
|
||||||
|
|
@ -309,6 +318,9 @@ func indexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, inte
|
||||||
// There is a passed channel, the whole procedure will be interrupted if any
|
// There is a passed channel, the whole procedure will be interrupted if any
|
||||||
// signal received.
|
// signal received.
|
||||||
func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
|
func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
|
||||||
|
// Adjust range if needed
|
||||||
|
from = adjustRangeForBor(db, from)
|
||||||
|
|
||||||
// short circuit for invalid range
|
// short circuit for invalid range
|
||||||
if from >= to {
|
if from >= to {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -164,12 +164,6 @@ func (db *nofreezedb) ReadAncients(fn func(reader ethdb.AncientReaderOp) error)
|
||||||
return fn(db)
|
return fn(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MigrateTable processes the entries in a given table in sequence
|
|
||||||
// converting them to a new format if they're of an old format.
|
|
||||||
func (db *nofreezedb) MigrateTable(kind string, convert convertLegacyFn) error {
|
|
||||||
return errNotSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
// AncientDatadir returns an error as we don't have a backing chain freezer.
|
// AncientDatadir returns an error as we don't have a backing chain freezer.
|
||||||
func (db *nofreezedb) AncientDatadir() (string, error) {
|
func (db *nofreezedb) AncientDatadir() (string, error) {
|
||||||
return "", errNotSupported
|
return "", errNotSupported
|
||||||
|
|
|
||||||
|
|
@ -54,13 +54,11 @@ var (
|
||||||
// freezerTableSize defines the maximum size of freezer data files.
|
// freezerTableSize defines the maximum size of freezer data files.
|
||||||
const freezerTableSize = 2 * 1000 * 1000 * 1000
|
const freezerTableSize = 2 * 1000 * 1000 * 1000
|
||||||
|
|
||||||
// Freezer is a memory mapped append-only database to store immutable ordered
|
// Freezer is an append-only database to store immutable ordered data into
|
||||||
// data into flat files:
|
// flat files:
|
||||||
//
|
//
|
||||||
// - The append-only nature ensures that disk writes are minimized.
|
// - The append-only nature ensures that disk writes are minimized.
|
||||||
// - The memory mapping ensures we can max out system memory for caching without
|
// - The in-order data ensures that disk reads are always optimized.
|
||||||
// reserving it for go-ethereum. This would also reduce the memory requirements
|
|
||||||
// of Geth, and thus also GC overhead.
|
|
||||||
type Freezer struct {
|
type Freezer struct {
|
||||||
frozen atomic.Uint64 // Number of items already frozen
|
frozen atomic.Uint64 // Number of items already frozen
|
||||||
tail atomic.Uint64 // Number of the first stored item in the freezer
|
tail atomic.Uint64 // Number of the first stored item in the freezer
|
||||||
|
|
@ -162,7 +160,7 @@ func NewFreezer(datadir string, namespace string, readonly bool, offset uint64,
|
||||||
return freezer, nil
|
return freezer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close terminates the chain freezer, unmapping all the data files.
|
// Close terminates the chain freezer, closing all the data files.
|
||||||
func (f *Freezer) Close() error {
|
func (f *Freezer) Close() error {
|
||||||
f.writeLock.Lock()
|
f.writeLock.Lock()
|
||||||
defer f.writeLock.Unlock()
|
defer f.writeLock.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -413,13 +413,6 @@ func (f *MemoryFreezer) Sync() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MigrateTable processes and migrates entries of a given table to a new format.
|
|
||||||
// The second argument is a function that takes a raw entry and returns it
|
|
||||||
// in the newest format.
|
|
||||||
func (f *MemoryFreezer) MigrateTable(string, func([]byte) ([]byte, error)) error {
|
|
||||||
return errors.New("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close releases all the sources held by the memory freezer. It will panic if
|
// Close releases all the sources held by the memory freezer. It will panic if
|
||||||
// any following invocation is made to a closed freezer.
|
// any following invocation is made to a closed freezer.
|
||||||
func (f *MemoryFreezer) Close() error {
|
func (f *MemoryFreezer) Close() error {
|
||||||
|
|
|
||||||
|
|
@ -219,15 +219,6 @@ func (f *resettableFreezer) Sync() error {
|
||||||
return f.freezer.Sync()
|
return f.freezer.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MigrateTable processes the entries in a given table in sequence
|
|
||||||
// converting them to a new format if they're of an old format.
|
|
||||||
func (f *resettableFreezer) MigrateTable(kind string, convert convertLegacyFn) error {
|
|
||||||
f.lock.RLock()
|
|
||||||
defer f.lock.RUnlock()
|
|
||||||
|
|
||||||
return f.freezer.MigrateTable(kind, convert)
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup removes the directory located in the specified path
|
// cleanup removes the directory located in the specified path
|
||||||
// has the name with deletion marker suffix.
|
// has the name with deletion marker suffix.
|
||||||
func cleanup(path string) error {
|
func cleanup(path string) error {
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -418,99 +416,6 @@ func checkAncientCount(t *testing.T, f *Freezer, kind string, n uint64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRenameWindows(t *testing.T) {
|
|
||||||
var (
|
|
||||||
fname = "file.bin"
|
|
||||||
fname2 = "file2.bin"
|
|
||||||
data = []byte{1, 2, 3, 4}
|
|
||||||
data2 = []byte{2, 3, 4, 5}
|
|
||||||
data3 = []byte{3, 5, 6, 7}
|
|
||||||
dataLen = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create 2 temp dirs
|
|
||||||
dir1 := t.TempDir()
|
|
||||||
dir2 := t.TempDir()
|
|
||||||
|
|
||||||
// Create file in dir1 and fill with data
|
|
||||||
f, err := os.Create(filepath.Join(dir1, fname))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
f2, err := os.Create(filepath.Join(dir1, fname2))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
f3, err := os.Create(filepath.Join(dir2, fname2))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := f.Write(data); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := f2.Write(data2); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := f3.Write(data3); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f2.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f3.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Rename(f.Name(), filepath.Join(dir2, fname)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := os.Rename(f2.Name(), filepath.Join(dir2, fname2)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check file contents
|
|
||||||
f, err = os.Open(filepath.Join(dir2, fname))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer f.Close()
|
|
||||||
defer os.Remove(f.Name())
|
|
||||||
|
|
||||||
buf := make([]byte, dataLen)
|
|
||||||
if _, err := f.Read(buf); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(buf, data) {
|
|
||||||
t.Errorf("unexpected file contents. Got %v\n", buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
f, err = os.Open(filepath.Join(dir2, fname2))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer f.Close()
|
|
||||||
defer os.Remove(f.Name())
|
|
||||||
|
|
||||||
if _, err := f.Read(buf); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(buf, data2) {
|
|
||||||
t.Errorf("unexpected file contents. Got %v\n", buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFreezerCloseSync(t *testing.T) {
|
func TestFreezerCloseSync(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,12 +123,6 @@ func (t *table) Sync() error {
|
||||||
return t.db.Sync()
|
return t.db.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MigrateTable processes the entries in a given table in sequence
|
|
||||||
// converting them to a new format if they're of an old format.
|
|
||||||
func (t *table) MigrateTable(kind string, convert convertLegacyFn) error {
|
|
||||||
return t.db.MigrateTable(kind, convert)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AncientDatadir returns the ancient datadir of the underlying database.
|
// AncientDatadir returns the ancient datadir of the underlying database.
|
||||||
func (t *table) AncientDatadir() (string, error) {
|
func (t *table) AncientDatadir() (string, error) {
|
||||||
return t.db.AncientDatadir()
|
return t.db.AncientDatadir()
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ import (
|
||||||
|
|
||||||
// mode specifies how a tree location has been accessed
|
// mode specifies how a tree location has been accessed
|
||||||
// for the byte value:
|
// for the byte value:
|
||||||
// * the first bit is set if the branch has been edited
|
// * the first bit is set if the branch has been read
|
||||||
// * the second bit is set if the branch has been read
|
// * the second bit is set if the branch has been edited
|
||||||
type mode byte
|
type mode byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -94,11 +94,8 @@ func (ae *AccessEvents) Copy() *AccessEvents {
|
||||||
// member fields of an account.
|
// member fields of an account.
|
||||||
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite)
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite)
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite)
|
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite)
|
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite)
|
|
||||||
return gas
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,8 +104,7 @@ func (ae *AccessEvents) AddAccount(addr common.Address, isWrite bool) uint64 {
|
||||||
// call to that account.
|
// call to that account.
|
||||||
func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.VersionLeafKey, false)
|
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
||||||
gas += ae.touchAddressAndChargeGas(destination, zeroTreeIndex, utils.CodeSizeLeafKey, false)
|
|
||||||
return gas
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,41 +112,43 @@ func (ae *AccessEvents) MessageCallGas(destination common.Address) uint64 {
|
||||||
// cold balance member fields of the caller and the callee accounts.
|
// cold balance member fields of the caller and the callee accounts.
|
||||||
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 {
|
func (ae *AccessEvents) ValueTransferGas(callerAddr, targetAddr common.Address) uint64 {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
gas += ae.touchAddressAndChargeGas(callerAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||||
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
gas += ae.touchAddressAndChargeGas(targetAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||||
|
return gas
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContractCreateCPreheck charges access costs before
|
||||||
|
// a contract creation is initiated. It is just reads, because the
|
||||||
|
// address collision is done before the transfer, and so no write
|
||||||
|
// are guaranteed to happen at this point.
|
||||||
|
func (ae *AccessEvents) ContractCreatePreCheckGas(addr common.Address) uint64 {
|
||||||
|
var gas uint64
|
||||||
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, false)
|
||||||
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
||||||
return gas
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractCreateInitGas returns the access gas costs for the initialization of
|
// ContractCreateInitGas returns the access gas costs for the initialization of
|
||||||
// a contract creation.
|
// a contract creation.
|
||||||
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address, createSendsValue bool) uint64 {
|
func (ae *AccessEvents) ContractCreateInitGas(addr common.Address) uint64 {
|
||||||
var gas uint64
|
var gas uint64
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, true)
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, true)
|
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, true)
|
||||||
if createSendsValue {
|
|
||||||
gas += ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
|
||||||
}
|
|
||||||
return gas
|
return gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
// AddTxOrigin adds the member fields of the sender account to the access event list,
|
||||||
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
func (ae *AccessEvents) AddTxOrigin(originAddr common.Address) {
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.VersionLeafKey, false)
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BasicDataLeafKey, true)
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.BalanceLeafKey, true)
|
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.NonceLeafKey, true)
|
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeKeccakLeafKey, false)
|
|
||||||
ae.touchAddressAndChargeGas(originAddr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTxDestination adds the member fields of the sender account to the access event list,
|
// AddTxDestination adds the member fields of the sender account to the access event list,
|
||||||
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
// so that cold accesses are not charged, since they are covered by the 21000 gas.
|
||||||
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) {
|
func (ae *AccessEvents) AddTxDestination(addr common.Address, sendsValue bool) {
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, false)
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, sendsValue)
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, sendsValue)
|
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, false)
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, false)
|
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, false)
|
|
||||||
ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
// SlotGas returns the amount of gas to be charged for a cold storage access.
|
||||||
|
|
@ -275,39 +273,12 @@ func (ae *AccessEvents) CodeChunksRangeGas(contractAddr common.Address, startPC,
|
||||||
return statelessGasCharged
|
return statelessGasCharged
|
||||||
}
|
}
|
||||||
|
|
||||||
// VersionGas adds the account's version to the accessed data, and returns the
|
// BasicDataGas adds the account's basic data to the accessed data, and returns the
|
||||||
// amount of gas that it costs.
|
// amount of gas that it costs.
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an
|
// Note that an access in write mode implies an access in read mode, whereas an
|
||||||
// access in read mode does not imply an access in write mode.
|
// access in read mode does not imply an access in write mode.
|
||||||
func (ae *AccessEvents) VersionGas(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) BasicDataGas(addr common.Address, isWrite bool) uint64 {
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.VersionLeafKey, isWrite)
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BasicDataLeafKey, isWrite)
|
||||||
}
|
|
||||||
|
|
||||||
// BalanceGas adds the account's balance to the accessed data, and returns the
|
|
||||||
// amount of gas that it costs.
|
|
||||||
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
|
||||||
// read mode does not imply an access in write mode.
|
|
||||||
func (ae *AccessEvents) BalanceGas(addr common.Address, isWrite bool) uint64 {
|
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.BalanceLeafKey, isWrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NonceGas adds the account's nonce to the accessed data, and returns the
|
|
||||||
// amount of gas that it costs.
|
|
||||||
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
|
||||||
// read mode does not imply an access in write mode.
|
|
||||||
func (ae *AccessEvents) NonceGas(addr common.Address, isWrite bool) uint64 {
|
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.NonceLeafKey, isWrite)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CodeSizeGas adds the account's code size to the accessed data, and returns the
|
|
||||||
// amount of gas that it costs.
|
|
||||||
// in write mode. If false, the charged gas corresponds to an access in read mode.
|
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
|
||||||
// read mode does not imply an access in write mode.
|
|
||||||
func (ae *AccessEvents) CodeSizeGas(addr common.Address, isWrite bool) uint64 {
|
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeSizeLeafKey, isWrite)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
// CodeHashGas adds the account's code hash to the accessed data, and returns the
|
||||||
|
|
@ -316,5 +287,5 @@ func (ae *AccessEvents) CodeSizeGas(addr common.Address, isWrite bool) uint64 {
|
||||||
// Note that an access in write mode implies an access in read mode, whereas an access in
|
// Note that an access in write mode implies an access in read mode, whereas an access in
|
||||||
// read mode does not imply an access in write mode.
|
// read mode does not imply an access in write mode.
|
||||||
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 {
|
func (ae *AccessEvents) CodeHashGas(addr common.Address, isWrite bool) uint64 {
|
||||||
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeKeccakLeafKey, isWrite)
|
return ae.touchAddressAndChargeGas(addr, zeroTreeIndex, utils.CodeHashLeafKey, isWrite)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,55 +40,43 @@ func TestAccountHeaderGas(t *testing.T) {
|
||||||
ae := NewAccessEvents(utils.NewPointCache(1024))
|
ae := NewAccessEvents(utils.NewPointCache(1024))
|
||||||
|
|
||||||
// Check cold read cost
|
// Check cold read cost
|
||||||
gas := ae.VersionGas(testAddr, false)
|
gas := ae.BasicDataGas(testAddr, false)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.VersionGas(testAddr, false)
|
gas = ae.BasicDataGas(testAddr, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold read costs in the same group no longer incur the branch read cost
|
// Check cold read costs in the same group no longer incur the branch read cost
|
||||||
gas = ae.BalanceGas(testAddr, false)
|
|
||||||
if gas != params.WitnessChunkReadCost {
|
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
|
||||||
}
|
|
||||||
gas = ae.NonceGas(testAddr, false)
|
|
||||||
if gas != params.WitnessChunkReadCost {
|
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
|
||||||
}
|
|
||||||
gas = ae.CodeSizeGas(testAddr, false)
|
|
||||||
if gas != params.WitnessChunkReadCost {
|
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
|
||||||
}
|
|
||||||
gas = ae.CodeHashGas(testAddr, false)
|
gas = ae.CodeHashGas(testAddr, false)
|
||||||
if gas != params.WitnessChunkReadCost {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, params.WitnessChunkReadCost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold write cost
|
// Check cold write cost
|
||||||
gas = ae.VersionGas(testAddr, true)
|
gas = ae.BasicDataGas(testAddr, true)
|
||||||
if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
|
if want := params.WitnessBranchWriteCost + params.WitnessChunkWriteCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm write cost
|
// Check warm write cost
|
||||||
gas = ae.VersionGas(testAddr, true)
|
gas = ae.BasicDataGas(testAddr, true)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check a write without a read charges both read and write costs
|
// Check a write without a read charges both read and write costs
|
||||||
gas = ae.BalanceGas(testAddr2, true)
|
gas = ae.BasicDataGas(testAddr2, true)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessBranchWriteCost + params.WitnessChunkWriteCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that a write followed by a read charges nothing
|
// Check that a write followed by a read charges nothing
|
||||||
gas = ae.BalanceGas(testAddr2, false)
|
gas = ae.BasicDataGas(testAddr2, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
@ -112,13 +100,13 @@ func TestContractCreateInitGas(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cold read cost, without a value
|
// Check cold read cost, without a value
|
||||||
gas := ae.ContractCreateInitGas(testAddr, false)
|
gas := ae.ContractCreateInitGas(testAddr)
|
||||||
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + params.WitnessChunkWriteCost*2 + params.WitnessChunkReadCost*2; gas != want {
|
if want := params.WitnessBranchWriteCost + params.WitnessBranchReadCost + 2*params.WitnessChunkWriteCost + 2*params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check warm read cost
|
// Check warm read cost
|
||||||
gas = ae.ContractCreateInitGas(testAddr, false)
|
gas = ae.ContractCreateInitGas(testAddr)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
@ -131,17 +119,17 @@ func TestMessageCallGas(t *testing.T) {
|
||||||
|
|
||||||
// Check cold read cost, without a value
|
// Check cold read cost, without a value
|
||||||
gas := ae.MessageCallGas(testAddr)
|
gas := ae.MessageCallGas(testAddr)
|
||||||
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost*2; gas != want {
|
if want := params.WitnessBranchReadCost + params.WitnessChunkReadCost; gas != want {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that reading the version and code size of the same account does not incur the branch read cost
|
// Check that reading the basic data and code hash of the same account does not incur the branch read cost
|
||||||
gas = ae.VersionGas(testAddr, false)
|
gas = ae.BasicDataGas(testAddr, false)
|
||||||
if gas != 0 {
|
if gas != 0 {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
gas = ae.CodeSizeGas(testAddr, false)
|
gas = ae.CodeHashGas(testAddr, false)
|
||||||
if gas != 0 {
|
if gas != params.WitnessChunkReadCost {
|
||||||
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
t.Fatalf("incorrect gas computed, got %d, want %d", gas, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/lru"
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"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/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
|
@ -45,29 +46,29 @@ const (
|
||||||
|
|
||||||
// Database wraps access to tries and contract code.
|
// Database wraps access to tries and contract code.
|
||||||
type Database interface {
|
type Database interface {
|
||||||
|
// Reader returns a state reader associated with the specified state root.
|
||||||
|
Reader(root common.Hash) (Reader, error)
|
||||||
|
|
||||||
// OpenTrie opens the main account trie.
|
// OpenTrie opens the main account trie.
|
||||||
OpenTrie(root common.Hash) (Trie, error)
|
OpenTrie(root common.Hash) (Trie, error)
|
||||||
|
|
||||||
// OpenStorageTrie opens the storage trie of an account.
|
// OpenStorageTrie opens the storage trie of an account.
|
||||||
OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, trie Trie) (Trie, error)
|
OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, trie Trie) (Trie, error)
|
||||||
|
|
||||||
// CopyTrie returns an independent copy of the given trie.
|
|
||||||
CopyTrie(Trie) Trie
|
|
||||||
|
|
||||||
// ContractCode retrieves a particular contract's code.
|
// ContractCode retrieves a particular contract's code.
|
||||||
ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error)
|
ContractCode(addr common.Address, codeHash common.Hash) ([]byte, error)
|
||||||
|
|
||||||
// ContractCodeSize retrieves a particular contracts code's size.
|
// ContractCodeSize retrieves a particular contracts code's size.
|
||||||
ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error)
|
ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error)
|
||||||
|
|
||||||
// DiskDB returns the underlying key-value disk database.
|
|
||||||
DiskDB() ethdb.KeyValueStore
|
|
||||||
|
|
||||||
// PointCache returns the cache holding points used in verkle tree key computation
|
// PointCache returns the cache holding points used in verkle tree key computation
|
||||||
PointCache() *utils.PointCache
|
PointCache() *utils.PointCache
|
||||||
|
|
||||||
// TrieDB returns the underlying trie database for managing trie nodes.
|
// TrieDB returns the underlying trie database for managing trie nodes.
|
||||||
TrieDB() *triedb.Database
|
TrieDB() *triedb.Database
|
||||||
|
|
||||||
|
// Snapshot returns the underlying state snapshot.
|
||||||
|
Snapshot() *snapshot.Tree
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trie is a Ethereum Merkle Patricia trie.
|
// Trie is a Ethereum Merkle Patricia trie.
|
||||||
|
|
@ -94,7 +95,7 @@ type Trie interface {
|
||||||
// UpdateAccount abstracts an account write to the trie. It encodes the
|
// UpdateAccount abstracts an account write to the trie. It encodes the
|
||||||
// provided account object with associated algorithm and then updates it
|
// provided account object with associated algorithm and then updates it
|
||||||
// in the trie with provided address.
|
// in the trie with provided address.
|
||||||
UpdateAccount(address common.Address, account *types.StateAccount) error
|
UpdateAccount(address common.Address, account *types.StateAccount, codeLen int) error
|
||||||
|
|
||||||
// UpdateStorage associates key with value in the trie. If value has length zero,
|
// UpdateStorage associates key with value in the trie. If value has length zero,
|
||||||
// any existing value is deleted from the trie. The value bytes must not be modified
|
// any existing value is deleted from the trie. The value bytes must not be modified
|
||||||
|
|
@ -147,47 +148,62 @@ type Trie interface {
|
||||||
IsVerkle() bool
|
IsVerkle() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDatabase creates a backing store for state. The returned database is safe for
|
// CachingDB is an implementation of Database interface. It leverages both trie and
|
||||||
// concurrent use, but does not retain any recent trie nodes in memory. To keep some
|
// state snapshot to provide functionalities for state access. It's meant to be a
|
||||||
// historical state in memory, use the NewDatabaseWithConfig constructor.
|
// long-live object and has a few caches inside for sharing between blocks.
|
||||||
func NewDatabase(db ethdb.Database) Database {
|
type CachingDB struct {
|
||||||
return NewDatabaseWithConfig(db, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDatabaseWithConfig creates a backing store for state. The returned database
|
|
||||||
// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
|
|
||||||
// large memory cache.
|
|
||||||
func NewDatabaseWithConfig(db ethdb.Database, config *triedb.Config) Database {
|
|
||||||
return &cachingDB{
|
|
||||||
disk: db,
|
|
||||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
|
||||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
|
||||||
triedb: triedb.NewDatabase(db, config),
|
|
||||||
pointCache: utils.NewPointCache(pointCacheSize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDatabaseWithNodeDB creates a state database with an already initialized node database.
|
|
||||||
func NewDatabaseWithNodeDB(db ethdb.Database, triedb *triedb.Database) Database {
|
|
||||||
return &cachingDB{
|
|
||||||
disk: db,
|
|
||||||
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
|
||||||
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
|
||||||
triedb: triedb,
|
|
||||||
pointCache: utils.NewPointCache(pointCacheSize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type cachingDB struct {
|
|
||||||
disk ethdb.KeyValueStore
|
disk ethdb.KeyValueStore
|
||||||
codeSizeCache *lru.Cache[common.Hash, int]
|
|
||||||
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
|
||||||
triedb *triedb.Database
|
triedb *triedb.Database
|
||||||
|
snap *snapshot.Tree
|
||||||
|
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
|
||||||
|
codeSizeCache *lru.Cache[common.Hash, int]
|
||||||
pointCache *utils.PointCache
|
pointCache *utils.PointCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDatabase creates a state database with the provided data sources.
|
||||||
|
func NewDatabase(triedb *triedb.Database, snap *snapshot.Tree) *CachingDB {
|
||||||
|
return &CachingDB{
|
||||||
|
disk: triedb.Disk(),
|
||||||
|
triedb: triedb,
|
||||||
|
snap: snap,
|
||||||
|
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
|
||||||
|
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
|
||||||
|
pointCache: utils.NewPointCache(pointCacheSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reader returns a state reader associated with the specified state root.
|
||||||
|
func (db *CachingDB) Reader(stateRoot common.Hash) (Reader, error) {
|
||||||
|
var readers []Reader
|
||||||
|
|
||||||
|
// Set up the state snapshot reader if available. This feature
|
||||||
|
// is optional and may be partially useful if it's not fully
|
||||||
|
// generated.
|
||||||
|
if db.snap != nil {
|
||||||
|
sr, err := newStateReader(stateRoot, db.snap)
|
||||||
|
if err == nil {
|
||||||
|
readers = append(readers, sr) // snap reader is optional
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Set up the trie reader, which is expected to always be available
|
||||||
|
// as the gatekeeper unless the state is corrupted.
|
||||||
|
tr, err := newTrieReader(stateRoot, db.triedb, db.pointCache)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
readers = append(readers, tr)
|
||||||
|
|
||||||
|
return newMultiReader(readers...)
|
||||||
|
}
|
||||||
|
|
||||||
// OpenTrie opens the main account trie at a specific root hash.
|
// OpenTrie opens the main account trie at a specific root hash.
|
||||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
func (db *CachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||||
if db.triedb.IsVerkle() {
|
if db.triedb.IsVerkle() {
|
||||||
return trie.NewVerkleTrie(root, db.triedb, db.pointCache)
|
return trie.NewVerkleTrie(root, db.triedb, db.pointCache)
|
||||||
}
|
}
|
||||||
|
|
@ -200,7 +216,7 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenStorageTrie opens the storage trie of an account.
|
// OpenStorageTrie opens the storage trie of an account.
|
||||||
func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) {
|
func (db *CachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) {
|
||||||
// In the verkle case, there is only one tree. But the two-tree structure
|
// In the verkle case, there is only one tree. But the two-tree structure
|
||||||
// is hardcoded in the codebase. So we need to return the same trie in this
|
// is hardcoded in the codebase. So we need to return the same trie in this
|
||||||
// case.
|
// case.
|
||||||
|
|
@ -215,20 +231,8 @@ func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Addre
|
||||||
return tr, nil
|
return tr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CopyTrie returns an independent copy of the given trie.
|
|
||||||
func (db *cachingDB) CopyTrie(t Trie) Trie {
|
|
||||||
switch t := t.(type) {
|
|
||||||
case *trie.StateTrie:
|
|
||||||
return t.Copy()
|
|
||||||
case *trie.VerkleTrie:
|
|
||||||
return t.Copy()
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("unknown trie type %T", t))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContractCode retrieves a particular contract's code.
|
// ContractCode retrieves a particular contract's code.
|
||||||
func (db *cachingDB) ContractCode(address common.Address, codeHash common.Hash) ([]byte, error) {
|
func (db *CachingDB) ContractCode(address common.Address, codeHash common.Hash) ([]byte, error) {
|
||||||
code, _ := db.codeCache.Get(codeHash)
|
code, _ := db.codeCache.Get(codeHash)
|
||||||
if len(code) > 0 {
|
if len(code) > 0 {
|
||||||
return code, nil
|
return code, nil
|
||||||
|
|
@ -249,7 +253,7 @@ func (db *cachingDB) ContractCode(address common.Address, codeHash common.Hash)
|
||||||
// ContractCodeWithPrefix retrieves a particular contract's code. If the
|
// ContractCodeWithPrefix retrieves a particular contract's code. If the
|
||||||
// code can't be found in the cache, then check the existence with **new**
|
// code can't be found in the cache, then check the existence with **new**
|
||||||
// db scheme.
|
// db scheme.
|
||||||
func (db *cachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error) {
|
func (db *CachingDB) ContractCodeWithPrefix(address common.Address, codeHash common.Hash) ([]byte, error) {
|
||||||
code, _ := db.codeCache.Get(codeHash)
|
code, _ := db.codeCache.Get(codeHash)
|
||||||
if len(code) > 0 {
|
if len(code) > 0 {
|
||||||
return code, nil
|
return code, nil
|
||||||
|
|
@ -268,7 +272,7 @@ func (db *cachingDB) ContractCodeWithPrefix(address common.Address, codeHash com
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractCodeSize retrieves a particular contracts code's size.
|
// ContractCodeSize retrieves a particular contracts code's size.
|
||||||
func (db *cachingDB) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
|
func (db *CachingDB) ContractCodeSize(addr common.Address, codeHash common.Hash) (int, error) {
|
||||||
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
|
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
|
||||||
return cached, nil
|
return cached, nil
|
||||||
}
|
}
|
||||||
|
|
@ -276,17 +280,29 @@ func (db *cachingDB) ContractCodeSize(addr common.Address, codeHash common.Hash)
|
||||||
return len(code), err
|
return len(code), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiskDB returns the underlying key-value disk database.
|
|
||||||
func (db *cachingDB) DiskDB() ethdb.KeyValueStore {
|
|
||||||
return db.disk
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrieDB retrieves any intermediate trie-node caching layer.
|
// TrieDB retrieves any intermediate trie-node caching layer.
|
||||||
func (db *cachingDB) TrieDB() *triedb.Database {
|
func (db *CachingDB) TrieDB() *triedb.Database {
|
||||||
return db.triedb
|
return db.triedb
|
||||||
}
|
}
|
||||||
|
|
||||||
// PointCache returns the cache of evaluated curve points.
|
// PointCache returns the cache of evaluated curve points.
|
||||||
func (db *cachingDB) PointCache() *utils.PointCache {
|
func (db *CachingDB) PointCache() *utils.PointCache {
|
||||||
return db.pointCache
|
return db.pointCache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Snapshot returns the underlying state snapshot.
|
||||||
|
func (db *CachingDB) Snapshot() *snapshot.Tree {
|
||||||
|
return db.snap
|
||||||
|
}
|
||||||
|
|
||||||
|
// mustCopyTrie returns a deep-copied trie.
|
||||||
|
func mustCopyTrie(t Trie) Trie {
|
||||||
|
switch t := t.(type) {
|
||||||
|
case *trie.StateTrie:
|
||||||
|
return t.Copy()
|
||||||
|
case *trie.VerkleTrie:
|
||||||
|
return t.Copy()
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("unknown trie type %T", t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ func testNodeIteratorCoverage(t *testing.T, scheme string) {
|
||||||
db, sdb, ndb, root, _ := makeTestState(scheme)
|
db, sdb, ndb, root, _ := makeTestState(scheme)
|
||||||
ndb.Commit(root, false)
|
ndb.Commit(root, false)
|
||||||
|
|
||||||
state, err := New(root, sdb, nil)
|
state, err := New(root, sdb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create state trie at %x: %v", root, err)
|
t.Fatalf("failed to create state trie at %x: %v", root, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,21 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
|
"slices"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/blockstm"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type revision struct {
|
||||||
|
id int
|
||||||
|
journalIndex int
|
||||||
|
}
|
||||||
|
|
||||||
// journalEntry is a modification entry in the state change journal that can be
|
// journalEntry is a modification entry in the state change journal that can be
|
||||||
// reverted on demand.
|
// reverted on demand.
|
||||||
type journalEntry interface {
|
type journalEntry interface {
|
||||||
|
|
@ -43,6 +51,9 @@ type journalEntry interface {
|
||||||
type journal struct {
|
type journal struct {
|
||||||
entries []journalEntry // Current changes tracked by the journal
|
entries []journalEntry // Current changes tracked by the journal
|
||||||
dirties map[common.Address]int // Dirty accounts and the number of changes
|
dirties map[common.Address]int // Dirty accounts and the number of changes
|
||||||
|
|
||||||
|
validRevisions []revision
|
||||||
|
nextRevisionId int
|
||||||
}
|
}
|
||||||
|
|
||||||
// newJournal creates a new initialized journal.
|
// newJournal creates a new initialized journal.
|
||||||
|
|
@ -52,6 +63,40 @@ func newJournal() *journal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset clears the journal, after this operation the journal can be used anew.
|
||||||
|
// It is semantically similar to calling 'newJournal', but the underlying slices
|
||||||
|
// can be reused.
|
||||||
|
func (j *journal) reset() {
|
||||||
|
j.entries = j.entries[:0]
|
||||||
|
j.validRevisions = j.validRevisions[:0]
|
||||||
|
clear(j.dirties)
|
||||||
|
j.nextRevisionId = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// snapshot returns an identifier for the current revision of the state.
|
||||||
|
func (j *journal) snapshot() int {
|
||||||
|
id := j.nextRevisionId
|
||||||
|
j.nextRevisionId++
|
||||||
|
j.validRevisions = append(j.validRevisions, revision{id, j.length()})
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
// revertToSnapshot reverts all state changes made since the given revision.
|
||||||
|
func (j *journal) revertToSnapshot(revid int, s *StateDB) {
|
||||||
|
// Find the snapshot in the stack of valid snapshots.
|
||||||
|
idx := sort.Search(len(j.validRevisions), func(i int) bool {
|
||||||
|
return j.validRevisions[i].id >= revid
|
||||||
|
})
|
||||||
|
if idx == len(j.validRevisions) || j.validRevisions[idx].id != revid {
|
||||||
|
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
|
||||||
|
}
|
||||||
|
snapshot := j.validRevisions[idx].journalIndex
|
||||||
|
|
||||||
|
// Replay the journal to undo changes and remove invalidated snapshots
|
||||||
|
j.revert(s, snapshot)
|
||||||
|
j.validRevisions = j.validRevisions[:idx]
|
||||||
|
}
|
||||||
|
|
||||||
// append inserts a new modification entry to the end of the change journal.
|
// append inserts a new modification entry to the end of the change journal.
|
||||||
func (j *journal) append(entry journalEntry) {
|
func (j *journal) append(entry journalEntry) {
|
||||||
j.entries = append(j.entries, entry)
|
j.entries = append(j.entries, entry)
|
||||||
|
|
@ -99,46 +144,120 @@ func (j *journal) copy() *journal {
|
||||||
return &journal{
|
return &journal{
|
||||||
entries: entries,
|
entries: entries,
|
||||||
dirties: maps.Clone(j.dirties),
|
dirties: maps.Clone(j.dirties),
|
||||||
|
validRevisions: slices.Clone(j.validRevisions),
|
||||||
|
nextRevisionId: j.nextRevisionId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *journal) logChange(txHash common.Hash) {
|
||||||
|
j.append(addLogChange{txhash: txHash})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) createObject(addr common.Address) {
|
||||||
|
j.append(createObjectChange{account: addr})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) createContract(addr common.Address) {
|
||||||
|
j.append(createContractChange{account: addr})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) destruct(addr common.Address) {
|
||||||
|
j.append(selfDestructChange{account: addr})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) storageChange(addr common.Address, key, prev, origin common.Hash) {
|
||||||
|
j.append(storageChange{
|
||||||
|
account: addr,
|
||||||
|
key: key,
|
||||||
|
prevvalue: prev,
|
||||||
|
origvalue: origin,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) transientStateChange(addr common.Address, key, prev common.Hash) {
|
||||||
|
j.append(transientStorageChange{
|
||||||
|
account: addr,
|
||||||
|
key: key,
|
||||||
|
prevalue: prev,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) refundChange(previous uint64) {
|
||||||
|
j.append(refundChange{prev: previous})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) balanceChange(addr common.Address, previous *uint256.Int) {
|
||||||
|
j.append(balanceChange{
|
||||||
|
account: addr,
|
||||||
|
prev: previous.Clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) setCode(address common.Address) {
|
||||||
|
j.append(codeChange{account: address})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) nonceChange(address common.Address, prev uint64) {
|
||||||
|
j.append(nonceChange{
|
||||||
|
account: address,
|
||||||
|
prev: prev,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) touchChange(address common.Address) {
|
||||||
|
j.append(touchChange{
|
||||||
|
account: address,
|
||||||
|
})
|
||||||
|
if address == ripemd {
|
||||||
|
// Explicitly put it in the dirty-cache, which is otherwise generated from
|
||||||
|
// flattened journals.
|
||||||
|
j.dirty(address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) accessListAddAccount(addr common.Address) {
|
||||||
|
j.append(accessListAddAccountChange{addr})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *journal) accessListAddSlot(addr common.Address, slot common.Hash) {
|
||||||
|
j.append(accessListAddSlotChange{
|
||||||
|
address: addr,
|
||||||
|
slot: slot,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// Changes to the account trie.
|
// Changes to the account trie.
|
||||||
createObjectChange struct {
|
createObjectChange struct {
|
||||||
account *common.Address
|
account common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
// createContractChange represents an account becoming a contract-account.
|
// createContractChange represents an account becoming a contract-account.
|
||||||
// This event happens prior to executing initcode. The journal-event simply
|
// This event happens prior to executing initcode. The journal-event simply
|
||||||
// manages the created-flag, in order to allow same-tx destruction.
|
// manages the created-flag, in order to allow same-tx destruction.
|
||||||
createContractChange struct {
|
createContractChange struct {
|
||||||
account common.Address
|
account common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
selfDestructChange struct {
|
selfDestructChange struct {
|
||||||
account *common.Address
|
account common.Address
|
||||||
prev bool // whether account had already self-destructed
|
|
||||||
prevbalance *uint256.Int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes to individual accounts.
|
// Changes to individual accounts.
|
||||||
balanceChange struct {
|
balanceChange struct {
|
||||||
account *common.Address
|
account common.Address
|
||||||
prev *uint256.Int
|
prev *uint256.Int
|
||||||
}
|
}
|
||||||
nonceChange struct {
|
nonceChange struct {
|
||||||
account *common.Address
|
account common.Address
|
||||||
prev uint64
|
prev uint64
|
||||||
}
|
}
|
||||||
storageChange struct {
|
storageChange struct {
|
||||||
account *common.Address
|
account common.Address
|
||||||
key common.Hash
|
key common.Hash
|
||||||
prevvalue common.Hash
|
prevvalue common.Hash
|
||||||
origvalue common.Hash
|
origvalue common.Hash
|
||||||
}
|
}
|
||||||
codeChange struct {
|
codeChange struct {
|
||||||
account *common.Address
|
account common.Address
|
||||||
prevcode, prevhash []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes to other state values.
|
// Changes to other state values.
|
||||||
|
|
@ -148,36 +267,32 @@ type (
|
||||||
addLogChange struct {
|
addLogChange struct {
|
||||||
txhash common.Hash
|
txhash common.Hash
|
||||||
}
|
}
|
||||||
addPreimageChange struct {
|
|
||||||
hash common.Hash
|
|
||||||
}
|
|
||||||
touchChange struct {
|
touchChange struct {
|
||||||
account *common.Address
|
account common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes to the access list
|
// Changes to the access list
|
||||||
accessListAddAccountChange struct {
|
accessListAddAccountChange struct {
|
||||||
address *common.Address
|
address common.Address
|
||||||
}
|
}
|
||||||
accessListAddSlotChange struct {
|
accessListAddSlotChange struct {
|
||||||
address *common.Address
|
address common.Address
|
||||||
slot *common.Hash
|
slot common.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changes to transient storage
|
// Changes to transient storage
|
||||||
transientStorageChange struct {
|
transientStorageChange struct {
|
||||||
account *common.Address
|
account common.Address
|
||||||
key, prevalue common.Hash
|
key, prevalue common.Hash
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ch createObjectChange) revert(s *StateDB) {
|
func (ch createObjectChange) revert(s *StateDB) {
|
||||||
delete(s.stateObjects, *ch.account)
|
delete(s.stateObjects, ch.account)
|
||||||
RevertWrite(s, blockstm.NewAddressKey(*ch.account))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch createObjectChange) dirtied() *common.Address {
|
func (ch createObjectChange) dirtied() *common.Address {
|
||||||
return ch.account
|
return &ch.account
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch createObjectChange) copy() journalEntry {
|
func (ch createObjectChange) copy() journalEntry {
|
||||||
|
|
@ -201,23 +316,19 @@ func (ch createContractChange) copy() journalEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch selfDestructChange) revert(s *StateDB) {
|
func (ch selfDestructChange) revert(s *StateDB) {
|
||||||
obj := s.getStateObject(*ch.account)
|
obj := s.getStateObject(ch.account)
|
||||||
if obj != nil {
|
if obj != nil {
|
||||||
obj.selfDestructed = ch.prev
|
obj.selfDestructed = false
|
||||||
obj.setBalance(ch.prevbalance)
|
|
||||||
RevertWrite(s, blockstm.NewSubpathKey(*ch.account, SuicidePath))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch selfDestructChange) dirtied() *common.Address {
|
func (ch selfDestructChange) dirtied() *common.Address {
|
||||||
return ch.account
|
return &ch.account
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch selfDestructChange) copy() journalEntry {
|
func (ch selfDestructChange) copy() journalEntry {
|
||||||
return selfDestructChange{
|
return selfDestructChange{
|
||||||
account: ch.account,
|
account: ch.account,
|
||||||
prev: ch.prev,
|
|
||||||
prevbalance: new(uint256.Int).Set(ch.prevbalance),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,7 +338,7 @@ func (ch touchChange) revert(s *StateDB) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch touchChange) dirtied() *common.Address {
|
func (ch touchChange) dirtied() *common.Address {
|
||||||
return ch.account
|
return &ch.account
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch touchChange) copy() journalEntry {
|
func (ch touchChange) copy() journalEntry {
|
||||||
|
|
@ -237,11 +348,11 @@ func (ch touchChange) copy() journalEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch balanceChange) revert(s *StateDB) {
|
func (ch balanceChange) revert(s *StateDB) {
|
||||||
s.getStateObject(*ch.account).setBalance(ch.prev)
|
s.getStateObject(ch.account).setBalance(ch.prev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch balanceChange) dirtied() *common.Address {
|
func (ch balanceChange) dirtied() *common.Address {
|
||||||
return ch.account
|
return &ch.account
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch balanceChange) copy() journalEntry {
|
func (ch balanceChange) copy() journalEntry {
|
||||||
|
|
@ -252,11 +363,11 @@ func (ch balanceChange) copy() journalEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch nonceChange) revert(s *StateDB) {
|
func (ch nonceChange) revert(s *StateDB) {
|
||||||
s.getStateObject(*ch.account).setNonce(ch.prev)
|
s.getStateObject(ch.account).setNonce(ch.prev)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch nonceChange) dirtied() *common.Address {
|
func (ch nonceChange) dirtied() *common.Address {
|
||||||
return ch.account
|
return &ch.account
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch nonceChange) copy() journalEntry {
|
func (ch nonceChange) copy() journalEntry {
|
||||||
|
|
@ -267,29 +378,23 @@ func (ch nonceChange) copy() journalEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch codeChange) revert(s *StateDB) {
|
func (ch codeChange) revert(s *StateDB) {
|
||||||
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
|
s.getStateObject(ch.account).setCode(types.EmptyCodeHash, nil)
|
||||||
RevertWrite(s, blockstm.NewSubpathKey(*ch.account, CodePath))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch codeChange) dirtied() *common.Address {
|
func (ch codeChange) dirtied() *common.Address {
|
||||||
return ch.account
|
return &ch.account
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch codeChange) copy() journalEntry {
|
func (ch codeChange) copy() journalEntry {
|
||||||
return codeChange{
|
return codeChange{account: ch.account}
|
||||||
account: ch.account,
|
|
||||||
prevhash: common.CopyBytes(ch.prevhash),
|
|
||||||
prevcode: common.CopyBytes(ch.prevcode),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch storageChange) revert(s *StateDB) {
|
func (ch storageChange) revert(s *StateDB) {
|
||||||
s.getStateObject(*ch.account).setState(ch.key, ch.prevvalue, ch.origvalue)
|
s.getStateObject(ch.account).setState(ch.key, ch.prevvalue, ch.origvalue)
|
||||||
RevertWrite(s, blockstm.NewStateKey(*ch.account, ch.key))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch storageChange) dirtied() *common.Address {
|
func (ch storageChange) dirtied() *common.Address {
|
||||||
return ch.account
|
return &ch.account
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch storageChange) copy() journalEntry {
|
func (ch storageChange) copy() journalEntry {
|
||||||
|
|
@ -301,7 +406,7 @@ func (ch storageChange) copy() journalEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch transientStorageChange) revert(s *StateDB) {
|
func (ch transientStorageChange) revert(s *StateDB) {
|
||||||
s.setTransientState(*ch.account, ch.key, ch.prevalue)
|
s.setTransientState(ch.account, ch.key, ch.prevalue)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch transientStorageChange) dirtied() *common.Address {
|
func (ch transientStorageChange) dirtied() *common.Address {
|
||||||
|
|
@ -351,20 +456,6 @@ func (ch addLogChange) copy() journalEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch addPreimageChange) revert(s *StateDB) {
|
|
||||||
delete(s.preimages, ch.hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ch addPreimageChange) dirtied() *common.Address {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ch addPreimageChange) copy() journalEntry {
|
|
||||||
return addPreimageChange{
|
|
||||||
hash: ch.hash,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ch accessListAddAccountChange) revert(s *StateDB) {
|
func (ch accessListAddAccountChange) revert(s *StateDB) {
|
||||||
/*
|
/*
|
||||||
One important invariant here, is that whenever a (addr, slot) is added, if the
|
One important invariant here, is that whenever a (addr, slot) is added, if the
|
||||||
|
|
@ -375,7 +466,7 @@ func (ch accessListAddAccountChange) revert(s *StateDB) {
|
||||||
(addr) at this point, since no storage adds can remain when come upon
|
(addr) at this point, since no storage adds can remain when come upon
|
||||||
a single (addr) change.
|
a single (addr) change.
|
||||||
*/
|
*/
|
||||||
s.accessList.DeleteAddress(*ch.address)
|
s.accessList.DeleteAddress(ch.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch accessListAddAccountChange) dirtied() *common.Address {
|
func (ch accessListAddAccountChange) dirtied() *common.Address {
|
||||||
|
|
@ -389,7 +480,7 @@ func (ch accessListAddAccountChange) copy() journalEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch accessListAddSlotChange) revert(s *StateDB) {
|
func (ch accessListAddSlotChange) revert(s *StateDB) {
|
||||||
s.accessList.DeleteSlot(*ch.address, *ch.slot)
|
s.accessList.DeleteSlot(ch.address, ch.slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch accessListAddSlotChange) dirtied() *common.Address {
|
func (ch accessListAddSlotChange) dirtied() *common.Address {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ package state
|
||||||
import "github.com/ethereum/go-ethereum/metrics"
|
import "github.com/ethereum/go-ethereum/metrics"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
accountReadMeters = metrics.NewRegisteredMeter("state/read/accounts", nil)
|
||||||
|
storageReadMeters = metrics.NewRegisteredMeter("state/read/storage", nil)
|
||||||
accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
|
accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
|
||||||
storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
|
storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
|
||||||
accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
|
accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
|
||||||
|
|
|
||||||
|
|
@ -274,11 +274,10 @@ func (p *Pruner) Prune(root common.Hash) error {
|
||||||
// is the presence of root can indicate the presence of the
|
// is the presence of root can indicate the presence of the
|
||||||
// entire trie.
|
// entire trie.
|
||||||
if !rawdb.HasLegacyTrieNode(p.db, root) {
|
if !rawdb.HasLegacyTrieNode(p.db, root) {
|
||||||
// The special case is for clique based networks(goerli
|
// The special case is for clique based networks, it's possible
|
||||||
// and some other private networks), it's possible that two
|
// that two consecutive blocks will have same root. In this case
|
||||||
// consecutive blocks will have same root. In this case snapshot
|
// snapshot difflayer won't be created. So HEAD-127 may not paired
|
||||||
// difflayer won't be created. So HEAD-127 may not paired with
|
// with head-127 layer. Instead the paired layer is higher than the
|
||||||
// head-127 layer. Instead the paired layer is higher than the
|
|
||||||
// bottom-most diff layer. Try to find the bottom-most snapshot
|
// bottom-most diff layer. Try to find the bottom-most snapshot
|
||||||
// layer with state available.
|
// layer with state available.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
313
core/state/reader.go
Normal file
313
core/state/reader.go
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
// Copyright 2024 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"maps"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"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/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/ethereum/go-ethereum/trie/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reader defines the interface for accessing accounts and storage slots
|
||||||
|
// associated with a specific state.
|
||||||
|
type Reader interface {
|
||||||
|
// Account retrieves the account associated with a particular address.
|
||||||
|
//
|
||||||
|
// - Returns a nil account if it does not exist
|
||||||
|
// - Returns an error only if an unexpected issue occurs
|
||||||
|
// - The returned account is safe to modify after the call
|
||||||
|
Account(addr common.Address) (*types.StateAccount, error)
|
||||||
|
|
||||||
|
// Storage retrieves the storage slot associated with a particular account
|
||||||
|
// address and slot key.
|
||||||
|
//
|
||||||
|
// - Returns an empty slot if it does not exist
|
||||||
|
// - Returns an error only if an unexpected issue occurs
|
||||||
|
// - The returned storage slot is safe to modify after the call
|
||||||
|
Storage(addr common.Address, slot common.Hash) (common.Hash, error)
|
||||||
|
|
||||||
|
// Copy returns a deep-copied state reader.
|
||||||
|
Copy() Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// stateReader is a wrapper over the state snapshot and implements the Reader
|
||||||
|
// interface. It provides an efficient way to access flat state.
|
||||||
|
type stateReader struct {
|
||||||
|
snap snapshot.Snapshot
|
||||||
|
buff crypto.KeccakState
|
||||||
|
}
|
||||||
|
|
||||||
|
// newStateReader constructs a flat state reader with on the specified state root.
|
||||||
|
func newStateReader(root common.Hash, snaps *snapshot.Tree) (*stateReader, error) {
|
||||||
|
snap := snaps.Snapshot(root)
|
||||||
|
if snap == nil {
|
||||||
|
return nil, errors.New("snapshot is not available")
|
||||||
|
}
|
||||||
|
return &stateReader{
|
||||||
|
snap: snap,
|
||||||
|
buff: crypto.NewKeccakState(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account implements Reader, 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 *stateReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
|
ret, err := r.snap.Account(crypto.HashData(r.buff, addr.Bytes()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ret == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
acct := &types.StateAccount{
|
||||||
|
Nonce: ret.Nonce,
|
||||||
|
Balance: ret.Balance,
|
||||||
|
CodeHash: ret.CodeHash,
|
||||||
|
Root: common.BytesToHash(ret.Root),
|
||||||
|
}
|
||||||
|
if len(acct.CodeHash) == 0 {
|
||||||
|
acct.CodeHash = types.EmptyCodeHash.Bytes()
|
||||||
|
}
|
||||||
|
if acct.Root == (common.Hash{}) {
|
||||||
|
acct.Root = types.EmptyRootHash
|
||||||
|
}
|
||||||
|
return acct, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage implements Reader, 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 *stateReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||||
|
addrHash := crypto.HashData(r.buff, addr.Bytes())
|
||||||
|
slotHash := crypto.HashData(r.buff, key.Bytes())
|
||||||
|
ret, err := r.snap.Storage(addrHash, slotHash)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
if len(ret) == 0 {
|
||||||
|
return common.Hash{}, nil
|
||||||
|
}
|
||||||
|
// Perform the rlp-decode as the slot value is RLP-encoded in the state
|
||||||
|
// snapshot.
|
||||||
|
_, content, _, err := rlp.Split(ret)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
var value common.Hash
|
||||||
|
value.SetBytes(content)
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy implements Reader, returning a deep-copied snap reader.
|
||||||
|
func (r *stateReader) Copy() Reader {
|
||||||
|
return &stateReader{
|
||||||
|
snap: r.snap,
|
||||||
|
buff: crypto.NewKeccakState(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// trieReader implements the Reader interface, providing functions to access
|
||||||
|
// state from the referenced trie.
|
||||||
|
type trieReader struct {
|
||||||
|
root common.Hash // State root which uniquely represent a state
|
||||||
|
db *triedb.Database // Database for loading trie
|
||||||
|
buff crypto.KeccakState // Buffer for keccak256 hashing
|
||||||
|
mainTrie Trie // Main trie, resolved in constructor
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// trieReader constructs a trie reader of the specific state. An error will be
|
||||||
|
// returned if the associated trie specified by root is not existent.
|
||||||
|
func newTrieReader(root common.Hash, db *triedb.Database, cache *utils.PointCache) (*trieReader, error) {
|
||||||
|
var (
|
||||||
|
tr Trie
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if !db.IsVerkle() {
|
||||||
|
tr, err = trie.NewStateTrie(trie.StateTrieID(root), db)
|
||||||
|
} else {
|
||||||
|
tr, err = trie.NewVerkleTrie(root, db, cache)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &trieReader{
|
||||||
|
root: root,
|
||||||
|
db: db,
|
||||||
|
buff: crypto.NewKeccakState(),
|
||||||
|
mainTrie: tr,
|
||||||
|
subRoots: make(map[common.Address]common.Hash),
|
||||||
|
subTries: make(map[common.Address]Trie),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account implements Reader, retrieving the account specified by the address.
|
||||||
|
//
|
||||||
|
// An error will be returned if the trie state is corrupted. An nil account
|
||||||
|
// will be returned if it's not existent in the trie.
|
||||||
|
func (r *trieReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
|
account, err := r.mainTrie.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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage implements Reader, retrieving the storage slot specified by the
|
||||||
|
// address and slot key.
|
||||||
|
//
|
||||||
|
// An error will be returned if the trie state is corrupted. An empty storage
|
||||||
|
// slot will be returned if it's not existent in the trie.
|
||||||
|
func (r *trieReader) Storage(addr common.Address, key common.Hash) (common.Hash, error) {
|
||||||
|
var (
|
||||||
|
tr Trie
|
||||||
|
found bool
|
||||||
|
value common.Hash
|
||||||
|
)
|
||||||
|
if r.db.IsVerkle() {
|
||||||
|
tr = r.mainTrie
|
||||||
|
} else {
|
||||||
|
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.HashData(r.buff, addr.Bytes()), root), r.db)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
value.SetBytes(ret)
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy implements Reader, returning a deep-copied trie reader.
|
||||||
|
func (r *trieReader) Copy() Reader {
|
||||||
|
tries := make(map[common.Address]Trie)
|
||||||
|
for addr, tr := range r.subTries {
|
||||||
|
tries[addr] = mustCopyTrie(tr)
|
||||||
|
}
|
||||||
|
return &trieReader{
|
||||||
|
root: r.root,
|
||||||
|
db: r.db,
|
||||||
|
buff: crypto.NewKeccakState(),
|
||||||
|
mainTrie: mustCopyTrie(r.mainTrie),
|
||||||
|
subRoots: maps.Clone(r.subRoots),
|
||||||
|
subTries: tries,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiReader is the aggregation of a list of Reader interface, providing state
|
||||||
|
// access by leveraging all readers. The checking priority is determined by the
|
||||||
|
// position in the reader list.
|
||||||
|
type multiReader struct {
|
||||||
|
readers []Reader // List of readers, sorted by checking priority
|
||||||
|
}
|
||||||
|
|
||||||
|
// newMultiReader constructs a multiReader instance with the given readers. The
|
||||||
|
// priority among readers is assumed to be sorted. Note, it must contain at least
|
||||||
|
// one reader for constructing a multiReader.
|
||||||
|
func newMultiReader(readers ...Reader) (*multiReader, error) {
|
||||||
|
if len(readers) == 0 {
|
||||||
|
return nil, errors.New("empty reader set")
|
||||||
|
}
|
||||||
|
return &multiReader{
|
||||||
|
readers: readers,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account implementing Reader interface, retrieving the account associated with
|
||||||
|
// a particular address.
|
||||||
|
//
|
||||||
|
// - Returns a nil account if it does not exist
|
||||||
|
// - Returns an error only if an unexpected issue occurs
|
||||||
|
// - The returned account is safe to modify after the call
|
||||||
|
func (r *multiReader) Account(addr common.Address) (*types.StateAccount, error) {
|
||||||
|
var errs []error
|
||||||
|
for _, reader := range r.readers {
|
||||||
|
acct, err := reader.Account(addr)
|
||||||
|
if err == nil {
|
||||||
|
return acct, nil
|
||||||
|
}
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
return nil, errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage implementing Reader interface, retrieving the storage slot associated
|
||||||
|
// with a particular account address and slot key.
|
||||||
|
//
|
||||||
|
// - Returns an empty slot if it does not exist
|
||||||
|
// - Returns an error only if an unexpected issue occurs
|
||||||
|
// - The returned storage slot is safe to modify after the call
|
||||||
|
func (r *multiReader) Storage(addr common.Address, slot common.Hash) (common.Hash, error) {
|
||||||
|
var errs []error
|
||||||
|
for _, reader := range r.readers {
|
||||||
|
slot, err := reader.Storage(addr, slot)
|
||||||
|
if err == nil {
|
||||||
|
return slot, nil
|
||||||
|
}
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
return common.Hash{}, errors.Join(errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy implementing Reader interface, returning a deep-copied state reader.
|
||||||
|
func (r *multiReader) Copy() Reader {
|
||||||
|
var readers []Reader
|
||||||
|
for _, reader := range r.readers {
|
||||||
|
readers = append(readers, reader.Copy())
|
||||||
|
}
|
||||||
|
return &multiReader{readers: readers}
|
||||||
|
}
|
||||||
|
|
@ -74,6 +74,14 @@ func (dl *diskLayer) Stale() bool {
|
||||||
return dl.stale
|
return dl.stale
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// markStale sets the stale flag as true.
|
||||||
|
func (dl *diskLayer) markStale() {
|
||||||
|
dl.lock.Lock()
|
||||||
|
defer dl.lock.Unlock()
|
||||||
|
|
||||||
|
dl.stale = true
|
||||||
|
}
|
||||||
|
|
||||||
// Account directly retrieves the account associated with a particular hash in
|
// Account directly retrieves the account associated with a particular hash in
|
||||||
// the snapshot slim data format.
|
// the snapshot slim data format.
|
||||||
func (dl *diskLayer) Account(hash common.Hash) (*types.SlimAccount, error) {
|
func (dl *diskLayer) Account(hash common.Hash) (*types.SlimAccount, error) {
|
||||||
|
|
@ -184,3 +192,18 @@ func (dl *diskLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro
|
||||||
func (dl *diskLayer) Update(blockHash common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
|
func (dl *diskLayer) Update(blockHash common.Hash, destructs map[common.Hash]struct{}, accounts map[common.Hash][]byte, storage map[common.Hash]map[common.Hash][]byte) *diffLayer {
|
||||||
return newDiffLayer(dl, blockHash, destructs, accounts, storage)
|
return newDiffLayer(dl, blockHash, destructs, accounts, storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stopGeneration aborts the state snapshot generation if it is currently running.
|
||||||
|
func (dl *diskLayer) stopGeneration() {
|
||||||
|
dl.lock.RLock()
|
||||||
|
generating := dl.genMarker != nil
|
||||||
|
dl.lock.RUnlock()
|
||||||
|
if !generating {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if dl.genAbort != nil {
|
||||||
|
abort := make(chan *generatorStats)
|
||||||
|
dl.genAbort <- abort
|
||||||
|
<-abort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -698,18 +698,11 @@ func generateAccounts(ctx *generatorContext, dl *diskLayer, accMarker []byte) er
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Always reset the initial account range as 1 whenever recover from the
|
|
||||||
// interruption. TODO(rjl493456442) can we remove it?
|
|
||||||
var accountRange = accountCheckRange
|
|
||||||
if len(accMarker) > 0 {
|
|
||||||
accountRange = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
origin := common.CopyBytes(accMarker)
|
origin := common.CopyBytes(accMarker)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
id := trie.StateTrieID(dl.root)
|
id := trie.StateTrieID(dl.root)
|
||||||
exhausted, last, err := dl.generateRange(ctx, id, rawdb.SnapshotAccountPrefix, snapAccount, origin, accountRange, onAccount, types.FullAccountRLP)
|
exhausted, last, err := dl.generateRange(ctx, id, rawdb.SnapshotAccountPrefix, snapAccount, origin, accountCheckRange, onAccount, types.FullAccountRLP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err // The procedure it aborted, either by external signal or internal error.
|
return err // The procedure it aborted, either by external signal or internal error.
|
||||||
}
|
}
|
||||||
|
|
@ -722,8 +715,6 @@ func generateAccounts(ctx *generatorContext, dl *diskLayer, accMarker []byte) er
|
||||||
ctx.removeStorageLeft()
|
ctx.removeStorageLeft()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
accountRange = accountCheckRange
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -262,24 +262,11 @@ func (t *Tree) Disable() {
|
||||||
for _, layer := range t.layers {
|
for _, layer := range t.layers {
|
||||||
switch layer := layer.(type) {
|
switch layer := layer.(type) {
|
||||||
case *diskLayer:
|
case *diskLayer:
|
||||||
|
// TODO this function will hang if it's called twice. Will
|
||||||
layer.lock.RLock()
|
// fix it in the following PRs.
|
||||||
generating := layer.genMarker != nil
|
layer.stopGeneration()
|
||||||
layer.lock.RUnlock()
|
layer.markStale()
|
||||||
if !generating {
|
layer.Release()
|
||||||
// Generator is already aborted or finished
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// If the base layer is generating, abort it
|
|
||||||
if layer.genAbort != nil {
|
|
||||||
abort := make(chan *generatorStats)
|
|
||||||
layer.genAbort <- abort
|
|
||||||
<-abort
|
|
||||||
}
|
|
||||||
// Layer should be inactive now, mark it as stale
|
|
||||||
layer.lock.Lock()
|
|
||||||
layer.stale = true
|
|
||||||
layer.lock.Unlock()
|
|
||||||
|
|
||||||
case *diffLayer:
|
case *diffLayer:
|
||||||
// If the layer is a simple diff, simply mark as stale
|
// If the layer is a simple diff, simply mark as stale
|
||||||
|
|
@ -768,16 +755,11 @@ func (t *Tree) Rebuild(root common.Hash) {
|
||||||
for _, layer := range t.layers {
|
for _, layer := range t.layers {
|
||||||
switch layer := layer.(type) {
|
switch layer := layer.(type) {
|
||||||
case *diskLayer:
|
case *diskLayer:
|
||||||
// If the base layer is generating, abort it and save
|
// TODO this function will hang if it's called twice. Will
|
||||||
if layer.genAbort != nil {
|
// fix it in the following PRs.
|
||||||
abort := make(chan *generatorStats)
|
layer.stopGeneration()
|
||||||
layer.genAbort <- abort
|
layer.markStale()
|
||||||
<-abort
|
layer.Release()
|
||||||
}
|
|
||||||
// Layer should be inactive now, mark it as stale
|
|
||||||
layer.lock.Lock()
|
|
||||||
layer.stale = true
|
|
||||||
layer.lock.Unlock()
|
|
||||||
|
|
||||||
case *diffLayer:
|
case *diffLayer:
|
||||||
// If the layer is a simple diff, simply mark as stale
|
// If the layer is a simple diff, simply mark as stale
|
||||||
|
|
|
||||||
|
|
@ -117,15 +117,7 @@ func (s *stateObject) markSelfdestructed() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) touch() {
|
func (s *stateObject) touch() {
|
||||||
s.db.journal.append(touchChange{
|
s.db.journal.touchChange(s.address)
|
||||||
account: &s.address,
|
|
||||||
})
|
|
||||||
|
|
||||||
if s.address == ripemd {
|
|
||||||
// Explicitly put it in the dirty-cache, which is otherwise generated from
|
|
||||||
// flattened journals.
|
|
||||||
s.db.journal.dirty(s.address)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTrie returns the associated storage trie. The trie will be opened if it's
|
// getTrie returns the associated storage trie. The trie will be opened if it's
|
||||||
|
|
@ -155,7 +147,7 @@ func (s *stateObject) getTrie() (Trie, error) {
|
||||||
func (s *stateObject) getPrefetchedTrie() Trie {
|
func (s *stateObject) getPrefetchedTrie() Trie {
|
||||||
// If there's nothing to meaningfully return, let the user figure it out by
|
// If there's nothing to meaningfully return, let the user figure it out by
|
||||||
// pulling the trie from disk.
|
// pulling the trie from disk.
|
||||||
if s.data.Root == types.EmptyRootHash || s.db.prefetcher == nil {
|
if (s.data.Root == types.EmptyRootHash && !s.db.db.TrieDB().IsVerkle()) || s.db.prefetcher == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Attempt to retrieve the trie from the prefetcher
|
// Attempt to retrieve the trie from the prefetcher
|
||||||
|
|
@ -203,46 +195,17 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash {
|
||||||
s.originStorage[key] = common.Hash{} // track the empty slot as origin value
|
s.originStorage[key] = common.Hash{} // track the empty slot as origin value
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
// If no live objects are available, attempt to use snapshots
|
s.db.StorageLoaded++
|
||||||
var (
|
|
||||||
enc []byte
|
|
||||||
err error
|
|
||||||
value common.Hash
|
|
||||||
)
|
|
||||||
|
|
||||||
if s.db.snap != nil {
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
enc, err = s.db.snap.Storage(s.addrHash, crypto.Keccak256Hash(key.Bytes()))
|
value, err := s.db.reader.Storage(s.address, key)
|
||||||
s.db.SnapshotStorageReads += time.Since(start)
|
|
||||||
|
|
||||||
if len(enc) > 0 {
|
|
||||||
_, content, _, err := rlp.Split(enc)
|
|
||||||
if err != nil {
|
|
||||||
s.db.setError(err)
|
|
||||||
}
|
|
||||||
value.SetBytes(content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the snapshot is unavailable or reading from it fails, load from the database.
|
|
||||||
if s.db.snap == nil || err != nil {
|
|
||||||
start := time.Now()
|
|
||||||
tr, err := s.getTrie()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.db.setError(err)
|
s.db.setError(err)
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
val, err := tr.GetStorage(s.address, key.Bytes())
|
|
||||||
s.db.StorageReads += time.Since(start)
|
s.db.StorageReads += time.Since(start)
|
||||||
|
|
||||||
if err != nil {
|
// Schedule the resolved storage slots for prefetching if it's enabled.
|
||||||
s.db.setError(err)
|
|
||||||
return common.Hash{}
|
|
||||||
}
|
|
||||||
value.SetBytes(val)
|
|
||||||
}
|
|
||||||
// Independent of where we loaded the data from, add it to the prefetcher.
|
|
||||||
// Whilst this would be a bit weird if snapshots are disabled, but we still
|
|
||||||
// want the trie nodes to end up in the prefetcher too, so just push through.
|
|
||||||
if s.db.prefetcher != nil && s.data.Root != types.EmptyRootHash {
|
if s.db.prefetcher != nil && s.data.Root != types.EmptyRootHash {
|
||||||
if err = s.db.prefetcher.prefetch(s.addrHash, s.origin.Root, s.address, [][]byte{key[:]}, true); err != nil {
|
if err = s.db.prefetcher.prefetch(s.addrHash, s.origin.Root, s.address, [][]byte{key[:]}, true); err != nil {
|
||||||
log.Error("Failed to prefetch storage slot", "addr", s.address, "key", key, "err", err)
|
log.Error("Failed to prefetch storage slot", "addr", s.address, "key", key, "err", err)
|
||||||
|
|
@ -262,16 +225,11 @@ func (s *stateObject) SetState(key, value common.Hash) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// New value is different, update and journal the change
|
// New value is different, update and journal the change
|
||||||
s.db.journal.append(storageChange{
|
s.db.journal.storageChange(s.address, key, prev, origin)
|
||||||
account: &s.address,
|
s.setState(key, value, origin)
|
||||||
key: key,
|
|
||||||
prevvalue: prev,
|
|
||||||
origvalue: origin,
|
|
||||||
})
|
|
||||||
if s.db.logger != nil && s.db.logger.OnStorageChange != nil {
|
if s.db.logger != nil && s.db.logger.OnStorageChange != nil {
|
||||||
s.db.logger.OnStorageChange(s.address, key, prev, value)
|
s.db.logger.OnStorageChange(s.address, key, prev, value)
|
||||||
}
|
}
|
||||||
s.setState(key, value, origin)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// setState updates a value in account dirty storage. The dirtiness will be
|
// setState updates a value in account dirty storage. The dirtiness will be
|
||||||
|
|
@ -525,10 +483,7 @@ func (s *stateObject) SubBalance(amount *uint256.Int, reason tracing.BalanceChan
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) SetBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) {
|
func (s *stateObject) SetBalance(amount *uint256.Int, reason tracing.BalanceChangeReason) {
|
||||||
s.db.journal.append(balanceChange{
|
s.db.journal.balanceChange(s.address, s.data.Balance)
|
||||||
account: &s.address,
|
|
||||||
prev: new(uint256.Int).Set(s.data.Balance),
|
|
||||||
})
|
|
||||||
if s.db.logger != nil && s.db.logger.OnBalanceChange != nil {
|
if s.db.logger != nil && s.db.logger.OnBalanceChange != nil {
|
||||||
s.db.logger.OnBalanceChange(s.address, s.Balance().ToBig(), amount.ToBig(), reason)
|
s.db.logger.OnBalanceChange(s.address, s.Balance().ToBig(), amount.ToBig(), reason)
|
||||||
}
|
}
|
||||||
|
|
@ -556,7 +511,7 @@ func (s *stateObject) deepCopy(db *StateDB) *stateObject {
|
||||||
newContract: s.newContract,
|
newContract: s.newContract,
|
||||||
}
|
}
|
||||||
if s.trie != nil {
|
if s.trie != nil {
|
||||||
obj.trie = db.db.CopyTrie(s.trie)
|
obj.trie = mustCopyTrie(s.trie)
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
@ -609,14 +564,10 @@ func (s *stateObject) CodeSize() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
func (s *stateObject) SetCode(codeHash common.Hash, code []byte) {
|
||||||
prevcode := s.Code()
|
s.db.journal.setCode(s.address)
|
||||||
s.db.journal.append(codeChange{
|
|
||||||
account: &s.address,
|
|
||||||
prevhash: s.CodeHash(),
|
|
||||||
prevcode: prevcode,
|
|
||||||
})
|
|
||||||
if s.db.logger != nil && s.db.logger.OnCodeChange != nil {
|
if s.db.logger != nil && s.db.logger.OnCodeChange != nil {
|
||||||
s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), prevcode, codeHash, code)
|
// TODO remove prevcode from this callback
|
||||||
|
s.db.logger.OnCodeChange(s.address, common.BytesToHash(s.CodeHash()), nil, codeHash, code)
|
||||||
}
|
}
|
||||||
s.setCode(codeHash, code)
|
s.setCode(codeHash, code)
|
||||||
}
|
}
|
||||||
|
|
@ -628,10 +579,7 @@ func (s *stateObject) setCode(codeHash common.Hash, code []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stateObject) SetNonce(nonce uint64) {
|
func (s *stateObject) SetNonce(nonce uint64) {
|
||||||
s.db.journal.append(nonceChange{
|
s.db.journal.nonceChange(s.address, s.data.Nonce)
|
||||||
account: &s.address,
|
|
||||||
prev: s.data.Nonce,
|
|
||||||
})
|
|
||||||
if s.db.logger != nil && s.db.logger.OnNonceChange != nil {
|
if s.db.logger != nil && s.db.logger.OnNonceChange != nil {
|
||||||
s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce)
|
s.db.logger.OnNonceChange(s.address, s.data.Nonce, nonce)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,27 +26,25 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
type stateEnv struct {
|
type stateEnv struct {
|
||||||
db ethdb.Database
|
|
||||||
state *StateDB
|
state *StateDB
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStateEnv() *stateEnv {
|
func newStateEnv() *stateEnv {
|
||||||
db := rawdb.NewMemoryDatabase()
|
sdb, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
sdb, _ := New(types.EmptyRootHash, NewDatabase(db), nil)
|
return &stateEnv{state: sdb}
|
||||||
return &stateEnv{db: db, state: sdb}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDump(t *testing.T) {
|
func TestDump(t *testing.T) {
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
tdb := NewDatabaseWithConfig(db, &triedb.Config{Preimages: true})
|
triedb := triedb.NewDatabase(db, &triedb.Config{Preimages: true})
|
||||||
sdb, _ := New(types.EmptyRootHash, tdb, nil)
|
tdb := NewDatabase(triedb, nil)
|
||||||
s := &stateEnv{db: db, state: sdb}
|
sdb, _ := New(types.EmptyRootHash, tdb)
|
||||||
|
s := &stateEnv{state: sdb}
|
||||||
|
|
||||||
// generate a few entries
|
// generate a few entries
|
||||||
obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01}))
|
obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01}))
|
||||||
|
|
@ -62,7 +60,7 @@ func TestDump(t *testing.T) {
|
||||||
root, _ := s.state.Commit(0, false)
|
root, _ := s.state.Commit(0, false)
|
||||||
|
|
||||||
// check that DumpToCollector contains the state objects that are in trie
|
// check that DumpToCollector contains the state objects that are in trie
|
||||||
s.state, _ = New(root, tdb, nil)
|
s.state, _ = New(root, tdb)
|
||||||
got := string(s.state.Dump(nil))
|
got := string(s.state.Dump(nil))
|
||||||
want := `{
|
want := `{
|
||||||
"root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2",
|
"root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2",
|
||||||
|
|
@ -101,9 +99,10 @@ func TestDump(t *testing.T) {
|
||||||
|
|
||||||
func TestIterativeDump(t *testing.T) {
|
func TestIterativeDump(t *testing.T) {
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
tdb := NewDatabaseWithConfig(db, &triedb.Config{Preimages: true})
|
triedb := triedb.NewDatabase(db, &triedb.Config{Preimages: true})
|
||||||
sdb, _ := New(types.EmptyRootHash, tdb, nil)
|
tdb := NewDatabase(triedb, nil)
|
||||||
s := &stateEnv{db: db, state: sdb}
|
sdb, _ := New(types.EmptyRootHash, tdb)
|
||||||
|
s := &stateEnv{state: sdb}
|
||||||
|
|
||||||
// generate a few entries
|
// generate a few entries
|
||||||
obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01}))
|
obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01}))
|
||||||
|
|
@ -119,7 +118,7 @@ func TestIterativeDump(t *testing.T) {
|
||||||
s.state.updateStateObject(obj1)
|
s.state.updateStateObject(obj1)
|
||||||
s.state.updateStateObject(obj2)
|
s.state.updateStateObject(obj2)
|
||||||
root, _ := s.state.Commit(0, false)
|
root, _ := s.state.Commit(0, false)
|
||||||
s.state, _ = New(root, tdb, nil)
|
s.state, _ = New(root, tdb)
|
||||||
|
|
||||||
b := &bytes.Buffer{}
|
b := &bytes.Buffer{}
|
||||||
s.state.IterativeDump(nil, json.NewEncoder(b))
|
s.state.IterativeDump(nil, json.NewEncoder(b))
|
||||||
|
|
@ -195,7 +194,7 @@ func TestSnapshotEmpty(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateObjectRevert(t *testing.T) {
|
func TestCreateObjectRevert(t *testing.T) {
|
||||||
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
state, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
addr := common.BytesToAddress([]byte("so0"))
|
addr := common.BytesToAddress([]byte("so0"))
|
||||||
snap := state.Snapshot()
|
snap := state.Snapshot()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"maps"
|
"maps"
|
||||||
"math/big"
|
"math/big"
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -50,11 +49,6 @@ import (
|
||||||
// TriesInMemory represents the number of layers that are kept in RAM.
|
// TriesInMemory represents the number of layers that are kept in RAM.
|
||||||
const TriesInMemory = 128
|
const TriesInMemory = 128
|
||||||
|
|
||||||
type revision struct {
|
|
||||||
id int
|
|
||||||
journalIndex int
|
|
||||||
}
|
|
||||||
|
|
||||||
type mutationType int
|
type mutationType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -90,10 +84,8 @@ type StateDB struct {
|
||||||
db Database
|
db Database
|
||||||
prefetcher *triePrefetcher
|
prefetcher *triePrefetcher
|
||||||
trie Trie
|
trie Trie
|
||||||
hasher crypto.KeccakState
|
|
||||||
logger *tracing.Hooks
|
logger *tracing.Hooks
|
||||||
snaps *snapshot.Tree // Nil if snapshot is not available
|
reader Reader
|
||||||
snap snapshot.Snapshot // Nil if snapshot is not available
|
|
||||||
|
|
||||||
// originalRoot is the pre-state root, before any changes were made.
|
// originalRoot is the pre-state root, before any changes were made.
|
||||||
// It will be updated when the Commit is called.
|
// It will be updated when the Commit is called.
|
||||||
|
|
@ -147,6 +139,7 @@ type StateDB struct {
|
||||||
|
|
||||||
// Per-transaction access list
|
// Per-transaction access list
|
||||||
accessList *accessList
|
accessList *accessList
|
||||||
|
accessEvents *AccessEvents
|
||||||
|
|
||||||
// Transient storage
|
// Transient storage
|
||||||
transientStorage transientStorage
|
transientStorage transientStorage
|
||||||
|
|
@ -154,13 +147,18 @@ type StateDB struct {
|
||||||
// Journal of state modifications. This is the backbone of
|
// Journal of state modifications. This is the backbone of
|
||||||
// Snapshot and RevertToSnapshot.
|
// Snapshot and RevertToSnapshot.
|
||||||
journal *journal
|
journal *journal
|
||||||
validRevisions []revision
|
|
||||||
nextRevisionId int
|
|
||||||
|
|
||||||
// State witness if cross validation is needed
|
// State witness if cross validation is needed
|
||||||
witness *stateless.Witness
|
witness *stateless.Witness
|
||||||
|
|
||||||
// Measurements gathered during execution for debugging purposes
|
// Measurements gathered during execution for debugging purposes
|
||||||
|
|
||||||
|
AccountLoaded int // Number of accounts retrieved from the database during the state transition
|
||||||
|
AccountUpdated int // Number of accounts updated during the state transition
|
||||||
|
AccountDeleted int // Number of accounts deleted during the state transition
|
||||||
|
StorageLoaded int // Number of storage slots retrieved from the database during the state transition
|
||||||
|
StorageUpdated atomic.Int64 // Number of storage slots updated during the state transition
|
||||||
|
StorageDeleted atomic.Int64 // Number of storage slots deleted during the state transition
|
||||||
AccountReads time.Duration
|
AccountReads time.Duration
|
||||||
AccountHashes time.Duration
|
AccountHashes time.Duration
|
||||||
AccountUpdates time.Duration
|
AccountUpdates time.Duration
|
||||||
|
|
@ -176,25 +174,24 @@ type StateDB struct {
|
||||||
|
|
||||||
// Bor metrics
|
// Bor metrics
|
||||||
BorConsensusTime time.Duration
|
BorConsensusTime time.Duration
|
||||||
|
|
||||||
AccountUpdated int
|
|
||||||
StorageUpdated atomic.Int64
|
|
||||||
AccountDeleted int
|
|
||||||
StorageDeleted atomic.Int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new state from a given trie.
|
// New creates a new state from a given trie.
|
||||||
func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) {
|
func New(root common.Hash, db Database) (*StateDB, error) {
|
||||||
tr, err := db.OpenTrie(root)
|
tr, err := db.OpenTrie(root)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
reader, err := db.Reader(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
sdb := &StateDB{
|
sdb := &StateDB{
|
||||||
db: db,
|
db: db,
|
||||||
trie: tr,
|
trie: tr,
|
||||||
originalRoot: root,
|
originalRoot: root,
|
||||||
snaps: snaps,
|
reader: reader,
|
||||||
stateObjects: make(map[common.Address]*stateObject),
|
stateObjects: make(map[common.Address]*stateObject),
|
||||||
revertedKeys: make(map[blockstm.Key]struct{}),
|
revertedKeys: make(map[blockstm.Key]struct{}),
|
||||||
stateObjectsDestruct: make(map[common.Address]*stateObject),
|
stateObjectsDestruct: make(map[common.Address]*stateObject),
|
||||||
|
|
@ -204,17 +201,16 @@ func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error)
|
||||||
journal: newJournal(),
|
journal: newJournal(),
|
||||||
accessList: newAccessList(),
|
accessList: newAccessList(),
|
||||||
transientStorage: newTransientStorage(),
|
transientStorage: newTransientStorage(),
|
||||||
hasher: crypto.NewKeccakState(),
|
|
||||||
}
|
}
|
||||||
if sdb.snaps != nil {
|
if db.TrieDB().IsVerkle() {
|
||||||
sdb.snap = sdb.snaps.Snapshot(root)
|
sdb.accessEvents = NewAccessEvents(db.PointCache())
|
||||||
}
|
}
|
||||||
|
|
||||||
return sdb, nil
|
return sdb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWithMVHashmap(root common.Hash, db Database, snaps *snapshot.Tree, mvhm *blockstm.MVHashMap) (*StateDB, error) {
|
func NewWithMVHashmap(root common.Hash, db Database, snaps *snapshot.Tree, mvhm *blockstm.MVHashMap) (*StateDB, error) {
|
||||||
if sdb, err := New(root, db, snaps); err != nil {
|
if sdb, err := New(root, db); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
sdb.mvHashmap = mvhm
|
sdb.mvHashmap = mvhm
|
||||||
|
|
@ -503,18 +499,11 @@ func (s *StateDB) SetLogger(l *tracing.Hooks) {
|
||||||
// commit phase, most of the needed data is already hot.
|
// commit phase, most of the needed data is already hot.
|
||||||
func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness) {
|
func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness) {
|
||||||
// Terminate any previously running prefetcher
|
// Terminate any previously running prefetcher
|
||||||
if s.prefetcher != nil {
|
s.StopPrefetcher()
|
||||||
s.prefetcher.terminate(false)
|
|
||||||
s.prefetcher.report()
|
|
||||||
s.prefetcher = nil
|
|
||||||
}
|
|
||||||
// Enable witness collection if requested
|
// Enable witness collection if requested
|
||||||
s.witness = witness
|
s.witness = witness
|
||||||
|
|
||||||
// If snapshots are enabled, start prefethers explicitly
|
|
||||||
if s.snap != nil {
|
|
||||||
s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace, witness == nil)
|
|
||||||
|
|
||||||
// With the switch to the Proof-of-Stake consensus algorithm, block production
|
// With the switch to the Proof-of-Stake consensus algorithm, block production
|
||||||
// rewards are now handled at the consensus layer. Consequently, a block may
|
// rewards are now handled at the consensus layer. Consequently, a block may
|
||||||
// have no state transitions if it contains no transactions and no withdrawals.
|
// have no state transitions if it contains no transactions and no withdrawals.
|
||||||
|
|
@ -524,11 +513,11 @@ func (s *StateDB) StartPrefetcher(namespace string, witness *stateless.Witness)
|
||||||
// To prevent this, the account trie is always scheduled for prefetching once
|
// To prevent this, the account trie is always scheduled for prefetching once
|
||||||
// the prefetcher is constructed. For more details, see:
|
// the prefetcher is constructed. For more details, see:
|
||||||
// https://github.com/ethereum/go-ethereum/issues/29880
|
// https://github.com/ethereum/go-ethereum/issues/29880
|
||||||
|
s.prefetcher = newTriePrefetcher(s.db, s.originalRoot, namespace, witness == nil)
|
||||||
if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, nil, false); err != nil {
|
if err := s.prefetcher.prefetch(common.Hash{}, s.originalRoot, common.Address{}, nil, false); err != nil {
|
||||||
log.Error("Failed to prefetch account trie", "root", s.originalRoot, "err", err)
|
log.Error("Failed to prefetch account trie", "root", s.originalRoot, "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// StopPrefetcher terminates a running prefetcher and reports any leftover stats
|
// StopPrefetcher terminates a running prefetcher and reports any leftover stats
|
||||||
// from the gathered metrics.
|
// from the gathered metrics.
|
||||||
|
|
@ -553,7 +542,7 @@ func (s *StateDB) Error() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) AddLog(log *types.Log) {
|
func (s *StateDB) AddLog(log *types.Log) {
|
||||||
s.journal.append(addLogChange{txhash: s.thash})
|
s.journal.logChange(s.thash)
|
||||||
|
|
||||||
log.TxHash = s.thash
|
log.TxHash = s.thash
|
||||||
log.TxIndex = uint(s.txIndex)
|
log.TxIndex = uint(s.txIndex)
|
||||||
|
|
@ -589,7 +578,6 @@ func (s *StateDB) Logs() []*types.Log {
|
||||||
// AddPreimage records a SHA3 preimage seen by the VM.
|
// AddPreimage records a SHA3 preimage seen by the VM.
|
||||||
func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
|
func (s *StateDB) AddPreimage(hash common.Hash, preimage []byte) {
|
||||||
if _, ok := s.preimages[hash]; !ok {
|
if _, ok := s.preimages[hash]; !ok {
|
||||||
s.journal.append(addPreimageChange{hash: hash})
|
|
||||||
s.preimages[hash] = slices.Clone(preimage)
|
s.preimages[hash] = slices.Clone(preimage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -601,15 +589,14 @@ func (s *StateDB) Preimages() map[common.Hash][]byte {
|
||||||
|
|
||||||
// AddRefund adds gas to the refund counter
|
// AddRefund adds gas to the refund counter
|
||||||
func (s *StateDB) AddRefund(gas uint64) {
|
func (s *StateDB) AddRefund(gas uint64) {
|
||||||
s.journal.append(refundChange{prev: s.refund})
|
s.journal.refundChange(s.refund)
|
||||||
s.refund += gas
|
s.refund += gas
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubRefund removes gas from the refund counter.
|
// SubRefund removes gas from the refund counter.
|
||||||
// This method will panic if the refund counter goes below zero
|
// This method will panic if the refund counter goes below zero
|
||||||
func (s *StateDB) SubRefund(gas uint64) {
|
func (s *StateDB) SubRefund(gas uint64) {
|
||||||
s.journal.append(refundChange{prev: s.refund})
|
s.journal.refundChange(s.refund)
|
||||||
|
|
||||||
if gas > s.refund {
|
if gas > s.refund {
|
||||||
panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund))
|
panic(fmt.Sprintf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund))
|
||||||
}
|
}
|
||||||
|
|
@ -892,24 +879,19 @@ func (s *StateDB) SelfDestruct(addr common.Address) {
|
||||||
if stateObject == nil {
|
if stateObject == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
stateObject = s.mvRecordWritten(stateObject)
|
stateObject = s.mvRecordWritten(stateObject)
|
||||||
var (
|
|
||||||
prev = new(uint256.Int).Set(stateObject.Balance())
|
// Regardless of whether it is already destructed or not, we do have to
|
||||||
n = new(uint256.Int)
|
// journal the balance-change, if we set it to zero here.
|
||||||
)
|
if !stateObject.Balance().IsZero() {
|
||||||
s.journal.append(selfDestructChange{
|
stateObject.SetBalance(new(uint256.Int), tracing.BalanceDecreaseSelfdestruct)
|
||||||
account: &addr,
|
|
||||||
prev: stateObject.selfDestructed,
|
|
||||||
prevbalance: prev,
|
|
||||||
})
|
|
||||||
if s.logger != nil && s.logger.OnBalanceChange != nil && prev.Sign() > 0 {
|
|
||||||
s.logger.OnBalanceChange(addr, prev.ToBig(), n.ToBig(), tracing.BalanceDecreaseSelfdestruct)
|
|
||||||
}
|
}
|
||||||
|
// If it is already marked as self-destructed, we do not need to add it
|
||||||
|
// for journalling a second time.
|
||||||
|
if !stateObject.selfDestructed {
|
||||||
|
s.journal.destruct(addr)
|
||||||
stateObject.markSelfdestructed()
|
stateObject.markSelfdestructed()
|
||||||
|
}
|
||||||
stateObject.data.Balance = n
|
|
||||||
|
|
||||||
MVWrite(s, blockstm.NewSubpathKey(addr, SuicidePath))
|
MVWrite(s, blockstm.NewSubpathKey(addr, SuicidePath))
|
||||||
MVWrite(s, blockstm.NewSubpathKey(addr, BalancePath))
|
MVWrite(s, blockstm.NewSubpathKey(addr, BalancePath))
|
||||||
}
|
}
|
||||||
|
|
@ -932,12 +914,7 @@ func (s *StateDB) SetTransientState(addr common.Address, key, value common.Hash)
|
||||||
if prev == value {
|
if prev == value {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
s.journal.transientStateChange(addr, key, prev)
|
||||||
s.journal.append(transientStorageChange{
|
|
||||||
account: &addr,
|
|
||||||
key: key,
|
|
||||||
prevalue: prev,
|
|
||||||
})
|
|
||||||
s.setTransientState(addr, key, value)
|
s.setTransientState(addr, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -960,7 +937,7 @@ func (s *StateDB) GetTransientState(addr common.Address, key common.Hash) common
|
||||||
func (s *StateDB) updateStateObject(obj *stateObject) {
|
func (s *StateDB) updateStateObject(obj *stateObject) {
|
||||||
// Encode the account and update the account trie
|
// Encode the account and update the account trie
|
||||||
addr := obj.Address()
|
addr := obj.Address()
|
||||||
if err := s.trie.UpdateAccount(addr, &obj.data); err != nil {
|
if err := s.trie.UpdateAccount(addr, &obj.data, len(obj.code)); err != nil {
|
||||||
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err))
|
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err))
|
||||||
}
|
}
|
||||||
if obj.dirtyCode {
|
if obj.dirtyCode {
|
||||||
|
|
@ -993,45 +970,19 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
||||||
if _, ok := s.stateObjectsDestruct[addr]; ok {
|
if _, ok := s.stateObjectsDestruct[addr]; ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// If no live objects are available, attempt to use snapshots
|
s.AccountLoaded++
|
||||||
var data *types.StateAccount
|
|
||||||
if s.snap != nil {
|
|
||||||
start := time.Now()
|
|
||||||
acc, err := s.snap.Account(crypto.HashData(crypto.NewKeccakState(), addr.Bytes()))
|
|
||||||
s.SnapshotAccountReads += time.Since(start)
|
|
||||||
if err == nil {
|
|
||||||
if acc == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
data = &types.StateAccount{
|
|
||||||
Nonce: acc.Nonce,
|
|
||||||
Balance: acc.Balance,
|
|
||||||
CodeHash: acc.CodeHash,
|
|
||||||
Root: common.BytesToHash(acc.Root),
|
|
||||||
}
|
|
||||||
if len(data.CodeHash) == 0 {
|
|
||||||
data.CodeHash = types.EmptyCodeHash.Bytes()
|
|
||||||
}
|
|
||||||
if data.Root == (common.Hash{}) {
|
|
||||||
data.Root = types.EmptyRootHash
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If snapshot unavailable or reading from it failed, load from the database
|
|
||||||
if data == nil {
|
|
||||||
start := time.Now()
|
|
||||||
var err error
|
|
||||||
data, err = s.trie.GetAccount(addr)
|
|
||||||
s.AccountReads += time.Since(start)
|
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
acct, err := s.reader.Account(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.setError(fmt.Errorf("getDeleteStateObject (%x) error: %w", addr.Bytes(), err))
|
s.setError(fmt.Errorf("getStateObject (%x) error: %w", addr.Bytes(), err))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if data == nil {
|
s.AccountReads += time.Since(start)
|
||||||
|
// Short circuit if the account is not found
|
||||||
|
if acct == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Independent of where we loaded the data from, add it to the prefetcher.
|
// Independent of where we loaded the data from, add it to the prefetcher.
|
||||||
// Whilst this would be a bit weird if snapshots are disabled, but we still
|
// Whilst this would be a bit weird if snapshots are disabled, but we still
|
||||||
// want the trie nodes to end up in the prefetcher too, so just push through.
|
// want the trie nodes to end up in the prefetcher too, so just push through.
|
||||||
|
|
@ -1041,7 +992,7 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Insert into the live set
|
// Insert into the live set
|
||||||
obj := newObject(s, addr, data)
|
obj := newObject(s, addr, acct)
|
||||||
s.setStateObject(obj)
|
s.setStateObject(obj)
|
||||||
return obj
|
return obj
|
||||||
})
|
})
|
||||||
|
|
@ -1091,7 +1042,7 @@ func (s *StateDB) mvRecordWritten(object *stateObject) *stateObject {
|
||||||
// existing account with the given address, otherwise it will be silently overwritten.
|
// existing account with the given address, otherwise it will be silently overwritten.
|
||||||
func (s *StateDB) createObject(addr common.Address) *stateObject {
|
func (s *StateDB) createObject(addr common.Address) *stateObject {
|
||||||
obj := newObject(s, addr, nil)
|
obj := newObject(s, addr, nil)
|
||||||
s.journal.append(createObjectChange{account: &addr})
|
s.journal.createObject(addr)
|
||||||
s.setStateObject(obj)
|
s.setStateObject(obj)
|
||||||
MVWrite(s, blockstm.NewAddressKey(addr))
|
MVWrite(s, blockstm.NewAddressKey(addr))
|
||||||
return obj
|
return obj
|
||||||
|
|
@ -1117,7 +1068,7 @@ func (s *StateDB) CreateContract(addr common.Address) {
|
||||||
}
|
}
|
||||||
if !obj.newContract {
|
if !obj.newContract {
|
||||||
obj.newContract = true
|
obj.newContract = true
|
||||||
s.journal.append(createContractChange{account: addr})
|
s.journal.createContract(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
MVWrite(s, blockstm.NewAddressKey(addr))
|
MVWrite(s, blockstm.NewAddressKey(addr))
|
||||||
|
|
@ -1129,8 +1080,8 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
// Copy all the basic fields, initialize the memory ones
|
// Copy all the basic fields, initialize the memory ones
|
||||||
state := &StateDB{
|
state := &StateDB{
|
||||||
db: s.db,
|
db: s.db,
|
||||||
trie: s.db.CopyTrie(s.trie),
|
trie: mustCopyTrie(s.trie),
|
||||||
hasher: crypto.NewKeccakState(),
|
reader: s.reader.Copy(),
|
||||||
originalRoot: s.originalRoot,
|
originalRoot: s.originalRoot,
|
||||||
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
|
stateObjects: make(map[common.Address]*stateObject, len(s.stateObjects)),
|
||||||
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
|
stateObjectsDestruct: make(map[common.Address]*stateObject, len(s.stateObjectsDestruct)),
|
||||||
|
|
@ -1143,20 +1094,23 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
logs: make(map[common.Hash][]*types.Log, len(s.logs)),
|
||||||
logSize: s.logSize,
|
logSize: s.logSize,
|
||||||
preimages: maps.Clone(s.preimages),
|
preimages: maps.Clone(s.preimages),
|
||||||
journal: s.journal.copy(),
|
|
||||||
validRevisions: slices.Clone(s.validRevisions),
|
|
||||||
nextRevisionId: s.nextRevisionId,
|
|
||||||
|
|
||||||
// In order for the block producer to be able to use and make additions
|
// Do we need to copy the access list and transient storage?
|
||||||
// to the snapshot tree, we need to copy that as well. Otherwise, any
|
// In practice: No. At the start of a transaction, these two lists are empty.
|
||||||
// block mined by ourselves will cause gaps in the tree, and force the
|
// In practice, we only ever copy state _between_ transactions/blocks, never
|
||||||
// miner to operate trie-backed only.
|
// in the middle of a transaction. However, it doesn't cost us much to copy
|
||||||
snaps: s.snaps,
|
// empty lists, so we do it anyway to not blow up if we ever decide copy them
|
||||||
snap: s.snap,
|
// in the middle of a transaction.
|
||||||
|
accessList: s.accessList.Copy(),
|
||||||
|
transientStorage: s.transientStorage.Copy(),
|
||||||
|
journal: s.journal.copy(),
|
||||||
}
|
}
|
||||||
if s.witness != nil {
|
if s.witness != nil {
|
||||||
state.witness = s.witness.Copy()
|
state.witness = s.witness.Copy()
|
||||||
}
|
}
|
||||||
|
if s.accessEvents != nil {
|
||||||
|
state.accessEvents = s.accessEvents.Copy()
|
||||||
|
}
|
||||||
// Deep copy cached state objects.
|
// Deep copy cached state objects.
|
||||||
for addr, obj := range s.stateObjects {
|
for addr, obj := range s.stateObjects {
|
||||||
state.stateObjects[addr] = obj.deepCopy(state)
|
state.stateObjects[addr] = obj.deepCopy(state)
|
||||||
|
|
@ -1201,28 +1155,12 @@ func (s *StateDB) Copy() *StateDB {
|
||||||
|
|
||||||
// Snapshot returns an identifier for the current revision of the state.
|
// Snapshot returns an identifier for the current revision of the state.
|
||||||
func (s *StateDB) Snapshot() int {
|
func (s *StateDB) Snapshot() int {
|
||||||
id := s.nextRevisionId
|
return s.journal.snapshot()
|
||||||
s.nextRevisionId++
|
|
||||||
s.validRevisions = append(s.validRevisions, revision{id, s.journal.length()})
|
|
||||||
|
|
||||||
return id
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RevertToSnapshot reverts all state changes made since the given revision.
|
// RevertToSnapshot reverts all state changes made since the given revision.
|
||||||
func (s *StateDB) RevertToSnapshot(revid int) {
|
func (s *StateDB) RevertToSnapshot(revid int) {
|
||||||
// Find the snapshot in the stack of valid snapshots.
|
s.journal.revertToSnapshot(revid, s)
|
||||||
idx := sort.Search(len(s.validRevisions), func(i int) bool {
|
|
||||||
return s.validRevisions[i].id >= revid
|
|
||||||
})
|
|
||||||
if idx == len(s.validRevisions) || s.validRevisions[idx].id != revid {
|
|
||||||
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshot := s.validRevisions[idx].journalIndex
|
|
||||||
|
|
||||||
// Replay the journal to undo changes and remove invalidated snapshots
|
|
||||||
s.journal.revert(s, snapshot)
|
|
||||||
s.validRevisions = s.validRevisions[:idx]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRefund returns the current value of the refund counter.
|
// GetRefund returns the current value of the refund counter.
|
||||||
|
|
@ -1332,8 +1270,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// If witness building is enabled, gather all the read-only accesses
|
// If witness building is enabled, gather all the read-only accesses.
|
||||||
if s.witness != nil {
|
// Skip witness collection in Verkle mode, they will be gathered
|
||||||
|
// together at the end.
|
||||||
|
if s.witness != nil && !s.db.TrieDB().IsVerkle() {
|
||||||
// Pull in anything that has been accessed before destruction
|
// Pull in anything that has been accessed before destruction
|
||||||
for _, obj := range s.stateObjectsDestruct {
|
for _, obj := range s.stateObjectsDestruct {
|
||||||
// Skip any objects that haven't touched their storage
|
// Skip any objects that haven't touched their storage
|
||||||
|
|
@ -1374,7 +1314,7 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||||
// only a single trie is used for state hashing. Replacing a non-nil verkle tree
|
// only a single trie is used for state hashing. Replacing a non-nil verkle tree
|
||||||
// here could result in losing uncommitted changes from storage.
|
// here could result in losing uncommitted changes from storage.
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
if s.prefetcher != nil && (s.trie == nil || !s.trie.IsVerkle()) {
|
if s.prefetcher != nil {
|
||||||
if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil {
|
if trie := s.prefetcher.trie(common.Hash{}, s.originalRoot); trie == nil {
|
||||||
log.Error("Failed to retrieve account pre-fetcher trie")
|
log.Error("Failed to retrieve account pre-fetcher trie")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1440,20 +1380,16 @@ func (s *StateDB) SetTxContext(thash common.Hash, ti int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) clearJournalAndRefund() {
|
func (s *StateDB) clearJournalAndRefund() {
|
||||||
if len(s.journal.entries) > 0 {
|
s.journal.reset()
|
||||||
s.journal = newJournal()
|
|
||||||
s.refund = 0
|
s.refund = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
s.validRevisions = s.validRevisions[:0] // Snapshots can be created without journal entries
|
|
||||||
}
|
|
||||||
|
|
||||||
// fastDeleteStorage is the function that efficiently deletes the storage trie
|
// fastDeleteStorage is the function that efficiently deletes the storage trie
|
||||||
// of a specific account. It leverages the associated state snapshot for fast
|
// of a specific account. It leverages the associated state snapshot for fast
|
||||||
// storage iteration and constructs trie node deletion markers by creating
|
// storage iteration and constructs trie node deletion markers by creating
|
||||||
// stack trie with iterated slots.
|
// stack trie with iterated slots.
|
||||||
func (s *StateDB) fastDeleteStorage(addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) {
|
func (s *StateDB) fastDeleteStorage(snaps *snapshot.Tree, addrHash common.Hash, root common.Hash) (map[common.Hash][]byte, *trienode.NodeSet, error) {
|
||||||
iter, err := s.snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{})
|
iter, err := snaps.StorageIterator(s.originalRoot, addrHash, common.Hash{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1531,10 +1467,11 @@ func (s *StateDB) deleteStorage(addr common.Address, addrHash common.Hash, root
|
||||||
// The fast approach can be failed if the snapshot is not fully
|
// The fast approach can be failed if the snapshot is not fully
|
||||||
// generated, or it's internally corrupted. Fallback to the slow
|
// generated, or it's internally corrupted. Fallback to the slow
|
||||||
// one just in case.
|
// one just in case.
|
||||||
if s.snap != nil {
|
snaps := s.db.Snapshot()
|
||||||
slots, nodes, err = s.fastDeleteStorage(addrHash, root)
|
if snaps != nil {
|
||||||
|
slots, nodes, err = s.fastDeleteStorage(snaps, addrHash, root)
|
||||||
}
|
}
|
||||||
if s.snap == nil || err != nil {
|
if snaps == nil || err != nil {
|
||||||
slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root)
|
slots, nodes, err = s.slowDeleteStorage(addr, addrHash, root)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1738,6 +1675,8 @@ func (s *StateDB) commit(deleteEmptyObjects bool) (*stateUpdate, error) {
|
||||||
if err := workers.Wait(); err != nil {
|
if err := workers.Wait(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
accountReadMeters.Mark(int64(s.AccountLoaded))
|
||||||
|
storageReadMeters.Mark(int64(s.StorageLoaded))
|
||||||
accountUpdatedMeter.Mark(int64(s.AccountUpdated))
|
accountUpdatedMeter.Mark(int64(s.AccountUpdated))
|
||||||
storageUpdatedMeter.Mark(s.StorageUpdated.Load())
|
storageUpdatedMeter.Mark(s.StorageUpdated.Load())
|
||||||
accountDeletedMeter.Mark(int64(s.AccountDeleted))
|
accountDeletedMeter.Mark(int64(s.AccountDeleted))
|
||||||
|
|
@ -1746,7 +1685,10 @@ func (s *StateDB) commit(deleteEmptyObjects bool) (*stateUpdate, error) {
|
||||||
accountTrieDeletedMeter.Mark(int64(accountTrieNodesDeleted))
|
accountTrieDeletedMeter.Mark(int64(accountTrieNodesDeleted))
|
||||||
storageTriesUpdatedMeter.Mark(int64(storageTrieNodesUpdated))
|
storageTriesUpdatedMeter.Mark(int64(storageTrieNodesUpdated))
|
||||||
storageTriesDeletedMeter.Mark(int64(storageTrieNodesDeleted))
|
storageTriesDeletedMeter.Mark(int64(storageTrieNodesDeleted))
|
||||||
s.AccountUpdated, s.AccountDeleted = 0, 0
|
|
||||||
|
// Clear the metric markers
|
||||||
|
s.AccountLoaded, s.AccountUpdated, s.AccountDeleted = 0, 0, 0
|
||||||
|
s.StorageLoaded = 0
|
||||||
s.StorageUpdated.Store(0)
|
s.StorageUpdated.Store(0)
|
||||||
s.StorageDeleted.Store(0)
|
s.StorageDeleted.Store(0)
|
||||||
|
|
||||||
|
|
@ -1767,7 +1709,7 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool) (*stateU
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Commit dirty contract code if any exists
|
// Commit dirty contract code if any exists
|
||||||
if db := s.db.DiskDB(); db != nil && len(ret.codes) > 0 {
|
if db := s.db.TrieDB().Disk(); db != nil && len(ret.codes) > 0 {
|
||||||
batch := db.NewBatch()
|
batch := db.NewBatch()
|
||||||
for _, code := range ret.codes {
|
for _, code := range ret.codes {
|
||||||
rawdb.WriteCode(batch, code.hash, code.blob)
|
rawdb.WriteCode(batch, code.hash, code.blob)
|
||||||
|
|
@ -1778,18 +1720,16 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool) (*stateU
|
||||||
}
|
}
|
||||||
if !ret.empty() {
|
if !ret.empty() {
|
||||||
// If snapshotting is enabled, update the snapshot tree with this new version
|
// If snapshotting is enabled, update the snapshot tree with this new version
|
||||||
if s.snap != nil {
|
if snap := s.db.Snapshot(); snap != nil && snap.Snapshot(ret.originRoot) != nil {
|
||||||
s.snap = nil
|
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
if err := s.snaps.Update(ret.root, ret.originRoot, ret.destructs, ret.accounts, ret.storages); err != nil {
|
if err := snap.Update(ret.root, ret.originRoot, ret.destructs, ret.accounts, ret.storages); err != nil {
|
||||||
log.Warn("Failed to update snapshot tree", "from", ret.originRoot, "to", ret.root, "err", err)
|
log.Warn("Failed to update snapshot tree", "from", ret.originRoot, "to", ret.root, "err", err)
|
||||||
}
|
}
|
||||||
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
// Keep 128 diff layers in the memory, persistent layer is 129th.
|
||||||
// - head layer is paired with HEAD state
|
// - head layer is paired with HEAD state
|
||||||
// - head-1 layer is paired with HEAD-1 state
|
// - head-1 layer is paired with HEAD-1 state
|
||||||
// - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state
|
// - head-127 layer(bottom-most diff layer) is paired with HEAD-127 state
|
||||||
if err := s.snaps.Cap(ret.root, TriesInMemory); err != nil {
|
if err := snap.Cap(ret.root, TriesInMemory); err != nil {
|
||||||
log.Warn("Failed to cap snapshot tree", "root", ret.root, "layers", TriesInMemory, "err", err)
|
log.Warn("Failed to cap snapshot tree", "root", ret.root, "layers", TriesInMemory, "err", err)
|
||||||
}
|
}
|
||||||
s.SnapshotCommits += time.Since(start)
|
s.SnapshotCommits += time.Since(start)
|
||||||
|
|
@ -1804,6 +1744,7 @@ func (s *StateDB) commitAndFlush(block uint64, deleteEmptyObjects bool) (*stateU
|
||||||
s.TrieDBCommits += time.Since(start)
|
s.TrieDBCommits += time.Since(start)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.reader, _ = s.db.Reader(s.originalRoot)
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1875,7 +1816,7 @@ func (s *StateDB) Prepare(rules params.Rules, sender, coinbase common.Address, d
|
||||||
// AddAddressToAccessList adds the given address to the access list
|
// AddAddressToAccessList adds the given address to the access list
|
||||||
func (s *StateDB) AddAddressToAccessList(addr common.Address) {
|
func (s *StateDB) AddAddressToAccessList(addr common.Address) {
|
||||||
if s.accessList.AddAddress(addr) {
|
if s.accessList.AddAddress(addr) {
|
||||||
s.journal.append(accessListAddAccountChange{&addr})
|
s.journal.accessListAddAccount(addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1887,14 +1828,11 @@ func (s *StateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
|
||||||
// scope of 'address' without having the 'address' become already added
|
// scope of 'address' without having the 'address' become already added
|
||||||
// to the access list (via call-variant, create, etc).
|
// to the access list (via call-variant, create, etc).
|
||||||
// Better safe than sorry, though
|
// Better safe than sorry, though
|
||||||
s.journal.append(accessListAddAccountChange{&addr})
|
s.journal.accessListAddAccount(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if slotMod {
|
if slotMod {
|
||||||
s.journal.append(accessListAddSlotChange{
|
s.journal.accessListAddSlot(addr, slot)
|
||||||
address: &addr,
|
|
||||||
slot: &slot,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1960,6 +1898,7 @@ func (s *StateDB) markUpdate(addr common.Address) {
|
||||||
s.mutations[addr].typ = update
|
s.mutations[addr].typ = update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PointCache returns the point cache used by verkle tree.
|
||||||
func (s *StateDB) PointCache() *utils.PointCache {
|
func (s *StateDB) PointCache() *utils.PointCache {
|
||||||
return s.db.PointCache()
|
return s.db.PointCache()
|
||||||
}
|
}
|
||||||
|
|
@ -1968,3 +1907,7 @@ func (s *StateDB) PointCache() *utils.PointCache {
|
||||||
func (s *StateDB) Witness() *stateless.Witness {
|
func (s *StateDB) Witness() *stateless.Witness {
|
||||||
return s.witness
|
return s.witness
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *StateDB) AccessEvents() *AccessEvents {
|
||||||
|
return s.accessEvents
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction
|
||||||
args: make([]int64, 1),
|
args: make([]int64, 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SetState",
|
name: "SetStorage",
|
||||||
fn: func(a testAction, s *StateDB) {
|
fn: func(a testAction, s *StateDB) {
|
||||||
var key, val common.Hash
|
var key, val common.Hash
|
||||||
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
||||||
|
|
@ -197,7 +197,6 @@ func (test *stateTest) run() bool {
|
||||||
}
|
}
|
||||||
disk = rawdb.NewMemoryDatabase()
|
disk = rawdb.NewMemoryDatabase()
|
||||||
tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults})
|
tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults})
|
||||||
sdb = NewDatabaseWithNodeDB(disk, tdb)
|
|
||||||
byzantium = rand.Intn(2) == 0
|
byzantium = rand.Intn(2) == 0
|
||||||
)
|
)
|
||||||
defer disk.Close()
|
defer disk.Close()
|
||||||
|
|
@ -217,7 +216,7 @@ func (test *stateTest) run() bool {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
root = roots[len(roots)-1]
|
root = roots[len(roots)-1]
|
||||||
}
|
}
|
||||||
state, err := New(root, sdb, snaps)
|
state, err := New(root, NewDatabase(tdb, snaps))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ package state
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
"maps"
|
||||||
"math"
|
"math"
|
||||||
|
|
@ -57,8 +56,9 @@ func TestUpdateLeaks(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
tdb = triedb.NewDatabase(db, nil)
|
tdb = triedb.NewDatabase(db, nil)
|
||||||
|
sdb = NewDatabase(tdb, nil)
|
||||||
)
|
)
|
||||||
state, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(db, tdb), nil)
|
state, _ := New(types.EmptyRootHash, sdb)
|
||||||
|
|
||||||
// Update it with some accounts
|
// Update it with some accounts
|
||||||
for i := byte(0); i < 255; i++ {
|
for i := byte(0); i < 255; i++ {
|
||||||
|
|
@ -96,8 +96,8 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||||
finalDb := rawdb.NewMemoryDatabase()
|
finalDb := rawdb.NewMemoryDatabase()
|
||||||
transNdb := triedb.NewDatabase(transDb, nil)
|
transNdb := triedb.NewDatabase(transDb, nil)
|
||||||
finalNdb := triedb.NewDatabase(finalDb, nil)
|
finalNdb := triedb.NewDatabase(finalDb, nil)
|
||||||
transState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(transDb, transNdb), nil)
|
transState, _ := New(types.EmptyRootHash, NewDatabase(transNdb, nil))
|
||||||
finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil)
|
finalState, _ := New(types.EmptyRootHash, NewDatabase(finalNdb, nil))
|
||||||
|
|
||||||
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
|
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
|
||||||
state.SetBalance(addr, uint256.NewInt(uint64(11*i)+uint64(tweak)), tracing.BalanceChangeUnspecified)
|
state.SetBalance(addr, uint256.NewInt(uint64(11*i)+uint64(tweak)), tracing.BalanceChangeUnspecified)
|
||||||
|
|
@ -178,7 +178,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||||
// https://github.com/ethereum/go-ethereum/pull/15549.
|
// https://github.com/ethereum/go-ethereum/pull/15549.
|
||||||
func TestCopy(t *testing.T) {
|
func TestCopy(t *testing.T) {
|
||||||
// Create a random state test to copy and modify "independently"
|
// Create a random state test to copy and modify "independently"
|
||||||
orig, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
orig, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
|
|
||||||
for i := byte(0); i < 255; i++ {
|
for i := byte(0); i < 255; i++ {
|
||||||
obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
|
obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||||
|
|
@ -246,8 +246,8 @@ func TestCopy(t *testing.T) {
|
||||||
// TestCopyWithDirtyJournal tests if Copy can correct create a equal copied
|
// TestCopyWithDirtyJournal tests if Copy can correct create a equal copied
|
||||||
// stateDB with dirty journal present.
|
// stateDB with dirty journal present.
|
||||||
func TestCopyWithDirtyJournal(t *testing.T) {
|
func TestCopyWithDirtyJournal(t *testing.T) {
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := NewDatabaseForTesting()
|
||||||
orig, _ := New(types.EmptyRootHash, db, nil)
|
orig, _ := New(types.EmptyRootHash, db)
|
||||||
|
|
||||||
// Fill up the initial states
|
// Fill up the initial states
|
||||||
for i := byte(0); i < 255; i++ {
|
for i := byte(0); i < 255; i++ {
|
||||||
|
|
@ -257,7 +257,7 @@ func TestCopyWithDirtyJournal(t *testing.T) {
|
||||||
orig.updateStateObject(obj)
|
orig.updateStateObject(obj)
|
||||||
}
|
}
|
||||||
root, _ := orig.Commit(0, true)
|
root, _ := orig.Commit(0, true)
|
||||||
orig, _ = New(root, db, nil)
|
orig, _ = New(root, db)
|
||||||
|
|
||||||
// modify all in memory without finalizing
|
// modify all in memory without finalizing
|
||||||
for i := byte(0); i < 255; i++ {
|
for i := byte(0); i < 255; i++ {
|
||||||
|
|
@ -290,8 +290,8 @@ func TestCopyWithDirtyJournal(t *testing.T) {
|
||||||
// It then proceeds to make changes to S1. Those changes are _not_ supposed
|
// It then proceeds to make changes to S1. Those changes are _not_ supposed
|
||||||
// to affect S2. This test checks that the copy properly deep-copies the objectstate
|
// to affect S2. This test checks that the copy properly deep-copies the objectstate
|
||||||
func TestCopyObjectState(t *testing.T) {
|
func TestCopyObjectState(t *testing.T) {
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := NewDatabaseForTesting()
|
||||||
orig, _ := New(types.EmptyRootHash, db, nil)
|
orig, _ := New(types.EmptyRootHash, db)
|
||||||
|
|
||||||
// Fill up the initial states
|
// Fill up the initial states
|
||||||
for i := byte(0); i < 5; i++ {
|
for i := byte(0); i < 5; i++ {
|
||||||
|
|
@ -377,7 +377,7 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
||||||
args: make([]int64, 1),
|
args: make([]int64, 1),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "SetState",
|
name: "SetStorage",
|
||||||
fn: func(a testAction, s *StateDB) {
|
fn: func(a testAction, s *StateDB) {
|
||||||
var key, val common.Hash
|
var key, val common.Hash
|
||||||
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
||||||
|
|
@ -389,6 +389,12 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
||||||
{
|
{
|
||||||
name: "SetCode",
|
name: "SetCode",
|
||||||
fn: func(a testAction, s *StateDB) {
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
// SetCode can only be performed in case the addr does
|
||||||
|
// not already hold code
|
||||||
|
if c := s.GetCode(addr); len(c) > 0 {
|
||||||
|
// no-op
|
||||||
|
return
|
||||||
|
}
|
||||||
code := make([]byte, 16)
|
code := make([]byte, 16)
|
||||||
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
||||||
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
||||||
|
|
@ -551,7 +557,7 @@ func (test *snapshotTest) String() string {
|
||||||
func (test *snapshotTest) run() bool {
|
func (test *snapshotTest) run() bool {
|
||||||
// Run all actions and create snapshots.
|
// Run all actions and create snapshots.
|
||||||
var (
|
var (
|
||||||
state, _ = New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
state, _ = New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
snapshotRevs = make([]int, len(test.snapshots))
|
snapshotRevs = make([]int, len(test.snapshots))
|
||||||
sindex = 0
|
sindex = 0
|
||||||
checkstates = make([]*StateDB, len(test.snapshots))
|
checkstates = make([]*StateDB, len(test.snapshots))
|
||||||
|
|
@ -730,7 +736,7 @@ func TestTouchDelete(t *testing.T) {
|
||||||
s := newStateEnv()
|
s := newStateEnv()
|
||||||
s.state.getOrNewStateObject(common.Address{})
|
s.state.getOrNewStateObject(common.Address{})
|
||||||
root, _ := s.state.Commit(0, false)
|
root, _ := s.state.Commit(0, false)
|
||||||
s.state, _ = New(root, s.state.db, s.state.snaps)
|
s.state, _ = New(root, s.state.db)
|
||||||
|
|
||||||
snapshot := s.state.Snapshot()
|
snapshot := s.state.Snapshot()
|
||||||
s.state.AddBalance(common.Address{}, new(uint256.Int), tracing.BalanceChangeUnspecified)
|
s.state.AddBalance(common.Address{}, new(uint256.Int), tracing.BalanceChangeUnspecified)
|
||||||
|
|
@ -749,7 +755,7 @@ func TestTouchDelete(t *testing.T) {
|
||||||
func TestMVHashMapReadWriteDelete(t *testing.T) {
|
func TestMVHashMapReadWriteDelete(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := NewDatabaseForTesting()
|
||||||
mvhm := blockstm.MakeMVHashMap()
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
|
@ -816,7 +822,7 @@ func TestMVHashMapReadWriteDelete(t *testing.T) {
|
||||||
func TestMVHashMapCreateContract(t *testing.T) {
|
func TestMVHashMapCreateContract(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.HashDefaults), nil)
|
||||||
mvhm := blockstm.MakeMVHashMap()
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
|
@ -843,7 +849,7 @@ func TestMVHashMapCreateContract(t *testing.T) {
|
||||||
func TestMVHashMapRevert(t *testing.T) {
|
func TestMVHashMapRevert(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.HashDefaults), nil)
|
||||||
mvhm := blockstm.MakeMVHashMap()
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
|
@ -899,7 +905,7 @@ func TestMVHashMapRevert(t *testing.T) {
|
||||||
func TestMVHashMapMarkEstimate(t *testing.T) {
|
func TestMVHashMapMarkEstimate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.HashDefaults), nil)
|
||||||
mvhm := blockstm.MakeMVHashMap()
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
|
@ -965,7 +971,7 @@ func TestMVHashMapMarkEstimate(t *testing.T) {
|
||||||
func TestMVHashMapOverwrite(t *testing.T) {
|
func TestMVHashMapOverwrite(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.HashDefaults), nil)
|
||||||
mvhm := blockstm.MakeMVHashMap()
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
|
@ -1047,7 +1053,7 @@ func TestMVHashMapOverwrite(t *testing.T) {
|
||||||
func TestMVHashMapWriteNoConflict(t *testing.T) {
|
func TestMVHashMapWriteNoConflict(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.HashDefaults), nil)
|
||||||
mvhm := blockstm.MakeMVHashMap()
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
|
@ -1132,7 +1138,7 @@ func TestMVHashMapWriteNoConflict(t *testing.T) {
|
||||||
func TestApplyMVWriteSet(t *testing.T) {
|
func TestApplyMVWriteSet(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.HashDefaults), nil)
|
||||||
mvhm := blockstm.MakeMVHashMap()
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
|
@ -1227,7 +1233,7 @@ func TestApplyMVWriteSet(t *testing.T) {
|
||||||
func TestMVHashMapRevertConcurrent(t *testing.T) {
|
func TestMVHashMapRevertConcurrent(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), triedb.HashDefaults), nil)
|
||||||
mvhm := blockstm.MakeMVHashMap()
|
mvhm := blockstm.MakeMVHashMap()
|
||||||
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
s, _ := NewWithMVHashmap(common.Hash{}, db, nil, mvhm)
|
||||||
|
|
||||||
|
|
@ -1283,7 +1289,7 @@ func TestMVHashMapRevertConcurrent(t *testing.T) {
|
||||||
// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
|
// TestCopyOfCopy tests that modified objects are carried over to the copy, and the copy of the copy.
|
||||||
// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
|
// See https://github.com/ethereum/go-ethereum/pull/15225#issuecomment-380191512
|
||||||
func TestCopyOfCopy(t *testing.T) {
|
func TestCopyOfCopy(t *testing.T) {
|
||||||
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
state, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
addr := common.HexToAddress("aaaa")
|
addr := common.HexToAddress("aaaa")
|
||||||
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified)
|
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
|
|
@ -1301,8 +1307,8 @@ func TestCopyOfCopy(t *testing.T) {
|
||||||
//
|
//
|
||||||
// See https://github.com/ethereum/go-ethereum/issues/20106.
|
// See https://github.com/ethereum/go-ethereum/issues/20106.
|
||||||
func TestCopyCommitCopy(t *testing.T) {
|
func TestCopyCommitCopy(t *testing.T) {
|
||||||
tdb := NewDatabase(rawdb.NewMemoryDatabase())
|
tdb := NewDatabaseForTesting()
|
||||||
state, _ := New(types.EmptyRootHash, tdb, nil)
|
state, _ := New(types.EmptyRootHash, tdb)
|
||||||
|
|
||||||
// Create an account and check if the retrieved balance is correct
|
// Create an account and check if the retrieved balance is correct
|
||||||
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
|
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
|
||||||
|
|
@ -1363,7 +1369,7 @@ func TestCopyCommitCopy(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Commit state, ensure states can be loaded from disk
|
// Commit state, ensure states can be loaded from disk
|
||||||
root, _ := state.Commit(0, false)
|
root, _ := state.Commit(0, false)
|
||||||
state, _ = New(root, tdb, nil)
|
state, _ = New(root, tdb)
|
||||||
if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
|
if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
|
||||||
t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42)
|
t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42)
|
||||||
}
|
}
|
||||||
|
|
@ -1383,7 +1389,7 @@ func TestCopyCommitCopy(t *testing.T) {
|
||||||
//
|
//
|
||||||
// See https://github.com/ethereum/go-ethereum/issues/20106.
|
// See https://github.com/ethereum/go-ethereum/issues/20106.
|
||||||
func TestCopyCopyCommitCopy(t *testing.T) {
|
func TestCopyCopyCommitCopy(t *testing.T) {
|
||||||
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
state, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
|
|
||||||
// Create an account and check if the retrieved balance is correct
|
// Create an account and check if the retrieved balance is correct
|
||||||
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
|
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
|
||||||
|
|
@ -1463,8 +1469,8 @@ func TestCopyCopyCommitCopy(t *testing.T) {
|
||||||
|
|
||||||
// TestCommitCopy tests the copy from a committed state is not fully functional.
|
// TestCommitCopy tests the copy from a committed state is not fully functional.
|
||||||
func TestCommitCopy(t *testing.T) {
|
func TestCommitCopy(t *testing.T) {
|
||||||
db := NewDatabase(rawdb.NewMemoryDatabase())
|
db := NewDatabaseForTesting()
|
||||||
state, _ := New(types.EmptyRootHash, db, nil)
|
state, _ := New(types.EmptyRootHash, db)
|
||||||
|
|
||||||
// Create an account and check if the retrieved balance is correct
|
// Create an account and check if the retrieved balance is correct
|
||||||
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
|
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
|
||||||
|
|
@ -1489,7 +1495,7 @@ func TestCommitCopy(t *testing.T) {
|
||||||
}
|
}
|
||||||
root, _ := state.Commit(0, true)
|
root, _ := state.Commit(0, true)
|
||||||
|
|
||||||
state, _ = New(root, db, nil)
|
state, _ = New(root, db)
|
||||||
state.SetState(addr, skey2, sval2)
|
state.SetState(addr, skey2, sval2)
|
||||||
state.Commit(1, true)
|
state.Commit(1, true)
|
||||||
|
|
||||||
|
|
@ -1502,10 +1508,10 @@ func TestCommitCopy(t *testing.T) {
|
||||||
t.Fatalf("unexpected code: have %x", code)
|
t.Fatalf("unexpected code: have %x", code)
|
||||||
}
|
}
|
||||||
// Miss slots because of non-functional trie after commit
|
// Miss slots because of non-functional trie after commit
|
||||||
if val := copied.GetState(addr, skey1); val != (common.Hash{}) {
|
if val := copied.GetState(addr, skey1); val != sval1 {
|
||||||
t.Fatalf("unexpected storage slot: have %x", sval1)
|
t.Fatalf("unexpected storage slot: have %x", val)
|
||||||
}
|
}
|
||||||
if val := copied.GetCommittedState(addr, skey1); val != (common.Hash{}) {
|
if val := copied.GetCommittedState(addr, skey1); val != sval1 {
|
||||||
t.Fatalf("unexpected storage slot: have %x", val)
|
t.Fatalf("unexpected storage slot: have %x", val)
|
||||||
}
|
}
|
||||||
// Slots cached in the stateDB, available after commit
|
// Slots cached in the stateDB, available after commit
|
||||||
|
|
@ -1515,9 +1521,6 @@ func TestCommitCopy(t *testing.T) {
|
||||||
if val := copied.GetCommittedState(addr, skey2); val != sval2 {
|
if val := copied.GetCommittedState(addr, skey2); val != sval2 {
|
||||||
t.Fatalf("unexpected storage slot: have %x", val)
|
t.Fatalf("unexpected storage slot: have %x", val)
|
||||||
}
|
}
|
||||||
if !errors.Is(copied.Error(), trie.ErrCommitted) {
|
|
||||||
t.Fatalf("unexpected state error, %v", copied.Error())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestDeleteCreateRevert tests a weird state transition corner case that we hit
|
// TestDeleteCreateRevert tests a weird state transition corner case that we hit
|
||||||
|
|
@ -1530,13 +1533,13 @@ func TestCommitCopy(t *testing.T) {
|
||||||
// first, but the journal wiped the entire state object on create-revert.
|
// first, but the journal wiped the entire state object on create-revert.
|
||||||
func TestDeleteCreateRevert(t *testing.T) {
|
func TestDeleteCreateRevert(t *testing.T) {
|
||||||
// Create an initial state with a single contract
|
// Create an initial state with a single contract
|
||||||
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
state, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
|
|
||||||
addr := common.BytesToAddress([]byte("so"))
|
addr := common.BytesToAddress([]byte("so"))
|
||||||
state.SetBalance(addr, uint256.NewInt(1), tracing.BalanceChangeUnspecified)
|
state.SetBalance(addr, uint256.NewInt(1), tracing.BalanceChangeUnspecified)
|
||||||
|
|
||||||
root, _ := state.Commit(0, false)
|
root, _ := state.Commit(0, false)
|
||||||
state, _ = New(root, state.db, state.snaps)
|
state, _ = New(root, state.db)
|
||||||
|
|
||||||
// Simulate self-destructing in one transaction, then create-reverting in another
|
// Simulate self-destructing in one transaction, then create-reverting in another
|
||||||
state.SelfDestruct(addr)
|
state.SelfDestruct(addr)
|
||||||
|
|
@ -1548,7 +1551,7 @@ func TestDeleteCreateRevert(t *testing.T) {
|
||||||
|
|
||||||
// Commit the entire state and make sure we don't crash and have the correct state
|
// Commit the entire state and make sure we don't crash and have the correct state
|
||||||
root, _ = state.Commit(0, true)
|
root, _ = state.Commit(0, true)
|
||||||
state, _ = New(root, state.db, state.snaps)
|
state, _ = New(root, state.db)
|
||||||
|
|
||||||
if state.getStateObject(addr) != nil {
|
if state.getStateObject(addr) != nil {
|
||||||
t.Fatalf("self-destructed contract came alive")
|
t.Fatalf("self-destructed contract came alive")
|
||||||
|
|
@ -1579,10 +1582,10 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
|
||||||
CleanCacheSize: 0,
|
CleanCacheSize: 0,
|
||||||
}}) // disable caching
|
}}) // disable caching
|
||||||
}
|
}
|
||||||
db := NewDatabaseWithNodeDB(memDb, tdb)
|
db := NewDatabase(tdb, nil)
|
||||||
|
|
||||||
var root common.Hash
|
var root common.Hash
|
||||||
state, _ := New(types.EmptyRootHash, db, nil)
|
state, _ := New(types.EmptyRootHash, db)
|
||||||
addr := common.BytesToAddress([]byte("so"))
|
addr := common.BytesToAddress([]byte("so"))
|
||||||
{
|
{
|
||||||
state.SetBalance(addr, uint256.NewInt(1), tracing.BalanceChangeUnspecified)
|
state.SetBalance(addr, uint256.NewInt(1), tracing.BalanceChangeUnspecified)
|
||||||
|
|
@ -1597,7 +1600,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
|
||||||
tdb.Commit(root, false)
|
tdb.Commit(root, false)
|
||||||
}
|
}
|
||||||
// Create a new state on the old root
|
// Create a new state on the old root
|
||||||
state, _ = New(root, db, nil)
|
state, _ = New(root, db)
|
||||||
// Now we clear out the memdb
|
// Now we clear out the memdb
|
||||||
it := memDb.NewIterator(nil, nil)
|
it := memDb.NewIterator(nil, nil)
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
|
|
@ -1631,9 +1634,8 @@ func TestStateDBAccessList(t *testing.T) {
|
||||||
return common.HexToHash(a)
|
return common.HexToHash(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
memDb := rawdb.NewMemoryDatabase()
|
db := NewDatabaseForTesting()
|
||||||
db := NewDatabase(memDb)
|
state, _ := New(types.EmptyRootHash, db)
|
||||||
state, _ := New(types.EmptyRootHash, db, nil)
|
|
||||||
state.accessList = newAccessList()
|
state.accessList = newAccessList()
|
||||||
|
|
||||||
verifyAddrs := func(astrings ...string) {
|
verifyAddrs := func(astrings ...string) {
|
||||||
|
|
@ -1832,9 +1834,9 @@ func TestFlushOrderDataLoss(t *testing.T) {
|
||||||
// Create a state trie with many accounts and slots
|
// Create a state trie with many accounts and slots
|
||||||
var (
|
var (
|
||||||
memdb = rawdb.NewMemoryDatabase()
|
memdb = rawdb.NewMemoryDatabase()
|
||||||
triedb = triedb.NewDatabase(memdb, nil)
|
tdb = triedb.NewDatabase(memdb, triedb.HashDefaults)
|
||||||
statedb = NewDatabaseWithNodeDB(memdb, triedb)
|
statedb = NewDatabase(tdb, nil)
|
||||||
state, _ = New(types.EmptyRootHash, statedb, nil)
|
state, _ = New(types.EmptyRootHash, statedb)
|
||||||
)
|
)
|
||||||
|
|
||||||
for a := byte(0); a < 10; a++ {
|
for a := byte(0); a < 10; a++ {
|
||||||
|
|
@ -1848,15 +1850,15 @@ func TestFlushOrderDataLoss(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to commit state trie: %v", err)
|
t.Fatalf("failed to commit state trie: %v", err)
|
||||||
}
|
}
|
||||||
triedb.Reference(root, common.Hash{})
|
tdb.Reference(root, common.Hash{})
|
||||||
if err := triedb.Cap(1024); err != nil {
|
if err := tdb.Cap(1024); err != nil {
|
||||||
t.Fatalf("failed to cap trie dirty cache: %v", err)
|
t.Fatalf("failed to cap trie dirty cache: %v", err)
|
||||||
}
|
}
|
||||||
if err := triedb.Commit(root, false); err != nil {
|
if err := tdb.Commit(root, false); err != nil {
|
||||||
t.Fatalf("failed to commit state trie: %v", err)
|
t.Fatalf("failed to commit state trie: %v", err)
|
||||||
}
|
}
|
||||||
// Reopen the state trie from flushed disk and verify it
|
// Reopen the state trie from flushed disk and verify it
|
||||||
state, err = New(root, NewDatabase(memdb), nil)
|
state, err = New(root, NewDatabase(triedb.NewDatabase(memdb, triedb.HashDefaults), nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to reopen state trie: %v", err)
|
t.Fatalf("failed to reopen state trie: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1873,9 +1875,8 @@ func TestFlushOrderDataLoss(t *testing.T) {
|
||||||
func TestStateDBTransientStorage(t *testing.T) {
|
func TestStateDBTransientStorage(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
memDb := rawdb.NewMemoryDatabase()
|
db := NewDatabaseForTesting()
|
||||||
db := NewDatabase(memDb)
|
state, _ := New(types.EmptyRootHash, db)
|
||||||
state, _ := New(types.EmptyRootHash, db, nil)
|
|
||||||
|
|
||||||
key := common.Hash{0x01}
|
key := common.Hash{0x01}
|
||||||
value := common.Hash{0x02}
|
value := common.Hash{0x02}
|
||||||
|
|
@ -1913,9 +1914,9 @@ func TestDeleteStorage(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
disk = rawdb.NewMemoryDatabase()
|
disk = rawdb.NewMemoryDatabase()
|
||||||
tdb = triedb.NewDatabase(disk, nil)
|
tdb = triedb.NewDatabase(disk, nil)
|
||||||
db = NewDatabaseWithNodeDB(disk, tdb)
|
|
||||||
snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash)
|
snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, types.EmptyRootHash)
|
||||||
state, _ = New(types.EmptyRootHash, db, snaps)
|
db = NewDatabase(tdb, snaps)
|
||||||
|
state, _ = New(types.EmptyRootHash, db)
|
||||||
addr = common.HexToAddress("0x1")
|
addr = common.HexToAddress("0x1")
|
||||||
)
|
)
|
||||||
// Initialize account and populate storage
|
// Initialize account and populate storage
|
||||||
|
|
@ -1927,9 +1928,10 @@ func TestDeleteStorage(t *testing.T) {
|
||||||
state.SetState(addr, slot, value)
|
state.SetState(addr, slot, value)
|
||||||
}
|
}
|
||||||
root, _ := state.Commit(0, true)
|
root, _ := state.Commit(0, true)
|
||||||
|
|
||||||
// Init phase done, create two states, one with snap and one without
|
// Init phase done, create two states, one with snap and one without
|
||||||
fastState, _ := New(root, db, snaps)
|
fastState, _ := New(root, NewDatabase(tdb, snaps))
|
||||||
slowState, _ := New(root, db, nil)
|
slowState, _ := New(root, NewDatabase(tdb, nil))
|
||||||
|
|
||||||
obj := fastState.getOrNewStateObject(addr)
|
obj := fastState.getOrNewStateObject(addr)
|
||||||
storageRoot := obj.data.Root
|
storageRoot := obj.data.Root
|
||||||
|
|
@ -1967,8 +1969,8 @@ func TestStorageDirtiness(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
disk = rawdb.NewMemoryDatabase()
|
disk = rawdb.NewMemoryDatabase()
|
||||||
tdb = triedb.NewDatabase(disk, nil)
|
tdb = triedb.NewDatabase(disk, nil)
|
||||||
db = NewDatabaseWithNodeDB(disk, tdb)
|
db = NewDatabase(tdb, nil)
|
||||||
state, _ = New(types.EmptyRootHash, db, nil)
|
state, _ = New(types.EmptyRootHash, db)
|
||||||
addr = common.HexToAddress("0x1")
|
addr = common.HexToAddress("0x1")
|
||||||
checkDirty = func(key common.Hash, value common.Hash, dirty bool) {
|
checkDirty = func(key common.Hash, value common.Hash, dirty bool) {
|
||||||
obj := state.getStateObject(addr)
|
obj := state.getStateObject(addr)
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,8 @@ func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, c
|
||||||
}
|
}
|
||||||
db := rawdb.NewMemoryDatabase()
|
db := rawdb.NewMemoryDatabase()
|
||||||
nodeDb := triedb.NewDatabase(db, config)
|
nodeDb := triedb.NewDatabase(db, config)
|
||||||
sdb := NewDatabaseWithNodeDB(db, nodeDb)
|
sdb := NewDatabase(nodeDb, nil)
|
||||||
state, _ := New(types.EmptyRootHash, sdb, nil)
|
state, _ := New(types.EmptyRootHash, sdb)
|
||||||
|
|
||||||
// Fill it with some arbitrary data
|
// Fill it with some arbitrary data
|
||||||
var accounts []*testAccount
|
var accounts []*testAccount
|
||||||
|
|
@ -96,7 +96,7 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, scheme string, root com
|
||||||
config.PathDB = pathdb.Defaults
|
config.PathDB = pathdb.Defaults
|
||||||
}
|
}
|
||||||
// Check root availability and state contents
|
// Check root availability and state contents
|
||||||
state, err := New(root, NewDatabaseWithConfig(db, &config), nil)
|
state, err := New(root, NewDatabase(triedb.NewDatabase(db, &config), nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create state trie at %x: %v", root, err)
|
t.Fatalf("failed to create state trie at %x: %v", root, err)
|
||||||
}
|
}
|
||||||
|
|
@ -125,7 +125,7 @@ func checkStateConsistency(db ethdb.Database, scheme string, root common.Hash) e
|
||||||
if scheme == rawdb.PathScheme {
|
if scheme == rawdb.PathScheme {
|
||||||
config.PathDB = pathdb.Defaults
|
config.PathDB = pathdb.Defaults
|
||||||
}
|
}
|
||||||
state, err := New(root, NewDatabaseWithConfig(db, config), nil)
|
state, err := New(root, NewDatabase(triedb.NewDatabase(db, config), nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ var (
|
||||||
//
|
//
|
||||||
// Note, the prefetcher's API is not thread safe.
|
// Note, the prefetcher's API is not thread safe.
|
||||||
type triePrefetcher struct {
|
type triePrefetcher struct {
|
||||||
|
verkle bool // Flag whether the prefetcher is in verkle mode
|
||||||
db Database // Database to fetch trie nodes through
|
db Database // Database to fetch trie nodes through
|
||||||
root common.Hash // Root hash of the account trie for metrics
|
root common.Hash // Root hash of the account trie for metrics
|
||||||
fetchers map[string]*subfetcher // Subfetchers for each trie
|
fetchers map[string]*subfetcher // Subfetchers for each trie
|
||||||
|
|
@ -68,6 +69,7 @@ type triePrefetcher struct {
|
||||||
func newTriePrefetcher(db Database, root common.Hash, namespace string, noreads bool) *triePrefetcher {
|
func newTriePrefetcher(db Database, root common.Hash, namespace string, noreads bool) *triePrefetcher {
|
||||||
prefix := triePrefetchMetricsPrefix + namespace
|
prefix := triePrefetchMetricsPrefix + namespace
|
||||||
return &triePrefetcher{
|
return &triePrefetcher{
|
||||||
|
verkle: db.TrieDB().IsVerkle(),
|
||||||
db: db,
|
db: db,
|
||||||
root: root,
|
root: root,
|
||||||
fetchers: make(map[string]*subfetcher), // Active prefetchers use the fetchers map
|
fetchers: make(map[string]*subfetcher), // Active prefetchers use the fetchers map
|
||||||
|
|
@ -214,12 +216,18 @@ func (p *triePrefetcher) used(owner common.Hash, root common.Hash, used [][]byte
|
||||||
|
|
||||||
if fetcher := p.fetchers[p.trieID(owner, root)]; fetcher != nil {
|
if fetcher := p.fetchers[p.trieID(owner, root)]; fetcher != nil {
|
||||||
fetcher.wait() // ensure the fetcher's idle before poking in its internals
|
fetcher.wait() // ensure the fetcher's idle before poking in its internals
|
||||||
fetcher.used = used
|
fetcher.used = append(fetcher.used, used...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// trieID returns an unique trie identifier consists the trie owner and root hash.
|
// trieID returns an unique trie identifier consists the trie owner and root hash.
|
||||||
func (p *triePrefetcher) trieID(owner common.Hash, root common.Hash) string {
|
func (p *triePrefetcher) trieID(owner common.Hash, root common.Hash) string {
|
||||||
|
// The trie in verkle is only identified by state root
|
||||||
|
if p.verkle {
|
||||||
|
return p.root.Hex()
|
||||||
|
}
|
||||||
|
// The trie in merkle is either identified by state root (account trie),
|
||||||
|
// or identified by the owner and trie root (storage trie)
|
||||||
trieID := make([]byte, common.HashLength*2)
|
trieID := make([]byte, common.HashLength*2)
|
||||||
copy(trieID, owner.Bytes())
|
copy(trieID, owner.Bytes())
|
||||||
copy(trieID[common.HashLength:], root.Bytes())
|
copy(trieID[common.HashLength:], root.Bytes())
|
||||||
|
|
@ -339,31 +347,47 @@ func (sf *subfetcher) terminate(async bool) {
|
||||||
<-sf.term
|
<-sf.term
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// openTrie resolves the target trie from database for prefetching.
|
||||||
|
func (sf *subfetcher) openTrie() error {
|
||||||
|
// Open the verkle tree if the sub-fetcher is in verkle mode. Note, there is
|
||||||
|
// only a single fetcher for verkle.
|
||||||
|
if sf.db.TrieDB().IsVerkle() {
|
||||||
|
tr, err := sf.db.OpenTrie(sf.state)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Trie prefetcher failed opening verkle trie", "root", sf.root, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf.trie = tr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Open the merkle tree if the sub-fetcher is in merkle mode
|
||||||
|
if sf.owner == (common.Hash{}) {
|
||||||
|
tr, err := sf.db.OpenTrie(sf.state)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Trie prefetcher failed opening account trie", "root", sf.root, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf.trie = tr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tr, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("Trie prefetcher failed opening storage trie", "root", sf.root, "err", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sf.trie = tr
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// loop loads newly-scheduled trie tasks as they are received and loads them, stopping
|
// loop loads newly-scheduled trie tasks as they are received and loads them, stopping
|
||||||
// when requested.
|
// when requested.
|
||||||
func (sf *subfetcher) loop() {
|
func (sf *subfetcher) loop() {
|
||||||
// No matter how the loop stops, signal anyone waiting that it's terminated
|
// No matter how the loop stops, signal anyone waiting that it's terminated
|
||||||
defer close(sf.term)
|
defer close(sf.term)
|
||||||
|
|
||||||
// Start by opening the trie and stop processing if it fails
|
if err := sf.openTrie(); err != nil {
|
||||||
if sf.owner == (common.Hash{}) {
|
|
||||||
trie, err := sf.db.OpenTrie(sf.root)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sf.trie = trie
|
|
||||||
} else {
|
|
||||||
trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sf.trie = trie
|
|
||||||
}
|
|
||||||
// Trie opened successfully, keep prefetching items
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-sf.wake:
|
case <-sf.wake:
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,14 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/tracing"
|
"github.com/ethereum/go-ethereum/core/tracing"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/internal/testrand"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
func filledStateDB() *StateDB {
|
func filledStateDB() *StateDB {
|
||||||
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
|
state, _ := New(types.EmptyRootHash, NewDatabaseForTesting())
|
||||||
|
|
||||||
// Create an account and check if the retrieved balance is correct
|
// Create an account and check if the retrieved balance is correct
|
||||||
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
|
addr := common.HexToAddress("0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe")
|
||||||
|
|
@ -63,3 +66,47 @@ func TestUseAfterTerminate(t *testing.T) {
|
||||||
t.Errorf("Prefetcher returned nil trie after terminate")
|
t.Errorf("Prefetcher returned nil trie after terminate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVerklePrefetcher(t *testing.T) {
|
||||||
|
disk := rawdb.NewMemoryDatabase()
|
||||||
|
db := triedb.NewDatabase(disk, triedb.VerkleDefaults)
|
||||||
|
sdb := NewDatabase(db, nil)
|
||||||
|
|
||||||
|
state, err := New(types.EmptyRootHash, sdb)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to initialize state: %v", err)
|
||||||
|
}
|
||||||
|
// Create an account and check if the retrieved balance is correct
|
||||||
|
addr := testrand.Address()
|
||||||
|
skey := testrand.Hash()
|
||||||
|
sval := testrand.Hash()
|
||||||
|
|
||||||
|
state.SetBalance(addr, uint256.NewInt(42), tracing.BalanceChangeUnspecified) // Change the account trie
|
||||||
|
state.SetCode(addr, []byte("hello")) // Change an external metadata
|
||||||
|
state.SetState(addr, skey, sval) // Change the storage trie
|
||||||
|
root, _ := state.Commit(0, true)
|
||||||
|
|
||||||
|
state, _ = New(root, sdb)
|
||||||
|
sRoot := state.GetStorageRoot(addr)
|
||||||
|
fetcher := newTriePrefetcher(sdb, root, "", false)
|
||||||
|
|
||||||
|
// Read account
|
||||||
|
fetcher.prefetch(common.Hash{}, root, common.Address{}, [][]byte{
|
||||||
|
addr.Bytes(),
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
// Read storage slot
|
||||||
|
fetcher.prefetch(crypto.Keccak256Hash(addr.Bytes()), sRoot, addr, [][]byte{
|
||||||
|
skey.Bytes(),
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
fetcher.terminate(false)
|
||||||
|
accountTrie := fetcher.trie(common.Hash{}, root)
|
||||||
|
storageTrie := fetcher.trie(crypto.Keccak256Hash(addr.Bytes()), sRoot)
|
||||||
|
|
||||||
|
rootA := accountTrie.Hash()
|
||||||
|
rootB := storageTrie.Hash()
|
||||||
|
if rootA != rootB {
|
||||||
|
t.Fatal("Two different tries are retrieved")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
|
@ -90,7 +89,7 @@ func precacheTransaction(msg *Message, _ *params.ChainConfig, gaspool *GasPool,
|
||||||
// Update the evm with the new transaction context.
|
// Update the evm with the new transaction context.
|
||||||
evm.Reset(NewEVMTxContext(msg), statedb)
|
evm.Reset(NewEVMTxContext(msg), statedb)
|
||||||
// Add addresses to access list if applicable
|
// Add addresses to access list if applicable
|
||||||
_, err := ApplyMessage(evm, msg, gaspool, context.Background())
|
_, err := ApplyMessage(evm, msg, gaspool)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, hc *HeaderCha
|
||||||
// Process returns the receipts and logs accumulated during the process and
|
// Process returns the receipts and logs accumulated during the process and
|
||||||
// returns the amount of gas that was used in the process. If any of the
|
// returns the amount of gas that was used in the process. If any of the
|
||||||
// transactions failed to execute due to insufficient gas it will return an error.
|
// transactions failed to execute due to insufficient gas it will return an error.
|
||||||
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error) {
|
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (*ProcessResult, error) {
|
||||||
var (
|
var (
|
||||||
receipts types.Receipts
|
receipts types.Receipts
|
||||||
usedGas = new(uint64)
|
usedGas = new(uint64)
|
||||||
|
|
@ -76,57 +76,57 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
var (
|
var (
|
||||||
context vm.BlockContext
|
context vm.BlockContext
|
||||||
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
signer = types.MakeSigner(p.config, header.Number, header.Time)
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
context = NewEVMBlockContext(header, p.hc, nil)
|
context = NewEVMBlockContext(header, p.hc, nil)
|
||||||
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg)
|
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg)
|
||||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
ProcessBeaconBlockRoot(*beaconRoot, vmenv, statedb)
|
||||||
}
|
}
|
||||||
|
if p.config.IsPrague(block.Number()) {
|
||||||
|
ProcessParentBlockHash(block.ParentHash(), vmenv, statedb)
|
||||||
|
}
|
||||||
// Iterate over and process the individual transactions
|
// Iterate over and process the individual transactions
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
if interruptCtx != nil {
|
|
||||||
select {
|
|
||||||
case <-interruptCtx.Done():
|
|
||||||
return nil, nil, 0, interruptCtx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
|
|
||||||
receipt, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv, interruptCtx)
|
receipt, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, 0, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
receipts = append(receipts, receipt)
|
receipts = append(receipts, receipt)
|
||||||
allLogs = append(allLogs, receipt.Logs...)
|
allLogs = append(allLogs, receipt.Logs...)
|
||||||
}
|
}
|
||||||
// Fail if Shanghai not enabled and len(withdrawals) is non-zero.
|
// Read requests if Prague is enabled.
|
||||||
withdrawals := block.Withdrawals()
|
var requests types.Requests
|
||||||
if !p.config.IsShanghai(block.Number()) && withdrawals != nil {
|
if p.config.IsPrague(block.Number()) {
|
||||||
return nil, nil, 0, fmt.Errorf("withdrawals before shanghai")
|
requests, err = ParseDepositLogs(allLogs, p.config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
// Bor does not support withdrawals
|
|
||||||
// nolint
|
|
||||||
if withdrawals != nil {
|
|
||||||
withdrawals = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||||
p.hc.engine.Finalize(p.bc, header, statedb, block.Body())
|
p.hc.engine.Finalize(p.bc, header, statedb, block.Body())
|
||||||
|
|
||||||
return receipts, allLogs, *usedGas, nil
|
return &ProcessResult{
|
||||||
|
Receipts: receipts,
|
||||||
|
Requests: requests,
|
||||||
|
Logs: allLogs,
|
||||||
|
GasUsed: *usedGas,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
// ApplyTransactionWithEVM attempts to apply a transaction to the given state database
|
||||||
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
// and uses the input parameters for its environment similar to ApplyTransaction. However,
|
||||||
// this method takes an already created EVM instance as input.
|
// this method takes an already created EVM instance as input.
|
||||||
func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM, interruptCtx context.Context) (receipt *types.Receipt, err error) {
|
func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnTxStart != nil {
|
||||||
evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
evm.Config.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
if evm.Config.Tracer.OnTxEnd != nil {
|
if evm.Config.Tracer.OnTxEnd != nil {
|
||||||
|
|
@ -151,7 +151,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
|
||||||
// resume recording read and write
|
// resume recording read and write
|
||||||
statedb.SetMVHashmap(backupMVHashMap)
|
statedb.SetMVHashmap(backupMVHashMap)
|
||||||
|
|
||||||
result, err = ApplyMessageNoFeeBurnOrTip(evm, *msg, gp, interruptCtx)
|
result, err = ApplyMessageNoFeeBurnOrTip(evm, *msg, gp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -198,9 +198,14 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
|
||||||
|
|
||||||
*usedGas += result.UsedGas
|
*usedGas += result.UsedGas
|
||||||
|
|
||||||
|
return MakeReceipt(evm, result, statedb, blockNumber, blockHash, tx, *usedGas, root), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeReceipt generates the receipt object for a transaction given its execution result.
|
||||||
|
func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas uint64, root []byte) *types.Receipt {
|
||||||
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
// Create a new receipt for the transaction, storing the intermediate root and gas used
|
||||||
// by the tx.
|
// by the tx.
|
||||||
receipt = &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: *usedGas}
|
receipt := &types.Receipt{Type: tx.Type(), PostState: root, CumulativeGasUsed: usedGas}
|
||||||
if result.Failed() {
|
if result.Failed() {
|
||||||
receipt.Status = types.ReceiptStatusFailed
|
receipt.Status = types.ReceiptStatusFailed
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -216,18 +221,23 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the transaction created a contract, store the creation address in the receipt.
|
// If the transaction created a contract, store the creation address in the receipt.
|
||||||
if msg.To == nil {
|
if tx.To() == nil {
|
||||||
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
receipt.ContractAddress = crypto.CreateAddress(evm.TxContext.Origin, tx.Nonce())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 statedb.GetTrie().IsVerkle() {
|
||||||
|
statedb.AccessEvents().Merge(evm.AccessEvents)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the receipt logs and create the bloom filter.
|
// Set the receipt logs and create the bloom filter.
|
||||||
receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash)
|
receipt.Logs = statedb.GetLogs(tx.Hash(), blockNumber.Uint64(), blockHash)
|
||||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||||
receipt.BlockHash = blockHash
|
receipt.BlockHash = blockHash
|
||||||
receipt.BlockNumber = blockNumber
|
receipt.BlockNumber = blockNumber
|
||||||
receipt.TransactionIndex = uint(statedb.TxIndex())
|
receipt.TransactionIndex = uint(statedb.TxIndex())
|
||||||
|
return receipt
|
||||||
return receipt, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyTransaction attempts to apply a transaction to the given state database
|
// ApplyTransaction attempts to apply a transaction to the given state database
|
||||||
|
|
@ -243,17 +253,19 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
|
||||||
blockContext := NewEVMBlockContext(header, bc, author)
|
blockContext := NewEVMBlockContext(header, bc, author)
|
||||||
txContext := NewEVMTxContext(msg)
|
txContext := NewEVMTxContext(msg)
|
||||||
vmenv := vm.NewEVM(blockContext, txContext, statedb, config, cfg)
|
vmenv := vm.NewEVM(blockContext, txContext, statedb, config, cfg)
|
||||||
return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv, interruptCtx)
|
return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
|
||||||
// contract. This method is exported to be used in tests.
|
// contract. This method is exported to be used in tests.
|
||||||
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
|
func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
|
||||||
if vmenv.Config.Tracer != nil && vmenv.Config.Tracer.OnSystemCallStart != nil {
|
if tracer := vmenv.Config.Tracer; tracer != nil {
|
||||||
vmenv.Config.Tracer.OnSystemCallStart()
|
if tracer.OnSystemCallStart != nil {
|
||||||
|
tracer.OnSystemCallStart()
|
||||||
|
}
|
||||||
|
if tracer.OnSystemCallEnd != nil {
|
||||||
|
defer tracer.OnSystemCallEnd()
|
||||||
}
|
}
|
||||||
if vmenv.Config.Tracer != nil && vmenv.Config.Tracer.OnSystemCallEnd != nil {
|
|
||||||
defer vmenv.Config.Tracer.OnSystemCallEnd()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with
|
// If EIP-4788 is enabled, we need to invoke the beaconroot storage contract with
|
||||||
|
|
@ -269,6 +281,49 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
|
||||||
}
|
}
|
||||||
vmenv.Reset(NewEVMTxContext(msg), statedb)
|
vmenv.Reset(NewEVMTxContext(msg), statedb)
|
||||||
statedb.AddAddressToAccessList(params.BeaconRootsAddress)
|
statedb.AddAddressToAccessList(params.BeaconRootsAddress)
|
||||||
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560, nil)
|
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||||
statedb.Finalise(true)
|
statedb.Finalise(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProcessParentBlockHash stores the parent block hash in the history storage contract
|
||||||
|
// as per EIP-2935.
|
||||||
|
func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb *state.StateDB) {
|
||||||
|
if tracer := vmenv.Config.Tracer; tracer != nil {
|
||||||
|
if tracer.OnSystemCallStart != nil {
|
||||||
|
tracer.OnSystemCallStart()
|
||||||
|
}
|
||||||
|
if tracer.OnSystemCallEnd != nil {
|
||||||
|
defer tracer.OnSystemCallEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := &Message{
|
||||||
|
From: params.SystemAddress,
|
||||||
|
GasLimit: 30_000_000,
|
||||||
|
GasPrice: common.Big0,
|
||||||
|
GasFeeCap: common.Big0,
|
||||||
|
GasTipCap: common.Big0,
|
||||||
|
To: ¶ms.HistoryStorageAddress,
|
||||||
|
Data: prevHash.Bytes(),
|
||||||
|
}
|
||||||
|
vmenv.Reset(NewEVMTxContext(msg), statedb)
|
||||||
|
statedb.AddAddressToAccessList(params.HistoryStorageAddress)
|
||||||
|
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||||
|
statedb.Finalise(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseDepositLogs extracts the EIP-6110 deposit values from logs emitted by
|
||||||
|
// BeaconDepositContract.
|
||||||
|
func ParseDepositLogs(logs []*types.Log, config *params.ChainConfig) (types.Requests, error) {
|
||||||
|
deposits := make(types.Requests, 0)
|
||||||
|
for _, log := range logs {
|
||||||
|
if log.Address == config.DepositContractAddress {
|
||||||
|
d, err := types.UnpackIntoDeposit(log.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to parse deposit data: %v", err)
|
||||||
|
}
|
||||||
|
deposits = append(deposits, types.NewRequest(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return deposits, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"encoding/binary"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -31,11 +32,14 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
|
"github.com/ethereum/go-verkle"
|
||||||
"github.com/holiman/uint256"
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -489,13 +493,40 @@ func TestProcessVerkle(t *testing.T) {
|
||||||
|
|
||||||
txCost1 := params.TxGas
|
txCost1 := params.TxGas
|
||||||
txCost2 := params.TxGas
|
txCost2 := params.TxGas
|
||||||
contractCreationCost := intrinsicContractCreationGas + uint64(2039 /* execution costs */)
|
contractCreationCost := intrinsicContractCreationGas +
|
||||||
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(57444 /* execution costs */)
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* creation with value */
|
||||||
|
739 /* execution costs */
|
||||||
|
codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas +
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (tx) */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at pc=0x20) */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */
|
||||||
|
params.WitnessChunkReadCost + /* SLOAD in constructor */
|
||||||
|
params.WitnessChunkWriteCost + /* SSTORE in constructor */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + params.WitnessBranchReadCost + params.WitnessBranchWriteCost + /* creation (CREATE at PC=0x121) */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #0 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #1 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #2 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #3 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #4 */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* code chunk #5 */
|
||||||
|
params.WitnessChunkReadCost + /* SLOAD in constructor */
|
||||||
|
params.WitnessChunkWriteCost + /* SSTORE in constructor */
|
||||||
|
params.WitnessChunkReadCost + params.WitnessChunkWriteCost + /* write code hash for tx creation */
|
||||||
|
15*(params.WitnessChunkReadCost+params.WitnessChunkWriteCost) + /* code chunks #0..#14 */
|
||||||
|
4844 /* execution costs */
|
||||||
blockGasUsagesExpected := []uint64{
|
blockGasUsagesExpected := []uint64{
|
||||||
txCost1*2 + txCost2,
|
txCost1*2 + txCost2,
|
||||||
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
|
txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas,
|
||||||
}
|
}
|
||||||
_, chain, _, _, _ := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) {
|
_, chain, _, proofs, statediffs := GenerateVerkleChainWithGenesis(gspec, beacon.New(ethash.NewFaker()), 2, func(i int, gen *BlockGen) {
|
||||||
gen.SetPoS()
|
gen.SetPoS()
|
||||||
|
|
||||||
// TODO need to check that the tx cost provided is the exact amount used (no remaining left-over)
|
// TODO need to check that the tx cost provided is the exact amount used (no remaining left-over)
|
||||||
|
|
@ -516,7 +547,17 @@ func TestProcessVerkle(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Log("inserting blocks into the chain")
|
// Check proof for both blocks
|
||||||
|
err := verkle.Verify(proofs[0], gspec.ToBlock().Root().Bytes(), chain[0].Root().Bytes(), statediffs[0])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err = verkle.Verify(proofs[1], chain[0].Root().Bytes(), chain[1].Root().Bytes(), statediffs[1])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("verified verkle proof, inserting blocks into the chain")
|
||||||
|
|
||||||
endnum, err := blockchain.InsertChain(chain)
|
endnum, err := blockchain.InsertChain(chain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -536,3 +577,54 @@ func TestProcessVerkle(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProcessParentBlockHash(t *testing.T) {
|
||||||
|
var (
|
||||||
|
chainConfig = params.MergedTestChainConfig
|
||||||
|
hashA = common.Hash{0x01}
|
||||||
|
hashB = common.Hash{0x02}
|
||||||
|
header = &types.Header{ParentHash: hashA, Number: big.NewInt(2), Difficulty: big.NewInt(0)}
|
||||||
|
parent = &types.Header{ParentHash: hashB, Number: big.NewInt(1), Difficulty: big.NewInt(0)}
|
||||||
|
coinbase = common.Address{}
|
||||||
|
)
|
||||||
|
test := func(statedb *state.StateDB) {
|
||||||
|
statedb.SetNonce(params.HistoryStorageAddress, 1)
|
||||||
|
statedb.SetCode(params.HistoryStorageAddress, params.HistoryStorageCode)
|
||||||
|
statedb.IntermediateRoot(true)
|
||||||
|
|
||||||
|
vmContext := NewEVMBlockContext(header, nil, &coinbase)
|
||||||
|
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
|
||||||
|
ProcessParentBlockHash(header.ParentHash, evm, statedb)
|
||||||
|
|
||||||
|
vmContext = NewEVMBlockContext(parent, nil, &coinbase)
|
||||||
|
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
|
||||||
|
ProcessParentBlockHash(parent.ParentHash, evm, statedb)
|
||||||
|
|
||||||
|
// make sure that the state is correct
|
||||||
|
if have := getParentBlockHash(statedb, 1); have != hashA {
|
||||||
|
t.Errorf("want parent hash %v, have %v", hashA, have)
|
||||||
|
}
|
||||||
|
if have := getParentBlockHash(statedb, 0); have != hashB {
|
||||||
|
t.Errorf("want parent hash %v, have %v", hashB, have)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Run("MPT", func(t *testing.T) {
|
||||||
|
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabaseForTesting())
|
||||||
|
test(statedb)
|
||||||
|
})
|
||||||
|
t.Run("Verkle", func(t *testing.T) {
|
||||||
|
db := rawdb.NewMemoryDatabase()
|
||||||
|
cacheConfig := DefaultCacheConfigWithScheme(rawdb.PathScheme)
|
||||||
|
cacheConfig.SnapshotLimit = 0
|
||||||
|
triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig(true))
|
||||||
|
statedb, _ := state.New(types.EmptyVerkleHash, state.NewDatabase(triedb, nil))
|
||||||
|
test(statedb)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func getParentBlockHash(statedb *state.StateDB, number uint64) common.Hash {
|
||||||
|
ringIndex := number % params.HistoryServeWindow
|
||||||
|
var key common.Hash
|
||||||
|
binary.BigEndian.PutUint64(key[24:], ringIndex)
|
||||||
|
return statedb.GetState(params.HistoryStorageAddress, key)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -158,10 +157,13 @@ type Message struct {
|
||||||
BlobGasFeeCap *big.Int
|
BlobGasFeeCap *big.Int
|
||||||
BlobHashes []common.Hash
|
BlobHashes []common.Hash
|
||||||
|
|
||||||
// When SkipAccountChecks is true, the message nonce is not checked against the
|
// When SkipNonceChecks is true, the message nonce is not checked against the
|
||||||
// account nonce in state. It also disables checking that the sender is an EOA.
|
// account nonce in state.
|
||||||
// This field will be set to true for operations like RPC eth_call.
|
// This field will be set to true for operations like RPC eth_call.
|
||||||
SkipAccountChecks bool
|
SkipNonceChecks bool
|
||||||
|
|
||||||
|
// When SkipFromEOACheck is true, the message sender is not checked to be an EOA.
|
||||||
|
SkipFromEOACheck bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransactionToMessage converts a transaction into a Message.
|
// TransactionToMessage converts a transaction into a Message.
|
||||||
|
|
@ -176,7 +178,8 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
|
||||||
Value: tx.Value(),
|
Value: tx.Value(),
|
||||||
Data: tx.Data(),
|
Data: tx.Data(),
|
||||||
AccessList: tx.AccessList(),
|
AccessList: tx.AccessList(),
|
||||||
SkipAccountChecks: false,
|
SkipNonceChecks: false,
|
||||||
|
SkipFromEOACheck: false,
|
||||||
BlobHashes: tx.BlobHashes(),
|
BlobHashes: tx.BlobHashes(),
|
||||||
BlobGasFeeCap: tx.BlobGasFeeCap(),
|
BlobGasFeeCap: tx.BlobGasFeeCap(),
|
||||||
}
|
}
|
||||||
|
|
@ -198,15 +201,15 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
|
||||||
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
||||||
// indicates a core error meaning that the message would always fail for that particular
|
// indicates a core error meaning that the message would always fail for that particular
|
||||||
// state and would never be accepted within a block.
|
// state and would never be accepted within a block.
|
||||||
func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool, interruptCtx context.Context) (*ExecutionResult, error) {
|
func ApplyMessage(evm *vm.EVM, msg *Message, gp *GasPool) (*ExecutionResult, error) {
|
||||||
return NewStateTransition(evm, msg, gp).TransitionDb(interruptCtx)
|
return NewStateTransition(evm, msg, gp).TransitionDb()
|
||||||
}
|
}
|
||||||
|
|
||||||
func ApplyMessageNoFeeBurnOrTip(evm *vm.EVM, msg Message, gp *GasPool, interruptCtx context.Context) (*ExecutionResult, error) {
|
func ApplyMessageNoFeeBurnOrTip(evm *vm.EVM, msg Message, gp *GasPool) (*ExecutionResult, error) {
|
||||||
st := NewStateTransition(evm, &msg, gp)
|
st := NewStateTransition(evm, &msg, gp)
|
||||||
st.noFeeBurnAndTip = true
|
st.noFeeBurnAndTip = true
|
||||||
|
|
||||||
return st.TransitionDb(interruptCtx)
|
return st.TransitionDb()
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateTransition represents a state transition.
|
// StateTransition represents a state transition.
|
||||||
|
|
@ -312,7 +315,7 @@ func (st *StateTransition) buyGas() error {
|
||||||
func (st *StateTransition) preCheck() error {
|
func (st *StateTransition) preCheck() error {
|
||||||
// Only check transactions that are not fake
|
// Only check transactions that are not fake
|
||||||
msg := st.msg
|
msg := st.msg
|
||||||
if !msg.SkipAccountChecks {
|
if !msg.SkipNonceChecks {
|
||||||
// Make sure this transaction's nonce is correct.
|
// Make sure this transaction's nonce is correct.
|
||||||
stNonce := st.state.GetNonce(msg.From)
|
stNonce := st.state.GetNonce(msg.From)
|
||||||
if msgNonce := msg.Nonce; stNonce < msgNonce {
|
if msgNonce := msg.Nonce; stNonce < msgNonce {
|
||||||
|
|
@ -325,6 +328,8 @@ func (st *StateTransition) preCheck() error {
|
||||||
return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
|
return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
|
||||||
msg.From.Hex(), stNonce)
|
msg.From.Hex(), stNonce)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if !msg.SkipFromEOACheck {
|
||||||
// Make sure the sender is an EOA
|
// Make sure the sender is an EOA
|
||||||
codeHash := st.state.GetCodeHash(msg.From)
|
codeHash := st.state.GetCodeHash(msg.From)
|
||||||
if codeHash != (common.Hash{}) && codeHash != types.EmptyCodeHash {
|
if codeHash != (common.Hash{}) && codeHash != types.EmptyCodeHash {
|
||||||
|
|
@ -404,7 +409,7 @@ func (st *StateTransition) preCheck() error {
|
||||||
//
|
//
|
||||||
// However if any consensus issue encountered, return the error directly with
|
// However if any consensus issue encountered, return the error directly with
|
||||||
// nil evm execution result.
|
// nil evm execution result.
|
||||||
func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*ExecutionResult, error) {
|
func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||||
input1 := st.state.GetBalance(st.msg.From)
|
input1 := st.state.GetBalance(st.msg.From)
|
||||||
|
|
||||||
var input2 *uint256.Int
|
var input2 *uint256.Int
|
||||||
|
|
@ -488,7 +493,7 @@ func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*Executio
|
||||||
} else {
|
} else {
|
||||||
st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1)
|
st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1)
|
||||||
|
|
||||||
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value, interruptCtx)
|
ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
var gasRefund uint64
|
var gasRefund uint64
|
||||||
|
|
@ -537,7 +542,7 @@ func (st *StateTransition) TransitionDb(interruptCtx context.Context) (*Executio
|
||||||
|
|
||||||
// add the coinbase to the witness iff the fee is greater than 0
|
// add the coinbase to the witness iff the fee is greater than 0
|
||||||
if rules.IsEIP4762 && amount.Sign() != 0 {
|
if rules.IsEIP4762 && amount.Sign() != 0 {
|
||||||
st.evm.AccessEvents.BalanceGas(st.evm.Context.Coinbase, true)
|
st.evm.AccessEvents.AddAccount(st.evm.Context.Coinbase, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
output1 := new(big.Int).SetBytes(input1.Bytes())
|
output1 := new(big.Int).SetBytes(input1.Bytes())
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/lru"
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||||
|
|
@ -27,25 +25,33 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/stateless"
|
"github.com/ethereum/go-ethereum/core/stateless"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/triedb"
|
"github.com/ethereum/go-ethereum/triedb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExecuteStateless runs a stateless execution based on a witness, verifies
|
// ExecuteStateless runs a stateless execution based on a witness, verifies
|
||||||
// everything it can locally and returns the two computed fields that need the
|
// everything it can locally and returns the state root and receipt root, that
|
||||||
// other side to explicitly check.
|
// need the other side to explicitly check.
|
||||||
//
|
//
|
||||||
// This method is a bit of a sore thumb here, but:
|
// This method is a bit of a sore thumb here, but:
|
||||||
// - It cannot be placed in core/stateless, because state.New prodces a circular dep
|
// - It cannot be placed in core/stateless, because state.New prodces a circular dep
|
||||||
// - It cannot be placed outside of core, because it needs to construct a dud headerchain
|
// - It cannot be placed outside of core, because it needs to construct a dud headerchain
|
||||||
//
|
//
|
||||||
// TODO(karalabe): Would be nice to resolve both issues above somehow and move it.
|
// TODO(karalabe): Would be nice to resolve both issues above somehow and move it.
|
||||||
func ExecuteStateless(config *params.ChainConfig, witness *stateless.Witness) (common.Hash, common.Hash, error) {
|
func ExecuteStateless(config *params.ChainConfig, block *types.Block, witness *stateless.Witness) (common.Hash, common.Hash, error) {
|
||||||
|
// Sanity check if the supplied block accidentally contains a set root or
|
||||||
|
// receipt hash. If so, be very loud, but still continue.
|
||||||
|
if block.Root() != (common.Hash{}) {
|
||||||
|
log.Error("stateless runner received state root it's expected to calculate (faulty consensus client)", "block", block.Number())
|
||||||
|
}
|
||||||
|
if block.ReceiptHash() != (common.Hash{}) {
|
||||||
|
log.Error("stateless runner received receipt root it's expected to calculate (faulty consensus client)", "block", block.Number())
|
||||||
|
}
|
||||||
// Create and populate the state database to serve as the stateless backend
|
// Create and populate the state database to serve as the stateless backend
|
||||||
memdb := witness.MakeHashDB()
|
memdb := witness.MakeHashDB()
|
||||||
|
db, err := state.New(witness.Root(), state.NewDatabase(triedb.NewDatabase(memdb, triedb.HashDefaults), nil))
|
||||||
db, err := state.New(witness.Root(), state.NewDatabaseWithConfig(memdb, triedb.HashDefaults), nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, common.Hash{}, err
|
return common.Hash{}, common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -60,16 +66,15 @@ func ExecuteStateless(config *params.ChainConfig, witness *stateless.Witness) (c
|
||||||
validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block
|
validator := NewBlockValidator(config, nil) // No chain, we only validate the state, not the block
|
||||||
|
|
||||||
// Run the stateless blocks processing and self-validate certain fields
|
// Run the stateless blocks processing and self-validate certain fields
|
||||||
receipts, _, usedGas, err := processor.Process(witness.Block, db, vm.Config{}, context.Background())
|
res, err := processor.Process(block, db, vm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, common.Hash{}, err
|
return common.Hash{}, common.Hash{}, err
|
||||||
}
|
}
|
||||||
if err = validator.ValidateState(witness.Block, db, receipts, usedGas, true); err != nil {
|
if err = validator.ValidateState(block, db, res, true); err != nil {
|
||||||
return common.Hash{}, common.Hash{}, err
|
return common.Hash{}, common.Hash{}, err
|
||||||
}
|
}
|
||||||
// Almost everything validated, but receipt and state root needs to be returned
|
// Almost everything validated, but receipt and state root needs to be returned
|
||||||
receiptRoot := types.DeriveSha(receipts, trie.NewStackTrie(nil))
|
receiptRoot := types.DeriveSha(res.Receipts, trie.NewStackTrie(nil))
|
||||||
stateRoot := db.IntermediateRoot(config.IsEIP158(witness.Block.Number()))
|
stateRoot := db.IntermediateRoot(config.IsEIP158(block.Number()))
|
||||||
|
return stateRoot, receiptRoot, nil
|
||||||
return receiptRoot, stateRoot, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,13 @@ import (
|
||||||
// MakeHashDB imports tries, codes and block hashes from a witness into a new
|
// MakeHashDB imports tries, codes and block hashes from a witness into a new
|
||||||
// hash-based memory db. We could eventually rewrite this into a pathdb, but
|
// hash-based memory db. We could eventually rewrite this into a pathdb, but
|
||||||
// simple is better for now.
|
// simple is better for now.
|
||||||
|
//
|
||||||
|
// Note, this hashdb approach is quite strictly self-validating:
|
||||||
|
// - Headers are persisted keyed by hash, so blockhash will error on junk
|
||||||
|
// - Codes are persisted keyed by hash, so bytecode lookup will error on junk
|
||||||
|
// - Trie nodes are persisted keyed by hash, so trie expansion will error on junk
|
||||||
|
//
|
||||||
|
// Acceleration structures built would need to explicitly validate the witness.
|
||||||
func (w *Witness) MakeHashDB() ethdb.Database {
|
func (w *Witness) MakeHashDB() ethdb.Database {
|
||||||
var (
|
var (
|
||||||
memdb = rawdb.NewMemoryDatabase()
|
memdb = rawdb.NewMemoryDatabase()
|
||||||
|
|
|
||||||
|
|
@ -17,42 +17,31 @@
|
||||||
package stateless
|
package stateless
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:generate go run github.com/fjl/gencodec -type extWitness -field-override extWitnessMarshalling -out gen_encoding_json.go
|
|
||||||
|
|
||||||
// toExtWitness converts our internal witness representation to the consensus one.
|
// toExtWitness converts our internal witness representation to the consensus one.
|
||||||
func (w *Witness) toExtWitness() *extWitness {
|
func (w *Witness) toExtWitness() *extWitness {
|
||||||
ext := &extWitness{
|
ext := &extWitness{
|
||||||
Block: w.Block,
|
|
||||||
Headers: w.Headers,
|
Headers: w.Headers,
|
||||||
}
|
}
|
||||||
ext.Codes = make([][]byte, 0, len(w.Codes))
|
ext.Codes = make([][]byte, 0, len(w.Codes))
|
||||||
for code := range w.Codes {
|
for code := range w.Codes {
|
||||||
ext.Codes = append(ext.Codes, []byte(code))
|
ext.Codes = append(ext.Codes, []byte(code))
|
||||||
}
|
}
|
||||||
slices.SortFunc(ext.Codes, bytes.Compare)
|
|
||||||
|
|
||||||
ext.State = make([][]byte, 0, len(w.State))
|
ext.State = make([][]byte, 0, len(w.State))
|
||||||
for node := range w.State {
|
for node := range w.State {
|
||||||
ext.State = append(ext.State, []byte(node))
|
ext.State = append(ext.State, []byte(node))
|
||||||
}
|
}
|
||||||
slices.SortFunc(ext.State, bytes.Compare)
|
|
||||||
return ext
|
return ext
|
||||||
}
|
}
|
||||||
|
|
||||||
// fromExtWitness converts the consensus witness format into our internal one.
|
// fromExtWitness converts the consensus witness format into our internal one.
|
||||||
func (w *Witness) fromExtWitness(ext *extWitness) error {
|
func (w *Witness) fromExtWitness(ext *extWitness) error {
|
||||||
w.Block, w.Headers = ext.Block, ext.Headers
|
w.Headers = ext.Headers
|
||||||
|
|
||||||
w.Codes = make(map[string]struct{}, len(ext.Codes))
|
w.Codes = make(map[string]struct{}, len(ext.Codes))
|
||||||
for _, code := range ext.Codes {
|
for _, code := range ext.Codes {
|
||||||
|
|
@ -62,12 +51,7 @@ func (w *Witness) fromExtWitness(ext *extWitness) error {
|
||||||
for _, node := range ext.State {
|
for _, node := range ext.State {
|
||||||
w.State[string(node)] = struct{}{}
|
w.State[string(node)] = struct{}{}
|
||||||
}
|
}
|
||||||
return w.sanitize()
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON marshals a witness as JSON.
|
|
||||||
func (w *Witness) MarshalJSON() ([]byte, error) {
|
|
||||||
return w.toExtWitness().MarshalJSON()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeRLP serializes a witness as RLP.
|
// EncodeRLP serializes a witness as RLP.
|
||||||
|
|
@ -75,15 +59,6 @@ func (w *Witness) EncodeRLP(wr io.Writer) error {
|
||||||
return rlp.Encode(wr, w.toExtWitness())
|
return rlp.Encode(wr, w.toExtWitness())
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
|
||||||
func (w *Witness) UnmarshalJSON(input []byte) error {
|
|
||||||
var ext extWitness
|
|
||||||
if err := ext.UnmarshalJSON(input); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return w.fromExtWitness(&ext)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeRLP decodes a witness from RLP.
|
// DecodeRLP decodes a witness from RLP.
|
||||||
func (w *Witness) DecodeRLP(s *rlp.Stream) error {
|
func (w *Witness) DecodeRLP(s *rlp.Stream) error {
|
||||||
var ext extWitness
|
var ext extWitness
|
||||||
|
|
@ -93,37 +68,9 @@ func (w *Witness) DecodeRLP(s *rlp.Stream) error {
|
||||||
return w.fromExtWitness(&ext)
|
return w.fromExtWitness(&ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sanitize checks for some mandatory fields in the witness after decoding so
|
|
||||||
// the rest of the code can assume invariants and doesn't have to deal with
|
|
||||||
// corrupted data.
|
|
||||||
func (w *Witness) sanitize() error {
|
|
||||||
// Verify that the "parent" header (i.e. index 0) is available, and is the
|
|
||||||
// true parent of the block-to-be executed, since we use that to link the
|
|
||||||
// current block to the pre-state.
|
|
||||||
if len(w.Headers) == 0 {
|
|
||||||
return errors.New("parent header (for pre-root hash) missing")
|
|
||||||
}
|
|
||||||
for i, header := range w.Headers {
|
|
||||||
if header == nil {
|
|
||||||
return fmt.Errorf("witness header nil at position %d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if w.Headers[0].Hash() != w.Block.ParentHash() {
|
|
||||||
return fmt.Errorf("parent hash different: witness %v, block parent %v", w.Headers[0].Hash(), w.Block.ParentHash())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// extWitness is a witness RLP encoding for transferring across clients.
|
// extWitness is a witness RLP encoding for transferring across clients.
|
||||||
type extWitness struct {
|
type extWitness struct {
|
||||||
Block *types.Block `json:"block" gencodec:"required"`
|
Headers []*types.Header
|
||||||
Headers []*types.Header `json:"headers" gencodec:"required"`
|
Codes [][]byte
|
||||||
Codes [][]byte `json:"codes"`
|
State [][]byte
|
||||||
State [][]byte `json:"state"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// extWitnessMarshalling defines the hex marshalling types for a witness.
|
|
||||||
type extWitnessMarshalling struct {
|
|
||||||
Codes []hexutil.Bytes
|
|
||||||
State []hexutil.Bytes
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
|
||||||
|
|
||||||
package stateless
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ = (*extWitnessMarshalling)(nil)
|
|
||||||
|
|
||||||
// MarshalJSON marshals as JSON.
|
|
||||||
func (e extWitness) MarshalJSON() ([]byte, error) {
|
|
||||||
type extWitness struct {
|
|
||||||
Block *types.Block `json:"block" gencodec:"required"`
|
|
||||||
Headers []*types.Header `json:"headers" gencodec:"required"`
|
|
||||||
Codes []hexutil.Bytes `json:"codes"`
|
|
||||||
State []hexutil.Bytes `json:"state"`
|
|
||||||
}
|
|
||||||
var enc extWitness
|
|
||||||
enc.Block = e.Block
|
|
||||||
enc.Headers = e.Headers
|
|
||||||
if e.Codes != nil {
|
|
||||||
enc.Codes = make([]hexutil.Bytes, len(e.Codes))
|
|
||||||
for k, v := range e.Codes {
|
|
||||||
enc.Codes[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e.State != nil {
|
|
||||||
enc.State = make([]hexutil.Bytes, len(e.State))
|
|
||||||
for k, v := range e.State {
|
|
||||||
enc.State[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return json.Marshal(&enc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON unmarshals from JSON.
|
|
||||||
func (e *extWitness) UnmarshalJSON(input []byte) error {
|
|
||||||
type extWitness struct {
|
|
||||||
Block *types.Block `json:"block" gencodec:"required"`
|
|
||||||
Headers []*types.Header `json:"headers" gencodec:"required"`
|
|
||||||
Codes []hexutil.Bytes `json:"codes"`
|
|
||||||
State []hexutil.Bytes `json:"state"`
|
|
||||||
}
|
|
||||||
var dec extWitness
|
|
||||||
if err := json.Unmarshal(input, &dec); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if dec.Block == nil {
|
|
||||||
return errors.New("missing required field 'block' for extWitness")
|
|
||||||
}
|
|
||||||
e.Block = dec.Block
|
|
||||||
if dec.Headers == nil {
|
|
||||||
return errors.New("missing required field 'headers' for extWitness")
|
|
||||||
}
|
|
||||||
e.Headers = dec.Headers
|
|
||||||
if dec.Codes != nil {
|
|
||||||
e.Codes = make([][]byte, len(dec.Codes))
|
|
||||||
for k, v := range dec.Codes {
|
|
||||||
e.Codes[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dec.State != nil {
|
|
||||||
e.State = make([][]byte, len(dec.State))
|
|
||||||
for k, v := range dec.State {
|
|
||||||
e.State[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -17,16 +17,13 @@
|
||||||
package stateless
|
package stateless
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"maps"
|
"maps"
|
||||||
"slices"
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// HeaderReader is an interface to pull in headers in place of block hashes for
|
// HeaderReader is an interface to pull in headers in place of block hashes for
|
||||||
|
|
@ -36,10 +33,11 @@ type HeaderReader interface {
|
||||||
GetHeader(hash common.Hash, number uint64) *types.Header
|
GetHeader(hash common.Hash, number uint64) *types.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
// Witness encompasses a block, state and any other chain data required to apply
|
// Witness encompasses the state required to apply a set of transactions and
|
||||||
// a set of transactions and derive a post state/receipt root.
|
// derive a post state/receipt root.
|
||||||
type Witness struct {
|
type Witness struct {
|
||||||
Block *types.Block // Current block with rootHash and receiptHash zeroed out
|
context *types.Header // Header to which this witness belongs to, with rootHash and receiptHash zeroed out
|
||||||
|
|
||||||
Headers []*types.Header // Past headers in reverse order (0=parent, 1=parent's-parent, etc). First *must* be set.
|
Headers []*types.Header // Past headers in reverse order (0=parent, 1=parent's-parent, etc). First *must* be set.
|
||||||
Codes map[string]struct{} // Set of bytecodes ran or accessed
|
Codes map[string]struct{} // Set of bytecodes ran or accessed
|
||||||
State map[string]struct{} // Set of MPT state trie nodes (account and storage together)
|
State map[string]struct{} // Set of MPT state trie nodes (account and storage together)
|
||||||
|
|
@ -49,24 +47,23 @@ type Witness struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWitness creates an empty witness ready for population.
|
// NewWitness creates an empty witness ready for population.
|
||||||
func NewWitness(chain HeaderReader, block *types.Block) (*Witness, error) {
|
func NewWitness(context *types.Header, chain HeaderReader) (*Witness, error) {
|
||||||
// Zero out the result fields to avoid accidentally sending them to the verifier
|
// When building witnesses, retrieve the parent header, which will *always*
|
||||||
header := block.Header()
|
// be included to act as a trustless pre-root hash container
|
||||||
header.Root = common.Hash{}
|
var headers []*types.Header
|
||||||
header.ReceiptHash = common.Hash{}
|
if chain != nil {
|
||||||
|
parent := chain.GetHeader(context.ParentHash, context.Number.Uint64()-1)
|
||||||
// Retrieve the parent header, which will *always* be included to act as a
|
|
||||||
// trustless pre-root hash container
|
|
||||||
parent := chain.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return nil, errors.New("failed to retrieve parent header")
|
return nil, errors.New("failed to retrieve parent header")
|
||||||
}
|
}
|
||||||
|
headers = append(headers, parent)
|
||||||
|
}
|
||||||
// Create the wtness with a reconstructed gutted out block
|
// Create the wtness with a reconstructed gutted out block
|
||||||
return &Witness{
|
return &Witness{
|
||||||
Block: types.NewBlockWithHeader(header).WithBody(*block.Body()),
|
context: context,
|
||||||
|
Headers: headers,
|
||||||
Codes: make(map[string]struct{}),
|
Codes: make(map[string]struct{}),
|
||||||
State: make(map[string]struct{}),
|
State: make(map[string]struct{}),
|
||||||
Headers: []*types.Header{parent},
|
|
||||||
chain: chain,
|
chain: chain,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -76,11 +73,8 @@ func NewWitness(chain HeaderReader, block *types.Block) (*Witness, error) {
|
||||||
// the chain to cover the block being added.
|
// the chain to cover the block being added.
|
||||||
func (w *Witness) AddBlockHash(number uint64) {
|
func (w *Witness) AddBlockHash(number uint64) {
|
||||||
// Keep pulling in headers until this hash is populated
|
// Keep pulling in headers until this hash is populated
|
||||||
for int(w.Block.NumberU64()-number) > len(w.Headers) {
|
for int(w.context.Number.Uint64()-number) > len(w.Headers) {
|
||||||
tail := w.Block.Header()
|
tail := w.Headers[len(w.Headers)-1]
|
||||||
if len(w.Headers) > 0 {
|
|
||||||
tail = w.Headers[len(w.Headers)-1]
|
|
||||||
}
|
|
||||||
w.Headers = append(w.Headers, w.chain.GetHeader(tail.ParentHash, tail.Number.Uint64()-1))
|
w.Headers = append(w.Headers, w.chain.GetHeader(tail.ParentHash, tail.Number.Uint64()-1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -101,53 +95,22 @@ func (w *Witness) AddState(nodes map[string]struct{}) {
|
||||||
w.lock.Lock()
|
w.lock.Lock()
|
||||||
defer w.lock.Unlock()
|
defer w.lock.Unlock()
|
||||||
|
|
||||||
for node := range nodes {
|
maps.Copy(w.State, nodes)
|
||||||
w.State[node] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy deep-copies the witness object. Witness.Block isn't deep-copied as it
|
// Copy deep-copies the witness object. Witness.Block isn't deep-copied as it
|
||||||
// is never mutated by Witness
|
// is never mutated by Witness
|
||||||
func (w *Witness) Copy() *Witness {
|
func (w *Witness) Copy() *Witness {
|
||||||
return &Witness{
|
cpy := &Witness{
|
||||||
Block: w.Block,
|
|
||||||
Headers: slices.Clone(w.Headers),
|
Headers: slices.Clone(w.Headers),
|
||||||
Codes: maps.Clone(w.Codes),
|
Codes: maps.Clone(w.Codes),
|
||||||
State: maps.Clone(w.State),
|
State: maps.Clone(w.State),
|
||||||
|
chain: w.chain,
|
||||||
}
|
}
|
||||||
|
if w.context != nil {
|
||||||
|
cpy.context = types.CopyHeader(w.context)
|
||||||
}
|
}
|
||||||
|
return cpy
|
||||||
// String prints a human-readable summary containing the total size of the
|
|
||||||
// witness and the sizes of the underlying components
|
|
||||||
func (w *Witness) String() string {
|
|
||||||
blob, _ := rlp.EncodeToBytes(w)
|
|
||||||
bytesTotal := len(blob)
|
|
||||||
|
|
||||||
blob, _ = rlp.EncodeToBytes(w.Block)
|
|
||||||
bytesBlock := len(blob)
|
|
||||||
|
|
||||||
bytesHeaders := 0
|
|
||||||
for _, header := range w.Headers {
|
|
||||||
blob, _ = rlp.EncodeToBytes(header)
|
|
||||||
bytesHeaders += len(blob)
|
|
||||||
}
|
|
||||||
bytesCodes := 0
|
|
||||||
for code := range w.Codes {
|
|
||||||
bytesCodes += len(code)
|
|
||||||
}
|
|
||||||
bytesState := 0
|
|
||||||
for node := range w.State {
|
|
||||||
bytesState += len(node)
|
|
||||||
}
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
|
|
||||||
fmt.Fprintf(buf, "Witness #%d: %v\n", w.Block.Number(), common.StorageSize(bytesTotal))
|
|
||||||
fmt.Fprintf(buf, " block (%4d txs): %10v\n", len(w.Block.Transactions()), common.StorageSize(bytesBlock))
|
|
||||||
fmt.Fprintf(buf, "%4d headers: %10v\n", len(w.Headers), common.StorageSize(bytesHeaders))
|
|
||||||
fmt.Fprintf(buf, "%4d trie nodes: %10v\n", len(w.State), common.StorageSize(bytesState))
|
|
||||||
fmt.Fprintf(buf, "%4d codes: %10v\n", len(w.Codes), common.StorageSize(bytesCodes))
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root returns the pre-state root from the first header.
|
// Root returns the pre-state root from the first header.
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,24 @@
|
||||||
|
|
||||||
All notable changes to the tracing interface will be documented in this file.
|
All notable changes to the tracing interface will be documented in this file.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Modified types
|
||||||
|
|
||||||
|
- `GasChangeReason` has been extended with the following reasons which will be enabled only post-Verkle. There shouldn't be any gas changes with those reasons prior to the fork.
|
||||||
|
- `GasChangeWitnessContractCollisionCheck` flags the event of adding to the witness when checking for contract address collision.
|
||||||
|
|
||||||
|
## [v1.14.4]
|
||||||
|
|
||||||
|
This release contained only minor extensions to the tracing interface.
|
||||||
|
|
||||||
|
### Modified types
|
||||||
|
|
||||||
|
- `GasChangeReason` has been extended with the following reasons that will only be active post-Verkle.
|
||||||
|
- `GasChangeWitnessContractInit` flags the event of adding to the witness during the contract creation initialization step.
|
||||||
|
- `GasChangeWitnessContractCreation` flags the event of adding to the witness during the contract creation finalization step.
|
||||||
|
- `GasChangeWitnessCodeChunk` flags the event of adding one or more contract code chunks to the witness.
|
||||||
|
|
||||||
## [v1.14.3]
|
## [v1.14.3]
|
||||||
|
|
||||||
There have been minor backwards-compatible changes to the tracing interface to explicitly mark the execution of **system** contracts. As of now the only system call updates the parent beacon block root as per [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788). Other system calls are being considered for the future hardfork.
|
There have been minor backwards-compatible changes to the tracing interface to explicitly mark the execution of **system** contracts. As of now the only system call updates the parent beacon block root as per [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788). Other system calls are being considered for the future hardfork.
|
||||||
|
|
@ -75,6 +93,7 @@ The hooks `CaptureStart` and `CaptureEnd` have been removed. These hooks signale
|
||||||
- `CaptureState` -> `OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error)`. `op` is of type `byte` which can be cast to `vm.OpCode` when necessary. A `*vm.ScopeContext` is not passed anymore. It is replaced by `tracing.OpContext` which offers access to the memory, stack and current contract.
|
- `CaptureState` -> `OnOpcode(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, rData []byte, depth int, err error)`. `op` is of type `byte` which can be cast to `vm.OpCode` when necessary. A `*vm.ScopeContext` is not passed anymore. It is replaced by `tracing.OpContext` which offers access to the memory, stack and current contract.
|
||||||
- `CaptureFault` -> `OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error)`. Similar to above.
|
- `CaptureFault` -> `OnFault(pc uint64, op byte, gas, cost uint64, scope tracing.OpContext, depth int, err error)`. Similar to above.
|
||||||
|
|
||||||
[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.14.0...master
|
[unreleased]: https://github.com/ethereum/go-ethereum/compare/v1.14.8...master
|
||||||
[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0
|
[v1.14.0]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.0
|
||||||
[v1.14.3]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.3
|
[v1.14.3]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.3
|
||||||
|
[v1.14.4]: https://github.com/ethereum/go-ethereum/releases/tag/v1.14.4
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue