Compare commits
142 Commits
v1.4.2-alp
...
merge-tx-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be71d41aa5 | ||
|
|
5b46fe13e7 | ||
|
|
b0146261c7 | ||
|
|
d7b9866d3b | ||
|
|
f5ba30ed47 | ||
|
|
f190c49252 | ||
|
|
08769ead2b | ||
|
|
4d0f1e7117 | ||
|
|
c77bb1110d | ||
|
|
c856d21719 | ||
|
|
f45305b1ad | ||
|
|
d16532d678 | ||
|
|
5edd032cdb | ||
|
|
6b8cbbe172 | ||
|
|
5ea2ada0ee | ||
|
|
b230a02006 | ||
|
|
86e3a02490 | ||
|
|
0c0958ff87 | ||
|
|
c577ce3720 | ||
|
|
d436f9e2e8 | ||
|
|
97c3b9b267 | ||
|
|
0560685460 | ||
|
|
bf16a39876 | ||
|
|
2c8720016d | ||
|
|
f2ec3cc6a5 | ||
|
|
0a2e1282d2 | ||
|
|
adb5e8fe86 | ||
|
|
23f6194fad | ||
|
|
691d195526 | ||
|
|
b57c779759 | ||
|
|
4ab1c865b2 | ||
|
|
a7d5b02919 | ||
|
|
1ce9bb044d | ||
|
|
7948950f7a | ||
|
|
0c101e618a | ||
|
|
571ea2c4b9 | ||
|
|
7bc5a3353d | ||
|
|
901ea2e0d2 | ||
|
|
1d81f3316f | ||
|
|
43b2ffa63b | ||
|
|
0567715760 | ||
|
|
e32fcf5b93 | ||
|
|
e55028d788 | ||
|
|
9d8df917b8 | ||
|
|
9e170972f4 | ||
|
|
ba6726325a | ||
|
|
6573254a62 | ||
|
|
31d92c50ad | ||
|
|
7cab9c622c | ||
|
|
2a0e399c38 | ||
|
|
182c841374 | ||
|
|
14023fae6d | ||
|
|
d653cda82e | ||
|
|
4b54601d5c | ||
|
|
3b7f0e4279 | ||
|
|
fe1fff8c77 | ||
|
|
c0afdc9a98 | ||
|
|
fb435eb5f1 | ||
|
|
5cc253a2cd | ||
|
|
cbcd26c9a9 | ||
|
|
90eb5b33e8 | ||
|
|
837de88057 | ||
|
|
b4fb2f6ffc | ||
|
|
11503edeb2 | ||
|
|
3a6e3c67f2 | ||
|
|
335be39905 | ||
|
|
b7972bcd77 | ||
|
|
4bb1bd1a77 | ||
|
|
a05724588f | ||
|
|
009df5a121 | ||
|
|
d7836bfe98 | ||
|
|
f4bad20447 | ||
|
|
a75e82367d | ||
|
|
26f50099f4 | ||
|
|
060e5c6b34 | ||
|
|
4e9f699068 | ||
|
|
42a0236587 | ||
|
|
48f58a50bb | ||
|
|
e4688e4e7a | ||
|
|
75a03f420f | ||
|
|
46df9b4dcb | ||
|
|
7dbafe7453 | ||
|
|
f2c9141e4f | ||
|
|
c3623e9af7 | ||
|
|
32ac07f257 | ||
|
|
3b7ee60e14 | ||
|
|
438cdf0861 | ||
|
|
212b7a6972 | ||
|
|
7c7a9bc53b | ||
|
|
04a3b1f94f | ||
|
|
5d51873890 | ||
|
|
3e97f827b4 | ||
|
|
9ab8565128 | ||
|
|
7c89c65a97 | ||
|
|
e7c5ce2e94 | ||
|
|
eda56e22a9 | ||
|
|
dddf20e6e0 | ||
|
|
79cd5222e7 | ||
|
|
38db9bf4e2 | ||
|
|
e0b98ef9cb | ||
|
|
7f3f72ed41 | ||
|
|
76fb29504c | ||
|
|
84c36588cd | ||
|
|
22888c8725 | ||
|
|
e313d5b319 | ||
|
|
1208d07e94 | ||
|
|
fd284c74dd | ||
|
|
fdbe2e3cb0 | ||
|
|
9684ba3a83 | ||
|
|
1324884db7 | ||
|
|
ebe88c09a9 | ||
|
|
66ed85ef82 | ||
|
|
2893079aa4 | ||
|
|
3c81d559e7 | ||
|
|
c1496e7ced | ||
|
|
291ab99d4a | ||
|
|
e6e1d06687 | ||
|
|
c170814596 | ||
|
|
09b469f0bf | ||
|
|
6978f009ab | ||
|
|
74d20546c3 | ||
|
|
ccd7a44be0 | ||
|
|
10ae179a73 | ||
|
|
16b040e8c4 | ||
|
|
880f7ab865 | ||
|
|
58bc494fa7 | ||
|
|
d9b9b7f66b | ||
|
|
e4ddf5881b | ||
|
|
a4b436806e | ||
|
|
0b1438c3df | ||
|
|
0a2f33946b | ||
|
|
865e1e9f57 | ||
|
|
db4cf69166 | ||
|
|
28d55218f7 | ||
|
|
dbc27a199f | ||
|
|
1883438964 | ||
|
|
9986a69c25 | ||
|
|
5bae14f9df | ||
|
|
49623bd469 | ||
|
|
170fcd80c6 | ||
|
|
02d77c98f9 | ||
|
|
57d2b552c7 |
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -1,6 +1,5 @@
|
||||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
accounts/usbwallet @karalabe
|
||||
accounts/scwallet @gballet
|
||||
accounts/abi @gballet @MariusVanDerWijden
|
||||
|
||||
1
.nancy-ignore
Normal file
1
.nancy-ignore
Normal file
@@ -0,0 +1 @@
|
||||
CVE-2024-34478 # "CWE-754: Improper Check for Unusual or Exceptional Conditions." This vulnerability is BTC only, BSC does not have the issue.
|
||||
119
CHANGELOG.md
119
CHANGELOG.md
@@ -1,6 +1,119 @@
|
||||
# Changelog
|
||||
## v1.4.8
|
||||
### FEATURE
|
||||
* [\#2483](https://github.com/bnb-chain/bsc/pull/2483) core/vm: add secp256r1 into PrecompiledContractsHaber
|
||||
* [\#2400](https://github.com/bnb-chain/bsc/pull/2400) RIP-7212: Precompile for secp256r1 Curve Support
|
||||
|
||||
### IMPROVEMENT
|
||||
NA
|
||||
|
||||
### BUGFIX
|
||||
NA
|
||||
|
||||
## v1.4.7
|
||||
### FEATURE
|
||||
* [\#2439](https://github.com/bnb-chain/bsc/pull/2439) config: setup Mainnet Tycho(Cancun) hardfork date
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2396](https://github.com/bnb-chain/bsc/pull/2396) metrics: add blockInsertMgaspsGauge to trace mgasps
|
||||
* [\#2411](https://github.com/bnb-chain/bsc/pull/2411) build(deps): bump golang.org/x/net from 0.19.0 to 0.23.0
|
||||
* [\#2435](https://github.com/bnb-chain/bsc/pull/2435) txpool: limit max gas when mining is enabled
|
||||
* [\#2438](https://github.com/bnb-chain/bsc/pull/2438) fix: performance issue when load journal
|
||||
* [\#2440](https://github.com/bnb-chain/bsc/pull/2440) nancy: add files .nancy-ignore
|
||||
|
||||
### BUGFIX
|
||||
NA
|
||||
|
||||
## v1.4.6
|
||||
### FEATURE
|
||||
* [\#2227](https://github.com/bnb-chain/bsc/pull/2227) core: separated databases for block data
|
||||
* [\#2404](https://github.com/bnb-chain/bsc/pull/2404) cmd, p2p: filter peers by regex on name
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2201](https://github.com/bnb-chain/bsc/pull/2201) chore: render system bytecode by go:embed
|
||||
* [\#2363](https://github.com/bnb-chain/bsc/pull/2363) feat: greedy merge tx in bid
|
||||
* [\#2389](https://github.com/bnb-chain/bsc/pull/2389) deps: update prsym to solve warning about quic-go version
|
||||
* [\#2341](https://github.com/bnb-chain/bsc/pull/2341) core/trie: persist TrieJournal to journal file instead of kv database
|
||||
* [\#2395](https://github.com/bnb-chain/bsc/pull/2395) fix: trieJournal format compatible old db format
|
||||
* [\#2406](https://github.com/bnb-chain/bsc/pull/2406) feat: adaptive for loading journal file or journal kv during loadJournal
|
||||
* [\#2390](https://github.com/bnb-chain/bsc/pull/2390) chore: fix function names in comment
|
||||
* [\#2399](https://github.com/bnb-chain/bsc/pull/2399) chore: fix some typos in comments
|
||||
* [\#2408](https://github.com/bnb-chain/bsc/pull/2408) chore: fix some typos in comments
|
||||
* [\#2416](https://github.com/bnb-chain/bsc/pull/2416) fix: fix function names
|
||||
* [\#2424](https://github.com/bnb-chain/bsc/pull/2424) feat: recommit bid when newBidCh is empty to maximize mev reward
|
||||
* [\#2430](https://github.com/bnb-chain/bsc/pull/2430) fix: oom caused by non-discarded mev simulation env
|
||||
* [\#2428](https://github.com/bnb-chain/bsc/pull/2428) chore: add metric & log for blobTx
|
||||
* [\#2419](https://github.com/bnb-chain/bsc/pull/2419) metrics: add doublesign counter
|
||||
|
||||
### BUGFIX
|
||||
* [\#2244](https://github.com/bnb-chain/bsc/pull/2244) cmd/geth: fix importBlock
|
||||
* [\#2391](https://github.com/bnb-chain/bsc/pull/2391) fix: print value instead of pointer in ConfigCompatError
|
||||
* [\#2398](https://github.com/bnb-chain/bsc/pull/2398) fix: no import blocks before or equal to the finalized height
|
||||
* [\#2401](https://github.com/bnb-chain/bsc/pull/2401) fix: allow fast node to rewind after abnormal shutdown
|
||||
* [\#2403](https://github.com/bnb-chain/bsc/pull/2403) fix: NPE
|
||||
* [\#2423](https://github.com/bnb-chain/bsc/pull/2423) eth/gasprice: add query limit to defend DDOS attack
|
||||
* [\#2425](https://github.com/bnb-chain/bsc/pull/2425) fix: adapt journal for cmd
|
||||
|
||||
## v1.4.5
|
||||
### FEATURE
|
||||
* [\#2378](https://github.com/bnb-chain/bsc/pull/2378) config: setup Testnet Tycho(Cancun) hardfork date
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2333](https://github.com/bnb-chain/bsc/pull/2333) remove code that will not be executed
|
||||
* [\#2369](https://github.com/bnb-chain/bsc/pull/2369) core: stateDb has no trie and no snap return err
|
||||
|
||||
### BUGFIX
|
||||
* [\#2359](https://github.com/bnb-chain/bsc/pull/2359) triedb: do not open state freezer under notries
|
||||
|
||||
## v1.4.4
|
||||
### FEATURE
|
||||
* [\#2279](https://github.com/bnb-chain/bsc/pull/2279) BlobTx: implement EIP-4844 on BSC
|
||||
* [\#2337](https://github.com/bnb-chain/bsc/pull/2337) 4844: bugfix and improve
|
||||
* [\#2339](https://github.com/bnb-chain/bsc/pull/2339) fix: missing block asigment WithSidecars
|
||||
* [\#2350](https://github.com/bnb-chain/bsc/pull/2350) cancun: change empty withdrawHash value of header
|
||||
* [\#2335](https://github.com/bnb-chain/bsc/pull/2335) upgrade: update system contracts bytes code and hardfork time of Feynman upgrade
|
||||
* [\#2323](https://github.com/bnb-chain/bsc/pull/2323) feat: export GasCeil in mev_params
|
||||
* [\#2357](https://github.com/bnb-chain/bsc/pull/2357) feat: add bid fee ceil in mev_params
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2321](https://github.com/bnb-chain/bsc/pull/2321) test: use full syncmode to run rpc node
|
||||
* [\#2338](https://github.com/bnb-chain/bsc/pull/2338) cmd: include more node info in metrics
|
||||
* [\#2342](https://github.com/bnb-chain/bsc/pull/2342) p2p: add metrics for inbound/outbound peers
|
||||
* [\#2334](https://github.com/bnb-chain/bsc/pull/2334) core: improve chain rewinding mechanism
|
||||
* [\#2352](https://github.com/bnb-chain/bsc/pull/2352) core: fix block report when chain is not setHead
|
||||
|
||||
### BUGFIX
|
||||
NA
|
||||
|
||||
## v1.4.3
|
||||
### FEATURE
|
||||
* [\#2241](https://github.com/bnb-chain/bsc/pull/2241) cmd/utils, core/rawdb, triedb/pathdb: flip hash to path scheme
|
||||
* [\#2312](https://github.com/bnb-chain/bsc/pull/2312) cmd/utils, node: switch to Pebble as the default db if none exists
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2228](https://github.com/bnb-chain/bsc/pull/2228) core: rephrase TriesInMemory log
|
||||
* [\#2234](https://github.com/bnb-chain/bsc/pull/2234) cmd/utils: disable snap protocol for fast node
|
||||
* [\#2236](https://github.com/bnb-chain/bsc/pull/2236) build(deps): bump github.com/quic-go/quic-go from 0.39.3 to 0.39.4
|
||||
* [\#2240](https://github.com/bnb-chain/bsc/pull/2240) core/state: fix taskResult typo
|
||||
|
||||
* [\#2280](https://github.com/bnb-chain/bsc/pull/2280) cmd/utils, core: only full sync for fast nodes
|
||||
* [\#2298](https://github.com/bnb-chain/bsc/pull/2298) cmd, node: initialize ports with --instance
|
||||
* [\#2302](https://github.com/bnb-chain/bsc/pull/2302) cmd/geth, core/rawdb: add dbDeleteTrieState
|
||||
* [\#2304](https://github.com/bnb-chain/bsc/pull/2304) eth/ethconfig: remove overridekepler and overrideshanghai
|
||||
* [\#2307](https://github.com/bnb-chain/bsc/pull/2307) internal/ethapi: add net_nodeInfo
|
||||
* [\#2311](https://github.com/bnb-chain/bsc/pull/2311) Port cancun related changes from unreleased v1.14.0
|
||||
* [\#2313](https://github.com/bnb-chain/bsc/pull/2313) tests/truffle: use hbss to run test
|
||||
* [\#2314](https://github.com/bnb-chain/bsc/pull/2314) cmd/jsutil: dump MinGasPrice for validator
|
||||
* [\#2317](https://github.com/bnb-chain/bsc/pull/2317) feat: add mev metrics
|
||||
|
||||
### BUGFIX
|
||||
* [\#2272](https://github.com/bnb-chain/bsc/pull/2272) parlia: add state prepare for internal SC transaction
|
||||
* [\#2277](https://github.com/bnb-chain/bsc/pull/2277) fix: systemTx should be always at the end of block
|
||||
* [\#2299](https://github.com/bnb-chain/bsc/pull/2299) fix: add FeynmanFix upgrade for a testnet issue
|
||||
* [\#2310](https://github.com/bnb-chain/bsc/pull/2310) core/vm: fix PrecompiledContractsCancun
|
||||
|
||||
## v1.4.2
|
||||
### Feature
|
||||
### FEATURE
|
||||
* [\#2021](https://github.com/bnb-chain/bsc/pull/2021) feat: support separate trie database
|
||||
* [\#2224](https://github.com/bnb-chain/bsc/pull/2224) feat: support MEV
|
||||
|
||||
@@ -120,6 +233,10 @@ NA
|
||||
[event: fix Resubscribe deadlock when unsubscribing after inner sub ends (#28359)](https://github.com/bnb-chain/bsc/commit/ffc6a0f36edda396a8421cf7a3c0feb88be20d0b)
|
||||
[all: replace log15 with slog (#28187)](https://github.com/bnb-chain/bsc/commit/28e73717016cdc9ebdb5fdb3474cfbd3bd2d2524)
|
||||
|
||||
## v1.3.11
|
||||
BUGFIX
|
||||
* [\#2288](https://github.com/bnb-chain/bsc/pull/2288) fix: add FeynmanFix upgrade for a testnet issue
|
||||
|
||||
## v1.3.10
|
||||
FEATURE
|
||||
* [\#2047](https://github.com/bnb-chain/bsc/pull/2047) feat: add new fork block and precompile contract for BEP294 and BEP299
|
||||
|
||||
@@ -26,7 +26,7 @@ COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
|
||||
|
||||
EXPOSE 8545 8546 30303 30303/udp
|
||||
|
||||
# Add some metadata labels to help programatic image consumption
|
||||
# Add some metadata labels to help programmatic image consumption
|
||||
ARG COMMIT=""
|
||||
ARG VERSION=""
|
||||
ARG BUILDNUM=""
|
||||
|
||||
@@ -155,7 +155,7 @@ Note: If you encounter difficulties downloading the chaindata snapshot and prefe
|
||||
```shell
|
||||
./geth --config ./config.toml --datadir ./node --cache 8000 --rpc.allow-unprotected-txs --history.transactions 0
|
||||
|
||||
## It is recommand to run fullnode with `--tries-verify-mode none` if you want high performance and care little about state consistency
|
||||
## It is recommend to run fullnode with `--tries-verify-mode none` if you want high performance and care little about state consistency
|
||||
## It will run with Hash-Base Storage Scheme by default
|
||||
./geth --config ./config.toml --datadir ./node --cache 8000 --rpc.allow-unprotected-txs --history.transactions 0 --tries-verify-mode none
|
||||
|
||||
|
||||
2
accounts/external/backend.go
vendored
2
accounts/external/backend.go
vendored
@@ -163,7 +163,7 @@ func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, d
|
||||
hexutil.Encode(data)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If V is on 27/28-form, convert to to 0/1 for Clique and Parlia
|
||||
// If V is on 27/28-form, convert to 0/1 for Clique and Parlia
|
||||
if (mimeType == accounts.MimetypeClique || mimeType == accounts.MimetypeParlia) && (res[64] == 27 || res[64] == 28) {
|
||||
res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique and Parlia use
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// waitWatcherStarts waits up to 1s for the keystore watcher to start.
|
||||
// waitWatcherStart waits up to 1s for the keystore watcher to start.
|
||||
func waitWatcherStart(ks *KeyStore) bool {
|
||||
// On systems where file watch is not supported, just return "ok".
|
||||
if !ks.cache.watcher.enabled() {
|
||||
@@ -358,7 +358,6 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
||||
// Now replace file contents
|
||||
if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
wantAccounts = []accounts.Account{cachetestAccounts[1]}
|
||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||
@@ -374,7 +373,6 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
||||
// Now replace file contents again
|
||||
if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
wantAccounts = []accounts.Account{cachetestAccounts[2]}
|
||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||
@@ -390,7 +388,6 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
||||
// Now replace file contents with crap
|
||||
if err := os.WriteFile(file, []byte("foo"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
if err := waitForAccounts([]accounts.Account{}, ks); err != nil {
|
||||
t.Errorf("Emptying account file failed")
|
||||
|
||||
@@ -343,7 +343,7 @@ func TestWalletNotifications(t *testing.T) {
|
||||
checkEvents(t, wantEvents, events)
|
||||
}
|
||||
|
||||
// TestImportExport tests the import functionality of a keystore.
|
||||
// TestImportECDSA tests the import functionality of a keystore.
|
||||
func TestImportECDSA(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
@@ -362,7 +362,7 @@ func TestImportECDSA(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestImportECDSA tests the import and export functionality of a keystore.
|
||||
// TestImportExport tests the import and export functionality of a keystore.
|
||||
func TestImportExport(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
|
||||
@@ -263,7 +263,7 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash,
|
||||
|
||||
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
||||
// fields from the given block. It assumes the given block is post-merge block.
|
||||
func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar) *ExecutionPayloadEnvelope {
|
||||
func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars types.BlobSidecars) *ExecutionPayloadEnvelope {
|
||||
data := &ExecutableData{
|
||||
BlockHash: block.Hash(),
|
||||
ParentHash: block.ParentHash(),
|
||||
|
||||
@@ -169,7 +169,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
// Calculate the BlobBaseFee
|
||||
var excessBlobGas uint64
|
||||
if pre.Env.ExcessBlobGas != nil {
|
||||
excessBlobGas := *pre.Env.ExcessBlobGas
|
||||
excessBlobGas = *pre.Env.ExcessBlobGas
|
||||
vmContext.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas)
|
||||
} else {
|
||||
// If it is not explicitly defined, but we have the parent values, we try
|
||||
|
||||
@@ -525,7 +525,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
f.lock.Unlock()
|
||||
|
||||
// Send an error if too frequent funding, othewise a success
|
||||
// Send an error if too frequent funding, otherwise a success
|
||||
if !fund {
|
||||
if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(timeout)))); err != nil { // nolint: gosimple
|
||||
log.Warn("Failed to send funding error to client", "err", err)
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestAttachWithHeaders(t *testing.T) {
|
||||
// This is fixed in a follow-up PR.
|
||||
}
|
||||
|
||||
// TestAttachWithHeaders tests that 'geth db --remotedb' with custom headers works, i.e
|
||||
// TestRemoteDbWithHeaders tests that 'geth db --remotedb' with custom headers works, i.e
|
||||
// that custom headers are forwarded to the target.
|
||||
func TestRemoteDbWithHeaders(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -12,16 +12,16 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v4/io/prompt"
|
||||
validatorpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/validator-client"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/accounts/iface"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/accounts/petnames"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/keymanager/local"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v5/io/prompt"
|
||||
validatorpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/validator-client"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts/iface"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts/petnames"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/keymanager/local"
|
||||
"github.com/urfave/cli/v2"
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/internal/era"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
@@ -62,6 +63,7 @@ var (
|
||||
Flags: flags.Merge([]cli.Flag{
|
||||
utils.CachePreimagesFlag,
|
||||
utils.OverrideCancun,
|
||||
utils.OverrideHaber,
|
||||
utils.OverrideVerkle,
|
||||
}, utils.DatabaseFlags),
|
||||
Description: `
|
||||
@@ -255,6 +257,10 @@ func initGenesis(ctx *cli.Context) error {
|
||||
v := ctx.Uint64(utils.OverrideCancun.Name)
|
||||
overrides.OverrideCancun = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideHaber.Name) {
|
||||
v := ctx.Uint64(utils.OverrideHaber.Name)
|
||||
overrides.OverrideHaber = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||
overrides.OverrideVerkle = &v
|
||||
@@ -267,15 +273,21 @@ func initGenesis(ctx *cli.Context) error {
|
||||
defer chaindb.Close()
|
||||
|
||||
// if the trie data dir has been set, new trie db with a new state database
|
||||
if ctx.IsSet(utils.SeparateDBFlag.Name) {
|
||||
if ctx.IsSet(utils.MultiDataBaseFlag.Name) {
|
||||
statediskdb, dbErr := stack.OpenDatabaseWithFreezer(name+"/state", 0, 0, "", "", false, false, false, false)
|
||||
if dbErr != nil {
|
||||
utils.Fatalf("Failed to open separate trie database: %v", dbErr)
|
||||
}
|
||||
chaindb.SetStateStore(statediskdb)
|
||||
blockdb, err := stack.OpenDatabaseWithFreezer(name+"/block", 0, 0, "", "", false, false, false, false)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to open separate block database: %v", err)
|
||||
}
|
||||
chaindb.SetBlockStore(blockdb)
|
||||
log.Warn("Multi-database is an experimental feature")
|
||||
}
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||
defer triedb.Close()
|
||||
|
||||
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||
@@ -473,6 +485,13 @@ func dumpGenesis(ctx *cli.Context) error {
|
||||
}
|
||||
continue
|
||||
}
|
||||
// set the separate state & block database
|
||||
if stack.CheckIfMultiDataBase() && err == nil {
|
||||
stateDiskDb := utils.MakeStateDataBase(ctx, stack, true, false)
|
||||
db.SetStateStore(stateDiskDb)
|
||||
blockDb := utils.MakeBlockDatabase(ctx, stack, true, false)
|
||||
db.SetBlockStore(blockDb)
|
||||
}
|
||||
genesis, err := core.ReadGenesis(db)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to read genesis: %s", err)
|
||||
@@ -500,10 +519,16 @@ func importChain(ctx *cli.Context) error {
|
||||
// Start system runtime metrics collection
|
||||
go metrics.CollectProcessMetrics(3 * time.Second)
|
||||
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
stack, cfg := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
chain, db := utils.MakeChain(ctx, stack, false)
|
||||
backend, err := eth.New(stack, &cfg.Eth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chain := backend.BlockChain()
|
||||
db := backend.ChainDb()
|
||||
defer db.Close()
|
||||
|
||||
// Start periodically gathering memory profiles
|
||||
@@ -739,7 +764,7 @@ func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, eth
|
||||
arg := ctx.Args().First()
|
||||
if hashish(arg) {
|
||||
hash := common.HexToHash(arg)
|
||||
if number := rawdb.ReadHeaderNumber(db, hash); number != nil {
|
||||
if number := rawdb.ReadHeaderNumber(db.BlockStore(), hash); number != nil {
|
||||
header = rawdb.ReadHeader(db, hash, *number)
|
||||
} else {
|
||||
return nil, nil, common.Hash{}, fmt.Errorf("block %x not found", hash)
|
||||
@@ -758,7 +783,7 @@ func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, eth
|
||||
} else {
|
||||
// Use latest
|
||||
if scheme == rawdb.PathScheme {
|
||||
triedb := triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.ReadOnly})
|
||||
triedb := triedb.NewDatabase(db, &triedb.Config{PathDB: utils.PathDBConfigAddJournalFilePath(stack, pathdb.ReadOnly)})
|
||||
defer triedb.Close()
|
||||
if stateRoot := triedb.Head(); stateRoot != (common.Hash{}) {
|
||||
header.Root = stateRoot
|
||||
@@ -808,7 +833,8 @@ func dump(ctx *cli.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
triedb := utils.MakeTrieDatabase(ctx, db, true, true, false) // always enable preimage lookup
|
||||
defer db.Close()
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, db, true, true, false) // always enable preimage lookup
|
||||
defer triedb.Close()
|
||||
|
||||
state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
||||
@@ -828,7 +854,7 @@ func dumpAllRootHashInPath(ctx *cli.Context) error {
|
||||
defer stack.Close()
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer db.Close()
|
||||
triedb := triedb.NewDatabase(db, &triedb.Config{PathDB: pathdb.ReadOnly})
|
||||
triedb := triedb.NewDatabase(db, &triedb.Config{PathDB: utils.PathDBConfigAddJournalFilePath(stack, pathdb.ReadOnly)})
|
||||
defer triedb.Close()
|
||||
|
||||
scheme, err := rawdb.ParseStateScheme(ctx.String(utils.StateSchemeFlag.Name), db)
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/external"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
@@ -183,29 +185,32 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
||||
params.RialtoGenesisHash = common.HexToHash(v)
|
||||
}
|
||||
|
||||
if ctx.IsSet(utils.OverrideShanghai.Name) {
|
||||
v := ctx.Uint64(utils.OverrideShanghai.Name)
|
||||
cfg.Eth.OverrideShanghai = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideKepler.Name) {
|
||||
v := ctx.Uint64(utils.OverrideKepler.Name)
|
||||
cfg.Eth.OverrideKepler = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideCancun.Name) {
|
||||
v := ctx.Uint64(utils.OverrideCancun.Name)
|
||||
cfg.Eth.OverrideCancun = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideHaber.Name) {
|
||||
v := ctx.Uint64(utils.OverrideHaber.Name)
|
||||
cfg.Eth.OverrideHaber = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||
cfg.Eth.OverrideVerkle = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideFeynman.Name) {
|
||||
v := ctx.Uint64(utils.OverrideFeynman.Name)
|
||||
cfg.Eth.OverrideFeynman = &v
|
||||
if ctx.IsSet(utils.OverrideFullImmutabilityThreshold.Name) {
|
||||
params.FullImmutabilityThreshold = ctx.Uint64(utils.OverrideFullImmutabilityThreshold.Name)
|
||||
downloader.FullMaxForkAncestry = ctx.Uint64(utils.OverrideFullImmutabilityThreshold.Name)
|
||||
}
|
||||
if ctx.IsSet(utils.SeparateDBFlag.Name) && !stack.IsSeparatedDB() {
|
||||
utils.Fatalf("Failed to locate separate database subdirectory when separatedb parameter has been set")
|
||||
if ctx.IsSet(utils.OverrideMinBlocksForBlobRequests.Name) {
|
||||
params.MinBlocksForBlobRequests = ctx.Uint64(utils.OverrideMinBlocksForBlobRequests.Name)
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideDefaultExtraReserveForBlobRequests.Name) {
|
||||
params.DefaultExtraReserveForBlobRequests = ctx.Uint64(utils.OverrideDefaultExtraReserveForBlobRequests.Name)
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideBreatheBlockInterval.Name) {
|
||||
params.BreatheBlockInterval = ctx.Uint64(utils.OverrideBreatheBlockInterval.Name)
|
||||
}
|
||||
|
||||
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
|
||||
|
||||
// Create gauge with geth system and build information
|
||||
@@ -237,8 +242,8 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
||||
git, _ := version.VCS()
|
||||
utils.SetupMetrics(ctx,
|
||||
utils.EnableBuildInfo(git.Commit, git.Date),
|
||||
utils.EnableMinerInfo(ctx, cfg.Eth.Miner),
|
||||
utils.EnableNodeInfo(cfg.Eth.TxPool),
|
||||
utils.EnableMinerInfo(ctx, &cfg.Eth.Miner),
|
||||
utils.EnableNodeInfo(&cfg.Eth.TxPool, stack.Server().NodeInfo()),
|
||||
)
|
||||
return stack, backend
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ Remove blockchain and state databases`,
|
||||
dbCompactCmd,
|
||||
dbGetCmd,
|
||||
dbDeleteCmd,
|
||||
dbDeleteTrieStateCmd,
|
||||
dbInspectTrieCmd,
|
||||
dbPutCmd,
|
||||
dbGetSlotsCmd,
|
||||
@@ -105,12 +106,12 @@ Remove blockchain and state databases`,
|
||||
dbInspectTrieCmd = &cli.Command{
|
||||
Action: inspectTrie,
|
||||
Name: "inspect-trie",
|
||||
ArgsUsage: "<blocknum> <jobnum>",
|
||||
ArgsUsage: "<blocknum> <jobnum> <topn>",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.SyncModeFlag,
|
||||
},
|
||||
Usage: "Inspect the MPT tree of the account and contract.",
|
||||
Usage: "Inspect the MPT tree of the account and contract. 'blocknum' can be latest/snapshot/number. 'topn' means output the top N storage tries info ranked by the total number of TrieNodes",
|
||||
Description: `This commands iterates the entrie WorldState.`,
|
||||
}
|
||||
dbCheckStateContentCmd = &cli.Command{
|
||||
@@ -206,6 +207,15 @@ corruption if it is aborted during execution'!`,
|
||||
Description: `This command deletes the specified database key from the database.
|
||||
WARNING: This is a low-level operation which may cause database corruption!`,
|
||||
}
|
||||
dbDeleteTrieStateCmd = &cli.Command{
|
||||
Action: dbDeleteTrieState,
|
||||
Name: "delete-trie-state",
|
||||
Usage: "Delete all trie state key-value pairs from the database and the ancient state. Does not support hash-based state scheme.",
|
||||
Flags: flags.Merge([]cli.Flag{
|
||||
utils.SyncModeFlag,
|
||||
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||
Description: `This command deletes all trie state key-value pairs from the database and the ancient state.`,
|
||||
}
|
||||
dbPutCmd = &cli.Command{
|
||||
Action: dbPut,
|
||||
Name: "put",
|
||||
@@ -376,6 +386,7 @@ func inspectTrie(ctx *cli.Context) error {
|
||||
blockNumber uint64
|
||||
trieRootHash common.Hash
|
||||
jobnum uint64
|
||||
topN uint64
|
||||
)
|
||||
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
@@ -386,8 +397,8 @@ func inspectTrie(ctx *cli.Context) error {
|
||||
var headerBlockHash common.Hash
|
||||
if ctx.NArg() >= 1 {
|
||||
if ctx.Args().Get(0) == "latest" {
|
||||
headerHash := rawdb.ReadHeadHeaderHash(db)
|
||||
blockNumber = *(rawdb.ReadHeaderNumber(db, headerHash))
|
||||
headerHash := rawdb.ReadHeadHeaderHash(db.BlockStore())
|
||||
blockNumber = *(rawdb.ReadHeaderNumber(db.BlockStore(), headerHash))
|
||||
} else if ctx.Args().Get(0) == "snapshot" {
|
||||
trieRootHash = rawdb.ReadSnapshotRoot(db)
|
||||
blockNumber = math.MaxUint64
|
||||
@@ -401,12 +412,25 @@ func inspectTrie(ctx *cli.Context) error {
|
||||
|
||||
if ctx.NArg() == 1 {
|
||||
jobnum = 1000
|
||||
topN = 10
|
||||
} else if ctx.NArg() == 2 {
|
||||
var err error
|
||||
jobnum, err = strconv.ParseUint(ctx.Args().Get(1), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to Parse jobnum, Args[1]: %v, err: %v", ctx.Args().Get(1), err)
|
||||
}
|
||||
topN = 10
|
||||
} else {
|
||||
var err error
|
||||
jobnum, err = strconv.ParseUint(ctx.Args().Get(1), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to Parse jobnum, Args[1]: %v, err: %v", ctx.Args().Get(1), err)
|
||||
}
|
||||
|
||||
topN, err = strconv.ParseUint(ctx.Args().Get(2), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to Parse topn, Args[1]: %v, err: %v", ctx.Args().Get(1), err)
|
||||
}
|
||||
}
|
||||
|
||||
if blockNumber != math.MaxUint64 {
|
||||
@@ -426,7 +450,8 @@ func inspectTrie(ctx *cli.Context) error {
|
||||
var config *triedb.Config
|
||||
if dbScheme == rawdb.PathScheme {
|
||||
config = &triedb.Config{
|
||||
PathDB: pathdb.ReadOnly,
|
||||
PathDB: utils.PathDBConfigAddJournalFilePath(stack, pathdb.ReadOnly),
|
||||
Cache: 0,
|
||||
}
|
||||
} else if dbScheme == rawdb.HashScheme {
|
||||
config = triedb.HashDefaults
|
||||
@@ -438,7 +463,7 @@ func inspectTrie(ctx *cli.Context) error {
|
||||
fmt.Printf("fail to new trie tree, err: %v, rootHash: %v\n", err, trieRootHash.String())
|
||||
return err
|
||||
}
|
||||
theInspect, err := trie.NewInspector(theTrie, triedb, trieRootHash, blockNumber, jobnum)
|
||||
theInspect, err := trie.NewInspector(theTrie, triedb, trieRootHash, blockNumber, jobnum, int(topN))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -509,7 +534,7 @@ func checkStateContent(ctx *cli.Context) error {
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer db.Close()
|
||||
var (
|
||||
it = rawdb.NewKeyLengthIterator(db.NewIterator(prefix, start), 32)
|
||||
it ethdb.Iterator
|
||||
hasher = crypto.NewKeccakState()
|
||||
got = make([]byte, 32)
|
||||
errs int
|
||||
@@ -517,6 +542,11 @@ func checkStateContent(ctx *cli.Context) error {
|
||||
startTime = time.Now()
|
||||
lastLog = time.Now()
|
||||
)
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
it = rawdb.NewKeyLengthIterator(db.StateStore().NewIterator(prefix, start), 32)
|
||||
} else {
|
||||
it = rawdb.NewKeyLengthIterator(db.NewIterator(prefix, start), 32)
|
||||
}
|
||||
for it.Next() {
|
||||
count++
|
||||
k := it.Key()
|
||||
@@ -563,9 +593,11 @@ func dbStats(ctx *cli.Context) error {
|
||||
defer db.Close()
|
||||
|
||||
showLeveldbStats(db)
|
||||
if db.StateStore() != nil {
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
fmt.Println("show stats of state store")
|
||||
showLeveldbStats(db.StateStore())
|
||||
fmt.Println("show stats of block store")
|
||||
showLeveldbStats(db.BlockStore())
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -581,10 +613,11 @@ func dbCompact(ctx *cli.Context) error {
|
||||
log.Info("Stats before compaction")
|
||||
showLeveldbStats(db)
|
||||
|
||||
statediskdb := db.StateStore()
|
||||
if statediskdb != nil {
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
fmt.Println("show stats of state store")
|
||||
showLeveldbStats(statediskdb)
|
||||
showLeveldbStats(db.StateStore())
|
||||
fmt.Println("show stats of block store")
|
||||
showLeveldbStats(db.BlockStore())
|
||||
}
|
||||
|
||||
log.Info("Triggering compaction")
|
||||
@@ -593,8 +626,12 @@ func dbCompact(ctx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if statediskdb != nil {
|
||||
if err := statediskdb.Compact(nil, nil); err != nil {
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
if err := db.StateStore().Compact(nil, nil); err != nil {
|
||||
log.Error("Compact err", "error", err)
|
||||
return err
|
||||
}
|
||||
if err := db.BlockStore().Compact(nil, nil); err != nil {
|
||||
log.Error("Compact err", "error", err)
|
||||
return err
|
||||
}
|
||||
@@ -602,9 +639,11 @@ func dbCompact(ctx *cli.Context) error {
|
||||
|
||||
log.Info("Stats after compaction")
|
||||
showLeveldbStats(db)
|
||||
if statediskdb != nil {
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
fmt.Println("show stats of state store after compaction")
|
||||
showLeveldbStats(statediskdb)
|
||||
showLeveldbStats(db.StateStore())
|
||||
fmt.Println("show stats of block store after compaction")
|
||||
showLeveldbStats(db.BlockStore())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -625,18 +664,18 @@ func dbGet(ctx *cli.Context) error {
|
||||
log.Info("Could not decode the key", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
statediskdb := db.StateStore()
|
||||
data, err := db.Get(key)
|
||||
if err != nil {
|
||||
// if separate trie db exist, try to get it from separate db
|
||||
if statediskdb != nil {
|
||||
statedata, dberr := statediskdb.Get(key)
|
||||
if dberr == nil {
|
||||
fmt.Printf("key %#x: %#x\n", key, statedata)
|
||||
return nil
|
||||
}
|
||||
opDb := db
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
keyType := rawdb.DataTypeByKey(key)
|
||||
if keyType == rawdb.StateDataType {
|
||||
opDb = db.StateStore()
|
||||
} else if keyType == rawdb.BlockDataType {
|
||||
opDb = db.BlockStore()
|
||||
}
|
||||
}
|
||||
|
||||
data, err := opDb.Get(key)
|
||||
if err != nil {
|
||||
log.Info("Get operation failed", "key", fmt.Sprintf("%#x", key), "error", err)
|
||||
return err
|
||||
}
|
||||
@@ -799,17 +838,103 @@ func dbDelete(ctx *cli.Context) error {
|
||||
log.Info("Could not decode the key", "error", err)
|
||||
return err
|
||||
}
|
||||
data, err := db.Get(key)
|
||||
opDb := db
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
keyType := rawdb.DataTypeByKey(key)
|
||||
if keyType == rawdb.StateDataType {
|
||||
opDb = db.StateStore()
|
||||
} else if keyType == rawdb.BlockDataType {
|
||||
opDb = db.BlockStore()
|
||||
}
|
||||
}
|
||||
|
||||
data, err := opDb.Get(key)
|
||||
if err == nil {
|
||||
fmt.Printf("Previous value: %#x\n", data)
|
||||
}
|
||||
if err = db.Delete(key); err != nil {
|
||||
if err = opDb.Delete(key); err != nil {
|
||||
log.Info("Delete operation returned an error", "key", fmt.Sprintf("%#x", key), "error", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// dbDeleteTrieState deletes all trie state related key-value pairs from the database and the ancient state store.
|
||||
func dbDeleteTrieState(ctx *cli.Context) error {
|
||||
if ctx.NArg() > 0 {
|
||||
return fmt.Errorf("no arguments required")
|
||||
}
|
||||
|
||||
stack, config := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
db := utils.MakeChainDatabase(ctx, stack, false, false)
|
||||
defer db.Close()
|
||||
|
||||
var (
|
||||
err error
|
||||
start = time.Now()
|
||||
)
|
||||
|
||||
// If separate trie db exists, delete all files in the db folder
|
||||
if db.StateStore() != nil {
|
||||
statePath := filepath.Join(stack.ResolvePath("chaindata"), "state")
|
||||
log.Info("Removing separate trie database", "path", statePath)
|
||||
err = filepath.Walk(statePath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if path != statePath {
|
||||
fileInfo, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !fileInfo.IsDir() {
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
log.Info("Separate trie database deleted", "err", err, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete KV pairs from the database
|
||||
err = rawdb.DeleteTrieState(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove the full node ancient database
|
||||
dbPath := config.Eth.DatabaseFreezer
|
||||
switch {
|
||||
case dbPath == "":
|
||||
dbPath = filepath.Join(stack.ResolvePath("chaindata"), "ancient/state")
|
||||
case !filepath.IsAbs(dbPath):
|
||||
dbPath = config.Node.ResolvePath(dbPath)
|
||||
}
|
||||
|
||||
if !common.FileExist(dbPath) {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Info("Removing ancient state database", "path", dbPath)
|
||||
start = time.Now()
|
||||
filepath.Walk(dbPath, func(path string, info os.FileInfo, err error) error {
|
||||
if dbPath == path {
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() {
|
||||
os.Remove(path)
|
||||
return nil
|
||||
}
|
||||
return filepath.SkipDir
|
||||
})
|
||||
log.Info("State database successfully deleted", "path", dbPath, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// dbPut overwrite a value in the database
|
||||
func dbPut(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 2 {
|
||||
@@ -837,11 +962,22 @@ func dbPut(ctx *cli.Context) error {
|
||||
log.Info("Could not decode the value", "error", err)
|
||||
return err
|
||||
}
|
||||
data, err = db.Get(key)
|
||||
|
||||
opDb := db
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
keyType := rawdb.DataTypeByKey(key)
|
||||
if keyType == rawdb.StateDataType {
|
||||
opDb = db.StateStore()
|
||||
} else if keyType == rawdb.BlockDataType {
|
||||
opDb = db.BlockStore()
|
||||
}
|
||||
}
|
||||
|
||||
data, err = opDb.Get(key)
|
||||
if err == nil {
|
||||
fmt.Printf("Previous value: %#x\n", data)
|
||||
}
|
||||
return db.Put(key, value)
|
||||
return opDb.Put(key, value)
|
||||
}
|
||||
|
||||
// dbDumpTrie shows the key-value slots of a given storage trie
|
||||
@@ -854,8 +990,7 @@ func dbDumpTrie(ctx *cli.Context) error {
|
||||
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer db.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
var (
|
||||
@@ -933,7 +1068,7 @@ func freezerInspect(ctx *cli.Context) error {
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
ancient := stack.ResolveAncient("chaindata", ctx.String(utils.AncientFlag.Name))
|
||||
stack.Close()
|
||||
return rawdb.InspectFreezerTable(ancient, freezer, table, start, end)
|
||||
return rawdb.InspectFreezerTable(ancient, freezer, table, start, end, stack.CheckIfMultiDataBase())
|
||||
}
|
||||
|
||||
func importLDBdata(ctx *cli.Context) error {
|
||||
@@ -1073,11 +1208,11 @@ func showMetaData(ctx *cli.Context) error {
|
||||
defer stack.Close()
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer db.Close()
|
||||
ancients, err := db.Ancients()
|
||||
ancients, err := db.BlockStore().Ancients()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error accessing ancients: %v", err)
|
||||
}
|
||||
data := rawdb.ReadChainMetadata(db)
|
||||
data := rawdb.ReadChainMetadataFromMultiDatabase(db)
|
||||
data = append(data, []string{"frozen", fmt.Sprintf("%d items", ancients)})
|
||||
data = append(data, []string{"snapshotGenerator", snapshot.ParseGeneratorStatus(rawdb.ReadSnapshotGenerator(db))})
|
||||
if b := rawdb.ReadHeadBlock(db); b != nil {
|
||||
@@ -1138,8 +1273,8 @@ func hbss2pbss(ctx *cli.Context) error {
|
||||
log.Info("hbss2pbss triedb", "scheme", triedb.Scheme())
|
||||
defer triedb.Close()
|
||||
|
||||
headerHash := rawdb.ReadHeadHeaderHash(db)
|
||||
blockNumber := rawdb.ReadHeaderNumber(db, headerHash)
|
||||
headerHash := rawdb.ReadHeadHeaderHash(db.BlockStore())
|
||||
blockNumber := rawdb.ReadHeaderNumber(db.BlockStore(), headerHash)
|
||||
if blockNumber == nil {
|
||||
log.Error("read header number failed.")
|
||||
return fmt.Errorf("read header number failed")
|
||||
|
||||
@@ -151,8 +151,8 @@ func TestCustomBackend(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
for i, tt := range []backendTest{
|
||||
{ // When not specified, it should default to leveldb
|
||||
execArgs: []string{"--db.engine", "leveldb"},
|
||||
{ // When not specified, it should default to pebble
|
||||
execArgs: []string{"--db.engine", "pebble"},
|
||||
execExpect: "0x0000000000001338",
|
||||
},
|
||||
{ // Explicit leveldb
|
||||
|
||||
@@ -25,6 +25,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
@@ -70,11 +72,13 @@ var (
|
||||
utils.USBFlag,
|
||||
utils.SmartCardDaemonPathFlag,
|
||||
utils.RialtoHash,
|
||||
utils.OverrideShanghai,
|
||||
utils.OverrideKepler,
|
||||
utils.OverrideCancun,
|
||||
utils.OverrideHaber,
|
||||
utils.OverrideVerkle,
|
||||
utils.OverrideFeynman,
|
||||
utils.OverrideFullImmutabilityThreshold,
|
||||
utils.OverrideMinBlocksForBlobRequests,
|
||||
utils.OverrideDefaultExtraReserveForBlobRequests,
|
||||
utils.OverrideBreatheBlockInterval,
|
||||
utils.EnablePersonal,
|
||||
utils.TxPoolLocalsFlag,
|
||||
utils.TxPoolNoLocalsFlag,
|
||||
@@ -101,6 +105,7 @@ var (
|
||||
utils.TransactionHistoryFlag,
|
||||
utils.StateHistoryFlag,
|
||||
utils.PathDBSyncFlag,
|
||||
utils.JournalFileFlag,
|
||||
utils.LightServeFlag, // deprecated
|
||||
utils.LightIngressFlag, // deprecated
|
||||
utils.LightEgressFlag, // deprecated
|
||||
@@ -142,8 +147,10 @@ var (
|
||||
// utils.MinerNewPayloadTimeout,
|
||||
utils.NATFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.PeerFilterPatternsFlag,
|
||||
utils.DiscoveryV4Flag,
|
||||
utils.DiscoveryV5Flag,
|
||||
utils.InstanceFlag,
|
||||
utils.LegacyDiscoveryV5Flag, // deprecated
|
||||
utils.NetrestrictFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
@@ -172,6 +179,7 @@ var (
|
||||
utils.VoteJournalDirFlag,
|
||||
utils.LogDebugFlag,
|
||||
utils.LogBacktraceAtFlag,
|
||||
utils.BlobExtraReserveFlag,
|
||||
}, utils.NetworkFlags, utils.DatabaseFlags)
|
||||
|
||||
rpcFlags = []cli.Flag{
|
||||
@@ -450,6 +458,10 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isCon
|
||||
// Set the gas price to the limits from the CLI and start mining
|
||||
gasprice := flags.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
|
||||
ethBackend.TxPool().SetGasTip(gasprice)
|
||||
gasCeil := ethBackend.Miner().GasCeil()
|
||||
if gasCeil > params.SystemTxsGas {
|
||||
ethBackend.TxPool().SetMaxGas(gasCeil - params.SystemTxsGas)
|
||||
}
|
||||
if err := ethBackend.StartMining(); err != nil {
|
||||
utils.Fatalf("Failed to start mining: %v", err)
|
||||
}
|
||||
|
||||
@@ -178,10 +178,11 @@ func BlockchainCreator(t *testing.T, chaindbPath, AncientPath string, blockRemai
|
||||
|
||||
// Force run a freeze cycle
|
||||
type freezer interface {
|
||||
Freeze(threshold uint64) error
|
||||
Freeze() error
|
||||
Ancients() (uint64, error)
|
||||
}
|
||||
db.(freezer).Freeze(10)
|
||||
blockchain.SetFinalized(blocks[len(blocks)-1].Header())
|
||||
db.(freezer).Freeze()
|
||||
|
||||
frozen, err := db.Ancients()
|
||||
//make sure there're frozen items
|
||||
|
||||
@@ -92,7 +92,7 @@ WARNING: it's only supported in hash mode(--state.scheme=hash)".
|
||||
geth offline prune-block for block data in ancientdb.
|
||||
The amount of blocks expected for remaining after prune can be specified via block-amount-reserved in this command,
|
||||
will prune and only remain the specified amount of old block data in ancientdb.
|
||||
the brief workflow is to backup the the number of this specified amount blocks backward in original ancientdb
|
||||
the brief workflow is to backup the number of this specified amount blocks backward in original ancientdb
|
||||
into new ancient_backup, then delete the original ancientdb dir and rename the ancient_backup to original one for replacement,
|
||||
finally assemble the statedb and new ancientDb together.
|
||||
The purpose of doing it is because the block data will be moved into the ancient store when it
|
||||
@@ -355,7 +355,15 @@ func pruneBlock(ctx *cli.Context) error {
|
||||
if !ctx.IsSet(utils.AncientFlag.Name) {
|
||||
return errors.New("datadir.ancient must be set")
|
||||
} else {
|
||||
oldAncientPath = ctx.String(utils.AncientFlag.Name)
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
ancientPath := ctx.String(utils.AncientFlag.Name)
|
||||
index := strings.LastIndex(ancientPath, "/ancient/chain")
|
||||
if index != -1 {
|
||||
oldAncientPath = ancientPath[:index] + "/block/ancient/chain"
|
||||
}
|
||||
} else {
|
||||
oldAncientPath = ctx.String(utils.AncientFlag.Name)
|
||||
}
|
||||
if !filepath.IsAbs(oldAncientPath) {
|
||||
// force absolute paths, which often fail due to the splicing of relative paths
|
||||
return errors.New("datadir.ancient not abs path")
|
||||
@@ -401,7 +409,7 @@ func pruneBlock(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
if _, err := os.Stat(newAncientPath); err == nil {
|
||||
// No file lock found for old ancientDB but new ancientDB exsisted, indicating the geth was interrupted
|
||||
// No file lock found for old ancientDB but new ancientDB existed, indicating the geth was interrupted
|
||||
// after old ancientDB removal, this happened after backup successfully, so just rename the new ancientDB
|
||||
if err := blockpruner.AncientDbReplacer(); err != nil {
|
||||
log.Error("Failed to rename new ancient directory")
|
||||
@@ -524,7 +532,7 @@ func verifyState(ctx *cli.Context) error {
|
||||
log.Error("Failed to load head block")
|
||||
return errors.New("no head block")
|
||||
}
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
snapConfig := snapshot.Config{
|
||||
@@ -579,7 +587,7 @@ func traverseState(ctx *cli.Context) error {
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||
@@ -688,7 +696,7 @@ func traverseRawState(ctx *cli.Context) error {
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||
@@ -852,7 +860,8 @@ func dumpState(ctx *cli.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
|
||||
defer db.Close()
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
snapConfig := snapshot.Config{
|
||||
@@ -935,7 +944,7 @@ func snapshotExportPreimages(ctx *cli.Context) error {
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
var root common.Hash
|
||||
|
||||
@@ -11,6 +11,7 @@ Install node.js dependency:
|
||||
npm install
|
||||
```
|
||||
## Run
|
||||
### 1.Get Validator's Information: Version, MinGasPrice
|
||||
mainnet validators version
|
||||
```bash
|
||||
npm run startMainnet
|
||||
@@ -19,7 +20,8 @@ testnet validators version
|
||||
```bash
|
||||
npm run startTestnet
|
||||
```
|
||||
Transaction count
|
||||
|
||||
### 2.Get Transaction Count
|
||||
```bash
|
||||
node gettxcount.js --rpc ${url} --startNum ${start} --endNum ${end} --miner ${miner} (optional)
|
||||
```
|
||||
51
cmd/jsutils/check_blobtx.js
Normal file
51
cmd/jsutils/check_blobtx.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import { ethers } from "ethers";
|
||||
import program from "commander";
|
||||
|
||||
// depends on ethjs v6.11.0+ for 4844, https://github.com/ethers-io/ethers.js/releases/tag/v6.11.0
|
||||
// BSC testnet enabled 4844 on block: 39539137
|
||||
// Usage:
|
||||
// nvm use 20
|
||||
// node check_blobtx.js --rpc https://data-seed-prebsc-1-s1.binance.org:8545 --startNum 39539137
|
||||
// node check_blobtx.js --rpc https://data-seed-prebsc-1-s1.binance.org:8545 --startNum 39539137 --endNum 40345994
|
||||
program.option("--rpc <Rpc>", "Rpc Server URL");
|
||||
program.option("--startNum <Num>", "start block", 0);
|
||||
program.option("--endNum <Num>", "end block", 0);
|
||||
program.parse(process.argv);
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(program.rpc);
|
||||
const main = async () => {
|
||||
var startBlock = parseInt(program.startNum)
|
||||
var endBlock = parseInt(program.endNum)
|
||||
if (isNaN(endBlock) || isNaN(startBlock) || startBlock == 0) {
|
||||
console.error("invalid input, --startNum", program.startNum, "--end", program.endNum)
|
||||
return
|
||||
}
|
||||
// if --endNum is not specified, set it to the latest block number.
|
||||
if (endBlock == 0) {
|
||||
endBlock = await provider.getBlockNumber();
|
||||
}
|
||||
if (startBlock > endBlock) {
|
||||
console.error("invalid input, startBlock:",startBlock, " endBlock:", endBlock);
|
||||
return
|
||||
}
|
||||
|
||||
for (let i = startBlock; i <= endBlock; i++) {
|
||||
let blockData = await provider.getBlock(i);
|
||||
console.log("startBlock:",startBlock, "endBlock:", endBlock, "curBlock", i, "blobGasUsed", blockData.blobGasUsed);
|
||||
if (blockData.blobGasUsed == 0) {
|
||||
continue
|
||||
}
|
||||
for (let txIndex = 0; txIndex<= blockData.transactions.length - 1; txIndex++) {
|
||||
let txHash = blockData.transactions[txIndex]
|
||||
let txData = await provider.getTransaction(txHash);
|
||||
if (txData.type == 3) {
|
||||
console.log("BlobTx in block:",i, " txIndex:", txIndex, " txHash:", txHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
main().then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
49
cmd/jsutils/faucet_request.js
Normal file
49
cmd/jsutils/faucet_request.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import { ethers } from "ethers";
|
||||
import program from "commander";
|
||||
|
||||
// Usage:
|
||||
// node faucet_request.js --rpc localhost:8545 --startNum 39539137
|
||||
// node faucet_request.js --rpc localhost:8545 --startNum 39539137 --endNum 40345994
|
||||
|
||||
// node faucet_request.js --rpc https://data-seed-prebsc-1-s1.bnbchain.org:8545 --startNum 39539137 --endNum 40345994
|
||||
program.option("--rpc <Rpc>", "Rpc Server URL");
|
||||
program.option("--startNum <Num>", "start block", 0);
|
||||
program.option("--endNum <Num>", "end block", 0);
|
||||
program.parse(process.argv);
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(program.rpc);
|
||||
const main = async () => {
|
||||
var startBlock = parseInt(program.startNum)
|
||||
var endBlock = parseInt(program.endNum)
|
||||
if (isNaN(endBlock) || isNaN(startBlock) || startBlock == 0) {
|
||||
console.error("invalid input, --startNum", program.startNum, "--end", program.endNum)
|
||||
return
|
||||
}
|
||||
// if --endNum is not specified, set it to the latest block number.
|
||||
if (endBlock == 0) {
|
||||
endBlock = await provider.getBlockNumber();
|
||||
}
|
||||
if (startBlock > endBlock) {
|
||||
console.error("invalid input, startBlock:",startBlock, " endBlock:", endBlock);
|
||||
return
|
||||
}
|
||||
|
||||
let startBalance = await provider.getBalance("0xaa25Aa7a19f9c426E07dee59b12f944f4d9f1DD3", startBlock)
|
||||
let endBalance = await provider.getBalance("0xaa25Aa7a19f9c426E07dee59b12f944f4d9f1DD3", endBlock)
|
||||
const faucetAmount = BigInt(0.3 * 10**18); // Convert 0.3 ether to wei as a BigInt
|
||||
const numFaucetRequest = (startBalance - endBalance) / faucetAmount;
|
||||
|
||||
// Convert BigInt to ether
|
||||
const startBalanceEth = Number(startBalance) / 10**18;
|
||||
const endBalanceEth = Number(endBalance) / 10**18;
|
||||
|
||||
console.log(`Start Balance: ${startBalanceEth} ETH`);
|
||||
console.log(`End Balance: ${endBalanceEth} ETH`);
|
||||
|
||||
console.log("successful faucet request: ",numFaucetRequest);
|
||||
};
|
||||
main().then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -4,6 +4,9 @@ import program from "commander";
|
||||
program.option("--rpc <rpc>", "Rpc");
|
||||
program.option("--startNum <startNum>", "start num")
|
||||
program.option("--endNum <endNum>", "end num")
|
||||
// --miner:
|
||||
// specified: find the max txCounter from the specified validator
|
||||
// not specified: find the max txCounter from all validators
|
||||
program.option("--miner <miner>", "miner", "")
|
||||
program.parse(process.argv);
|
||||
|
||||
|
||||
@@ -12,10 +12,23 @@ const main = async () => {
|
||||
console.log(blockNum);
|
||||
for (let i = 0; i < program.Num; i++) {
|
||||
let blockData = await provider.getBlock(blockNum - i);
|
||||
// 1.get Geth client version
|
||||
let major = ethers.toNumber(ethers.dataSlice(blockData.extraData, 2, 3))
|
||||
let minor = ethers.toNumber(ethers.dataSlice(blockData.extraData, 3, 4))
|
||||
let patch = ethers.toNumber(ethers.dataSlice(blockData.extraData, 4, 5))
|
||||
console.log(blockData.miner, "version =", major + "." + minor + "." + patch)
|
||||
|
||||
// 2.get minimum txGasPrice based on the last non-zero-gasprice transaction
|
||||
let lastGasPrice = 0
|
||||
for (let txIndex = blockData.transactions.length - 1; txIndex >= 0; txIndex--) {
|
||||
let txHash = blockData.transactions[txIndex]
|
||||
let txData = await provider.getTransaction(txHash);
|
||||
if (txData.gasPrice == 0) {
|
||||
continue
|
||||
}
|
||||
lastGasPrice = txData.gasPrice
|
||||
break
|
||||
}
|
||||
console.log(blockData.miner, "version =", major + "." + minor + "." + patch, " MinGasPrice = " + lastGasPrice)
|
||||
}
|
||||
};
|
||||
main().then(() => process.exit(0))
|
||||
|
||||
@@ -93,10 +93,10 @@ var (
|
||||
Value: flags.DirectoryString(node.DefaultDataDir()),
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
SeparateDBFlag = &cli.BoolFlag{
|
||||
Name: "separatedb",
|
||||
Usage: "Enable a separated trie database, it will be created within a subdirectory called state, " +
|
||||
"Users can copy this state directory to another directory or disk, and then create a symbolic link to the state directory under the chaindata",
|
||||
MultiDataBaseFlag = &cli.BoolFlag{
|
||||
Name: "multidatabase",
|
||||
Usage: "Enable a separated state and block database, it will be created within two subdirectory called state and block, " +
|
||||
"Users can copy this state or block directory to another directory or disk, and then create a symbolic link to the state directory under the chaindata",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
DirectBroadcastFlag = &cli.BoolFlag{
|
||||
@@ -150,6 +150,12 @@ var (
|
||||
Usage: "Minimum free disk space in MB, once reached triggers auto shut down (default = --cache.gc converted to MB, 0 = disabled)",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
InstanceFlag = &cli.IntFlag{
|
||||
Name: "instance",
|
||||
Usage: "Configures the ports to avoid conflicts when running multiple nodes on the same machine. Maximum is 200. Only applicable for: port, authrpc.port, discovery,port, http.port, ws.port",
|
||||
Value: 1,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
KeyStoreDirFlag = &flags.DirectoryFlag{
|
||||
Name: "keystore",
|
||||
Usage: "Directory for the keystore (default = inside the datadir)",
|
||||
@@ -218,7 +224,7 @@ var (
|
||||
// hbss2pbss command options
|
||||
ForceFlag = &cli.BoolFlag{
|
||||
Name: "force",
|
||||
Usage: "Force convert hbss trie node to pbss trie node. Ingore any metadata",
|
||||
Usage: "Force convert hbss trie node to pbss trie node. Ignore any metadata",
|
||||
Value: false,
|
||||
}
|
||||
// Dump command options.
|
||||
@@ -299,29 +305,43 @@ var (
|
||||
Usage: "Manually specify the Rialto Genesis Hash, to trigger builtin network logic",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideShanghai = &cli.Uint64Flag{
|
||||
Name: "override.shanghai",
|
||||
Usage: "Manually specify the Shanghai fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideKepler = &cli.Uint64Flag{
|
||||
Name: "override.kepler",
|
||||
Usage: "Manually specify the Kepler fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideCancun = &cli.Uint64Flag{
|
||||
Name: "override.cancun",
|
||||
Usage: "Manually specify the Cancun fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideHaber = &cli.Uint64Flag{
|
||||
Name: "override.haber",
|
||||
Usage: "Manually specify the Haber fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideVerkle = &cli.Uint64Flag{
|
||||
Name: "override.verkle",
|
||||
Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideFeynman = &cli.Uint64Flag{
|
||||
Name: "override.feynman",
|
||||
Usage: "Manually specify the Feynman fork timestamp, overriding the bundled setting",
|
||||
OverrideFullImmutabilityThreshold = &cli.Uint64Flag{
|
||||
Name: "override.immutabilitythreshold",
|
||||
Usage: "It is the number of blocks after which a chain segment is considered immutable, only for testing purpose",
|
||||
Value: params.FullImmutabilityThreshold,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideMinBlocksForBlobRequests = &cli.Uint64Flag{
|
||||
Name: "override.minforblobrequest",
|
||||
Usage: "It keeps blob data available for min blocks in local, only for testing purpose",
|
||||
Value: params.MinBlocksForBlobRequests,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideDefaultExtraReserveForBlobRequests = &cli.Uint64Flag{
|
||||
Name: "override.defaultextrareserve",
|
||||
Usage: "It adds more extra time for expired blobs for some request cases, only for testing purpose",
|
||||
Value: params.DefaultExtraReserveForBlobRequests,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideBreatheBlockInterval = &cli.Uint64Flag{
|
||||
Name: "override.breatheblockinterval",
|
||||
Usage: "It changes the interval between breathe blocks, only for testing purpose",
|
||||
Value: params.BreatheBlockInterval,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
SyncModeFlag = &flags.TextMarshalerFlag{
|
||||
@@ -347,6 +367,12 @@ var (
|
||||
Value: false,
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
JournalFileFlag = &cli.BoolFlag{
|
||||
Name: "journalfile",
|
||||
Usage: "Enable using journal file to store the TrieJournal instead of KVDB in pbss (default = false)",
|
||||
Value: false,
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
StateHistoryFlag = &cli.Uint64Flag{
|
||||
Name: "history.state",
|
||||
Usage: "Number of recent blocks to retain state history for (default = 90,000 blocks, 0 = entire chain)",
|
||||
@@ -858,6 +884,11 @@ var (
|
||||
Usage: "Disables the peer discovery mechanism (manual peer addition)",
|
||||
Category: flags.NetworkingCategory,
|
||||
}
|
||||
PeerFilterPatternsFlag = &cli.StringSliceFlag{
|
||||
Name: "peerfilter",
|
||||
Usage: "Disallow peers connection if peer name matches the given regular expressions",
|
||||
Category: flags.NetworkingCategory,
|
||||
}
|
||||
DiscoveryV4Flag = &cli.BoolFlag{
|
||||
Name: "discovery.v4",
|
||||
Aliases: []string{"discv4"},
|
||||
@@ -1100,6 +1131,14 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
||||
Usage: "Path for the voteJournal dir in fast finality feature (default = inside the datadir)",
|
||||
Category: flags.FastFinalityCategory,
|
||||
}
|
||||
|
||||
// Blob setting
|
||||
BlobExtraReserveFlag = &cli.Uint64Flag{
|
||||
Name: "blob.extra-reserve",
|
||||
Usage: "Extra reserve threshold for blob, blob never expires when 0 is set, default 28800",
|
||||
Value: params.DefaultExtraReserveForBlobRequests,
|
||||
Category: flags.MiscCategory,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -1118,7 +1157,7 @@ var (
|
||||
DBEngineFlag,
|
||||
StateSchemeFlag,
|
||||
HttpHeaderFlag,
|
||||
SeparateDBFlag,
|
||||
MultiDataBaseFlag,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1516,6 +1555,9 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
||||
if ctx.IsSet(NoDiscoverFlag.Name) {
|
||||
cfg.NoDiscovery = true
|
||||
}
|
||||
if ctx.IsSet(PeerFilterPatternsFlag.Name) {
|
||||
cfg.PeerFilterPatterns = ctx.StringSlice(PeerFilterPatternsFlag.Name)
|
||||
}
|
||||
|
||||
CheckExclusive(ctx, DiscoveryV4Flag, NoDiscoverFlag)
|
||||
CheckExclusive(ctx, DiscoveryV5Flag, NoDiscoverFlag)
|
||||
@@ -1542,6 +1584,7 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
||||
|
||||
// SetNodeConfig applies node-related command line flags to the config.
|
||||
func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
||||
setInstance(ctx, cfg)
|
||||
SetP2PConfig(ctx, &cfg.P2P)
|
||||
setIPC(ctx, cfg)
|
||||
setHTTP(ctx, cfg)
|
||||
@@ -1935,9 +1978,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
if ctx.IsSet(PathDBSyncFlag.Name) {
|
||||
cfg.PathSyncFlush = true
|
||||
}
|
||||
if ctx.IsSet(JournalFileFlag.Name) {
|
||||
cfg.JournalFileEnabled = true
|
||||
}
|
||||
|
||||
if ctx.String(GCModeFlag.Name) == "archive" && cfg.TransactionHistory != 0 {
|
||||
cfg.TransactionHistory = 0
|
||||
log.Warn("Disabled transaction unindexing for archive node")
|
||||
|
||||
cfg.StateScheme = rawdb.HashScheme
|
||||
log.Warn("Forcing hash state-scheme for archive mode")
|
||||
}
|
||||
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheTrieFlag.Name) {
|
||||
cfg.TrieCleanCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheTrieFlag.Name) / 100
|
||||
@@ -1955,6 +2005,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
if cfg.TriesVerifyMode.NeedRemoteVerify() {
|
||||
cfg.EnableTrustProtocol = true
|
||||
}
|
||||
// A node without trie is not able to provide snap data, so it should disable snap protocol.
|
||||
if cfg.TriesVerifyMode != core.LocalVerify {
|
||||
log.Info("Automatically disables snap protocol due to verify mode", "mode", cfg.TriesVerifyMode)
|
||||
cfg.DisableSnapProtocol = true
|
||||
}
|
||||
|
||||
if cfg.SyncMode == downloader.SnapSync && cfg.TriesVerifyMode.NoTries() {
|
||||
log.Warn("Only local TriesVerifyMode can support snap sync, resetting to full sync", "mode", cfg.TriesVerifyMode)
|
||||
cfg.SyncMode = downloader.FullSync
|
||||
}
|
||||
}
|
||||
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheSnapshotFlag.Name) {
|
||||
cfg.SnapshotCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheSnapshotFlag.Name) / 100
|
||||
@@ -2086,7 +2146,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) {
|
||||
cfg.Genesis = nil // fallback to db content
|
||||
|
||||
//validate genesis has PoS enabled in block 0
|
||||
// validate genesis has PoS enabled in block 0
|
||||
genesis, err := core.ReadGenesis(chaindb)
|
||||
if err != nil {
|
||||
Fatalf("Could not read genesis from database: %v", err)
|
||||
@@ -2119,6 +2179,18 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil {
|
||||
Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err)
|
||||
}
|
||||
|
||||
// blob setting
|
||||
if ctx.IsSet(OverrideDefaultExtraReserveForBlobRequests.Name) {
|
||||
cfg.BlobExtraReserve = ctx.Uint64(OverrideDefaultExtraReserveForBlobRequests.Name)
|
||||
}
|
||||
if ctx.IsSet(BlobExtraReserveFlag.Name) {
|
||||
extraReserve := ctx.Uint64(BlobExtraReserveFlag.Name)
|
||||
if extraReserve > 0 && extraReserve < params.DefaultExtraReserveForBlobRequests {
|
||||
extraReserve = params.DefaultExtraReserveForBlobRequests
|
||||
}
|
||||
cfg.BlobExtraReserve = extraReserve
|
||||
}
|
||||
}
|
||||
|
||||
// SetDNSDiscoveryDefaults configures DNS discovery with the given URL if
|
||||
@@ -2178,7 +2250,7 @@ func EnableBuildInfo(gitCommit, gitDate string) SetupMetricsOption {
|
||||
}
|
||||
}
|
||||
|
||||
func EnableMinerInfo(ctx *cli.Context, minerConfig miner.Config) SetupMetricsOption {
|
||||
func EnableMinerInfo(ctx *cli.Context, minerConfig *miner.Config) SetupMetricsOption {
|
||||
return func() {
|
||||
if ctx.Bool(MiningEnabledFlag.Name) {
|
||||
// register miner info into metrics
|
||||
@@ -2201,10 +2273,13 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf
|
||||
return filterSystem
|
||||
}
|
||||
|
||||
func EnableNodeInfo(poolConfig legacypool.Config) SetupMetricsOption {
|
||||
func EnableNodeInfo(poolConfig *legacypool.Config, nodeInfo *p2p.NodeInfo) SetupMetricsOption {
|
||||
return func() {
|
||||
// register node info into metrics
|
||||
metrics.NewRegisteredLabel("node-info", nil).Mark(map[string]interface{}{
|
||||
"Enode": nodeInfo.Enode,
|
||||
"ENR": nodeInfo.ENR,
|
||||
"ID": nodeInfo.ID,
|
||||
"PriceLimit": poolConfig.PriceLimit,
|
||||
"PriceBump": poolConfig.PriceBump,
|
||||
"AccountSlots": poolConfig.AccountSlots,
|
||||
@@ -2322,9 +2397,11 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly, disableFree
|
||||
default:
|
||||
chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "", readonly, disableFreeze, false, false)
|
||||
// set the separate state database
|
||||
if stack.IsSeparatedDB() && err == nil {
|
||||
if stack.CheckIfMultiDataBase() && err == nil {
|
||||
stateDiskDb := MakeStateDataBase(ctx, stack, readonly, false)
|
||||
chainDb.SetStateStore(stateDiskDb)
|
||||
blockDb := MakeBlockDatabase(ctx, stack, readonly, false)
|
||||
chainDb.SetBlockStore(blockDb)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@@ -2336,7 +2413,7 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly, disableFree
|
||||
// MakeStateDataBase open a separate state database using the flags passed to the client and will hard crash if it fails.
|
||||
func MakeStateDataBase(ctx *cli.Context, stack *node.Node, readonly, disableFreeze bool) ethdb.Database {
|
||||
cache := ctx.Int(CacheFlag.Name) * ctx.Int(CacheDatabaseFlag.Name) / 100
|
||||
handles := MakeDatabaseHandles(ctx.Int(FDLimitFlag.Name)) / 2
|
||||
handles := MakeDatabaseHandles(ctx.Int(FDLimitFlag.Name)) * 90 / 100
|
||||
statediskdb, err := stack.OpenDatabaseWithFreezer("chaindata/state", cache, handles, "", "", readonly, disableFreeze, false, false)
|
||||
if err != nil {
|
||||
Fatalf("Failed to open separate trie database: %v", err)
|
||||
@@ -2344,6 +2421,23 @@ func MakeStateDataBase(ctx *cli.Context, stack *node.Node, readonly, disableFree
|
||||
return statediskdb
|
||||
}
|
||||
|
||||
// MakeBlockDatabase open a separate block database using the flags passed to the client and will hard crash if it fails.
|
||||
func MakeBlockDatabase(ctx *cli.Context, stack *node.Node, readonly, disableFreeze bool) ethdb.Database {
|
||||
cache := ctx.Int(CacheFlag.Name) * ctx.Int(CacheDatabaseFlag.Name) / 100
|
||||
handles := MakeDatabaseHandles(ctx.Int(FDLimitFlag.Name)) / 10
|
||||
blockDb, err := stack.OpenDatabaseWithFreezer("chaindata/block", cache, handles, "", "", readonly, disableFreeze, false, false)
|
||||
if err != nil {
|
||||
Fatalf("Failed to open separate block database: %v", err)
|
||||
}
|
||||
return blockDb
|
||||
}
|
||||
|
||||
func PathDBConfigAddJournalFilePath(stack *node.Node, config *pathdb.Config) *pathdb.Config {
|
||||
path := fmt.Sprintf("%s/%s", stack.ResolvePath("chaindata"), eth.JournalFileName)
|
||||
config.JournalFilePath = path
|
||||
return config
|
||||
}
|
||||
|
||||
// tryMakeReadOnlyDatabase try to open the chain database in read-only mode,
|
||||
// or fallback to write mode if the database is not initialized.
|
||||
//
|
||||
@@ -2486,7 +2580,7 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
|
||||
}
|
||||
|
||||
// MakeTrieDatabase constructs a trie database based on the configured scheme.
|
||||
func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database {
|
||||
func MakeTrieDatabase(ctx *cli.Context, stack *node.Node, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database {
|
||||
config := &triedb.Config{
|
||||
Preimages: preimage,
|
||||
IsVerkle: isVerkle,
|
||||
@@ -2507,6 +2601,7 @@ func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, read
|
||||
} else {
|
||||
config.PathDB = pathdb.Defaults
|
||||
}
|
||||
config.PathDB.JournalFilePath = fmt.Sprintf("%s/%s", stack.ResolvePath("chaindata"), eth.JournalFileName)
|
||||
return triedb.NewDatabase(disk, config)
|
||||
}
|
||||
|
||||
@@ -2528,3 +2623,24 @@ func ParseCLIAndConfigStateScheme(cliScheme, cfgScheme string) (string, error) {
|
||||
}
|
||||
return "", fmt.Errorf("incompatible state scheme, CLI: %s, config: %s", cliScheme, cfgScheme)
|
||||
}
|
||||
|
||||
// setInstance configures the port numbers for the given instance.
|
||||
func setInstance(ctx *cli.Context, cfg *node.Config) {
|
||||
if ctx.IsSet(InstanceFlag.Name) {
|
||||
cfg.Instance = ctx.Int(InstanceFlag.Name)
|
||||
}
|
||||
|
||||
if cfg.Instance > 200 {
|
||||
Fatalf("Instance number %d is too high, maximum is 200", cfg.Instance)
|
||||
}
|
||||
|
||||
if cfg.Instance == 1 { // using default ports
|
||||
return
|
||||
}
|
||||
|
||||
cfg.AuthPort = node.DefaultConfig.AuthPort + cfg.Instance*100 - 100
|
||||
cfg.HTTPPort = node.DefaultHTTPPort - cfg.Instance + 1
|
||||
cfg.WSPort = node.DefaultWSPort + cfg.Instance*2 - 2
|
||||
cfg.P2P.ListenAddr = fmt.Sprintf(":%d", node.DefaultListenPort+cfg.Instance-1)
|
||||
cfg.P2P.DiscAddr = fmt.Sprintf(":%d", node.DefaultDiscPort+cfg.Instance-1)
|
||||
}
|
||||
|
||||
@@ -407,7 +407,7 @@ func (c *Clique) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
|
||||
// at a checkpoint block without a parent (light client CHT), or we have piled
|
||||
// up more headers than allowed to be reorged (chain reinit from a freezer),
|
||||
// consider the checkpoint trusted and snapshot it.
|
||||
if number == 0 || (number%c.config.Epoch == 0 && (len(headers) > params.FullImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) {
|
||||
if number == 0 || (number%c.config.Epoch == 0 && (len(headers) > int(params.FullImmutabilityThreshold) || chain.GetHeaderByNumber(number-1) == nil)) {
|
||||
checkpoint := chain.GetHeaderByNumber(number)
|
||||
if checkpoint != nil {
|
||||
hash := checkpoint.Hash()
|
||||
|
||||
@@ -467,7 +467,6 @@ func (tt *cliqueTest) run(t *testing.T) {
|
||||
for j := 0; j < len(batches)-1; j++ {
|
||||
if k, err := chain.InsertChain(batches[j]); err != nil {
|
||||
t.Fatalf("failed to import batch %d, block %d: %v", j, k, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
if _, err = chain.InsertChain(batches[len(batches)-1]); err != tt.failure {
|
||||
|
||||
@@ -58,6 +58,9 @@ type ChainHeaderReader interface {
|
||||
|
||||
// GetHighestVerifiedHeader retrieves the highest header verified.
|
||||
GetHighestVerifiedHeader() *types.Header
|
||||
|
||||
// ChasingHead return the best chain head of peers.
|
||||
ChasingHead() *types.Header
|
||||
}
|
||||
|
||||
type VotePool interface {
|
||||
|
||||
@@ -15,14 +15,13 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
const SecondsPerDay uint64 = 86400
|
||||
|
||||
// the params should be two blocks' time(timestamp)
|
||||
func sameDayInUTC(first, second uint64) bool {
|
||||
return first/SecondsPerDay == second/SecondsPerDay
|
||||
return first/params.BreatheBlockInterval == second/params.BreatheBlockInterval
|
||||
}
|
||||
|
||||
func isBreatheBlock(lastBlockTime, blockTime uint64) bool {
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||
"github.com/willf/bitset"
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
@@ -48,8 +48,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
inMemorySnapshots = 256 // Number of recent snapshots to keep in memory
|
||||
inMemorySignatures = 4096 // Number of recent block signatures to keep in memory
|
||||
inMemorySnapshots = 256 // Number of recent snapshots to keep in memory
|
||||
inMemorySignatures = 4096 // Number of recent block signatures to keep in memory
|
||||
inMemoryHeaders = 86400 // Number of recent headers to keep in memory for double sign detection,
|
||||
|
||||
checkpointInterval = 1024 // Number of blocks after which to save the snapshot to the database
|
||||
defaultEpochLength = uint64(100) // Default number of blocks of checkpoint to update validatorSet from contract
|
||||
@@ -80,6 +81,7 @@ var (
|
||||
verifyVoteAttestationErrorCounter = metrics.NewRegisteredCounter("parlia/verifyVoteAttestation/error", nil)
|
||||
updateAttestationErrorCounter = metrics.NewRegisteredCounter("parlia/updateAttestation/error", nil)
|
||||
validVotesfromSelfCounter = metrics.NewRegisteredCounter("parlia/VerifyVote/self", nil)
|
||||
doubleSignCounter = metrics.NewRegisteredCounter("parlia/doublesign", nil)
|
||||
|
||||
systemContracts = map[common.Address]bool{
|
||||
common.HexToAddress(systemcontracts.ValidatorContract): true,
|
||||
@@ -216,8 +218,11 @@ type Parlia struct {
|
||||
genesisHash common.Hash
|
||||
db ethdb.Database // Database to store and retrieve snapshot checkpoints
|
||||
|
||||
recentSnaps *lru.ARCCache // Snapshots for recent block to speed up
|
||||
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
|
||||
recentSnaps *lru.ARCCache // Snapshots for recent block to speed up
|
||||
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
|
||||
recentHeaders *lru.ARCCache //
|
||||
// Recent headers to check for double signing: key includes block number and miner. value is the block header
|
||||
// If same key's value already exists for different block header roots then double sign is detected
|
||||
|
||||
signer types.Signer
|
||||
|
||||
@@ -263,6 +268,10 @@ func New(
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
recentHeaders, err := lru.NewARC(inMemoryHeaders)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
vABIBeforeLuban, err := abi.JSON(strings.NewReader(validatorSetABIBeforeLuban))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -286,6 +295,7 @@ func New(
|
||||
db: db,
|
||||
ethAPI: ethAPI,
|
||||
recentSnaps: recentSnaps,
|
||||
recentHeaders: recentHeaders,
|
||||
signatures: signatures,
|
||||
validatorSetABIBeforeLuban: vABIBeforeLuban,
|
||||
validatorSetABI: vABI,
|
||||
@@ -583,14 +593,6 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify existence / non-existence of withdrawalsHash.
|
||||
if header.WithdrawalsHash != nil {
|
||||
return fmt.Errorf("invalid withdrawalsHash: have %x, expected nil", header.WithdrawalsHash)
|
||||
}
|
||||
// Verify the existence / non-existence of cancun-specific header fields
|
||||
if header.ParentBeaconRoot != nil {
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
}
|
||||
cancun := chain.Config().IsCancun(header.Number, header.Time)
|
||||
if !cancun {
|
||||
switch {
|
||||
@@ -598,8 +600,18 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
||||
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
|
||||
case header.BlobGasUsed != nil:
|
||||
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
|
||||
case header.ParentBeaconRoot != nil:
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
case header.WithdrawalsHash != nil:
|
||||
return fmt.Errorf("invalid WithdrawalsHash, have %#x, expected nil", header.WithdrawalsHash)
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case header.ParentBeaconRoot != nil:
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
case !header.EmptyWithdrawalsHash():
|
||||
return errors.New("header has wrong WithdrawalsHash")
|
||||
}
|
||||
if err := eip4844.VerifyEIP4844Header(parent, header); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -697,10 +709,8 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
|
||||
}
|
||||
}
|
||||
|
||||
// If we're at the genesis, snapshot the initial state. Alternatively if we have
|
||||
// piled up more headers than allowed to be reorged (chain reinit from a freezer),
|
||||
// consider the checkpoint trusted and snapshot it.
|
||||
if number == 0 || (number%p.config.Epoch == 0 && (len(headers) > params.FullImmutabilityThreshold/10)) {
|
||||
// If we're at the genesis, snapshot the initial state.
|
||||
if number == 0 {
|
||||
checkpoint := chain.GetHeaderByNumber(number)
|
||||
if checkpoint != nil {
|
||||
// get checkpoint data
|
||||
@@ -714,12 +724,10 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
|
||||
|
||||
// new snapshot
|
||||
snap = newSnapshot(p.config, p.signatures, number, hash, validators, voteAddrs, p.ethAPI)
|
||||
if snap.Number%checkpointInterval == 0 { // snapshot will only be loaded when snap.Number%checkpointInterval == 0
|
||||
if err := snap.store(p.db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash)
|
||||
if err := snap.store(p.db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -811,6 +819,17 @@ func (p *Parlia) verifySeal(chain consensus.ChainHeaderReader, header *types.Hea
|
||||
return errCoinBaseMisMatch
|
||||
}
|
||||
|
||||
// check for double sign & add to cache
|
||||
key := proposalKey(*header)
|
||||
preHash, ok := p.recentHeaders.Get(key)
|
||||
if ok && preHash != header.Hash() {
|
||||
doubleSignCounter.Inc(1)
|
||||
log.Warn("DoubleSign detected", " block", header.Number, " miner", header.Coinbase,
|
||||
"hash1", preHash.(common.Hash), "hash2", header.Hash())
|
||||
} else {
|
||||
p.recentHeaders.Add(key, header.Hash())
|
||||
}
|
||||
|
||||
if _, ok := snap.Validators[signer]; !ok {
|
||||
return errUnauthorizedValidator(signer.String())
|
||||
}
|
||||
@@ -1988,16 +2007,19 @@ func applyMessage(
|
||||
chainConfig *params.ChainConfig,
|
||||
chainContext core.ChainContext,
|
||||
) (uint64, error) {
|
||||
// TODO(Nathan): state.Prepare should be called here, now accessList related EIP not affect systemtxs
|
||||
// EIP1153 may cause a critical issue in the future
|
||||
// Create a new context to be used in the EVM environment
|
||||
context := core.NewEVMBlockContext(header, chainContext, nil)
|
||||
// Create a new environment which holds all relevant information
|
||||
// about the transaction and calling mechanisms.
|
||||
vmenv := vm.NewEVM(context, vm.TxContext{Origin: msg.From(), GasPrice: big.NewInt(0)}, state, chainConfig, vm.Config{})
|
||||
// Apply the transaction to the current state (included in the env)
|
||||
if chainConfig.IsCancun(header.Number, header.Time) {
|
||||
rules := vmenv.ChainConfig().Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
|
||||
state.Prepare(rules, msg.From(), vmenv.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList)
|
||||
}
|
||||
// Increment the nonce for the next transaction
|
||||
state.SetNonce(msg.From(), state.GetNonce(msg.From())+1)
|
||||
|
||||
ret, returnGas, err := vmenv.Call(
|
||||
vm.AccountRef(msg.From()),
|
||||
*msg.To(),
|
||||
@@ -2010,3 +2032,8 @@ func applyMessage(
|
||||
}
|
||||
return msg.Gas() - returnGas, err
|
||||
}
|
||||
|
||||
// proposalKey build a key which is a combination of the block number and the proposer address.
|
||||
func proposalKey(header types.Header) string {
|
||||
return header.ParentHash.String() + header.Coinbase.String()
|
||||
}
|
||||
|
||||
@@ -66,6 +66,31 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin
|
||||
return validator
|
||||
}
|
||||
|
||||
// ValidateListsInBody validates that UncleHash, WithdrawalsHash, and WithdrawalsHash correspond to the lists in the block body, respectively.
|
||||
func ValidateListsInBody(block *types.Block) error {
|
||||
header := block.Header()
|
||||
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
|
||||
return fmt.Errorf("uncle root hash mismatch (header value %x, calculated %x)", header.UncleHash, hash)
|
||||
}
|
||||
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
|
||||
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
|
||||
}
|
||||
// Withdrawals are present after the Shanghai fork.
|
||||
if header.WithdrawalsHash != nil {
|
||||
// Withdrawals list must be present in body after Shanghai.
|
||||
if block.Withdrawals() == nil {
|
||||
return errors.New("missing withdrawals in block body")
|
||||
}
|
||||
if hash := types.DeriveSha(block.Withdrawals(), trie.NewStackTrie(nil)); hash != *header.WithdrawalsHash {
|
||||
return fmt.Errorf("withdrawals root hash mismatch (header value %x, calculated %x)", *header.WithdrawalsHash, hash)
|
||||
}
|
||||
} else if block.Withdrawals() != nil { // Withdrawals turn into empty from nil when BlockBody has Sidecars
|
||||
// Withdrawals are not allowed prior to shanghai fork
|
||||
return errors.New("withdrawals present in block body")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateBody validates the given block's uncles and verifies the block
|
||||
// header's transaction and uncle roots. The headers are assumed to be already
|
||||
// validated at this point.
|
||||
@@ -83,31 +108,12 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||
if err := v.engine.VerifyUncles(v.bc, block); err != nil {
|
||||
return err
|
||||
}
|
||||
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
|
||||
return fmt.Errorf("uncle root hash mismatch (header value %x, calculated %x)", header.UncleHash, hash)
|
||||
}
|
||||
|
||||
validateFuns := []func() error{
|
||||
func() error {
|
||||
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
|
||||
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
|
||||
}
|
||||
return nil
|
||||
return ValidateListsInBody(block)
|
||||
},
|
||||
func() error {
|
||||
// Withdrawals are present after the Shanghai fork.
|
||||
if header.WithdrawalsHash != nil {
|
||||
// Withdrawals list must be present in body after Shanghai.
|
||||
if block.Withdrawals() == nil {
|
||||
return errors.New("missing withdrawals in block body")
|
||||
}
|
||||
if hash := types.DeriveSha(block.Withdrawals(), trie.NewStackTrie(nil)); hash != *header.WithdrawalsHash {
|
||||
return fmt.Errorf("withdrawals root hash mismatch (header value %x, calculated %x)", *header.WithdrawalsHash, hash)
|
||||
}
|
||||
} else if block.Withdrawals() != nil {
|
||||
// Withdrawals are not allowed prior to shanghai fork
|
||||
return errors.New("withdrawals present in block body")
|
||||
}
|
||||
// Blob transactions may be present after the Cancun fork.
|
||||
var blobs int
|
||||
for i, tx := range block.Transactions() {
|
||||
|
||||
@@ -71,6 +71,8 @@ var (
|
||||
justifiedBlockGauge = metrics.NewRegisteredGauge("chain/head/justified", nil)
|
||||
finalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
|
||||
|
||||
blockInsertMgaspsGauge = metrics.NewRegisteredGauge("chain/insert/mgasps", nil)
|
||||
|
||||
chainInfoGauge = metrics.NewRegisteredGaugeInfo("chain/info", nil)
|
||||
|
||||
accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
|
||||
@@ -110,6 +112,7 @@ const (
|
||||
blockCacheLimit = 256
|
||||
diffLayerCacheLimit = 1024
|
||||
receiptsCacheLimit = 10000
|
||||
sidecarsCacheLimit = 1024
|
||||
txLookupCacheLimit = 1024
|
||||
maxBadBlockLimit = 16
|
||||
maxFutureBlocks = 256
|
||||
@@ -164,6 +167,8 @@ type CacheConfig struct {
|
||||
StateHistory uint64 // Number of blocks from head whose state histories are reserved.
|
||||
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
|
||||
PathSyncFlush bool // Whether sync flush the trienodebuffer of pathdb to disk.
|
||||
JournalFilePath string
|
||||
JournalFile bool
|
||||
|
||||
SnapshotNoBuild bool // Whether the background generation is allowed
|
||||
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
|
||||
@@ -183,10 +188,12 @@ func (c *CacheConfig) triedbConfig() *triedb.Config {
|
||||
}
|
||||
if c.StateScheme == rawdb.PathScheme {
|
||||
config.PathDB = &pathdb.Config{
|
||||
SyncFlush: c.PathSyncFlush,
|
||||
StateHistory: c.StateHistory,
|
||||
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
|
||||
DirtyCacheSize: c.TrieDirtyLimit * 1024 * 1024,
|
||||
SyncFlush: c.PathSyncFlush,
|
||||
StateHistory: c.StateHistory,
|
||||
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
|
||||
DirtyCacheSize: c.TrieDirtyLimit * 1024 * 1024,
|
||||
JournalFilePath: c.JournalFilePath,
|
||||
JournalFile: c.JournalFile,
|
||||
}
|
||||
}
|
||||
return config
|
||||
@@ -271,12 +278,15 @@ type BlockChain struct {
|
||||
highestVerifiedHeader atomic.Pointer[types.Header]
|
||||
currentBlock atomic.Pointer[types.Header] // Current head of the chain
|
||||
currentSnapBlock atomic.Pointer[types.Header] // Current head of snap-sync
|
||||
currentFinalBlock atomic.Pointer[types.Header] // Latest (consensus) finalized block
|
||||
chasingHead atomic.Pointer[types.Header]
|
||||
|
||||
bodyCache *lru.Cache[common.Hash, *types.Body]
|
||||
bodyRLPCache *lru.Cache[common.Hash, rlp.RawValue]
|
||||
receiptsCache *lru.Cache[common.Hash, []*types.Receipt]
|
||||
blockCache *lru.Cache[common.Hash, *types.Block]
|
||||
txLookupCache *lru.Cache[common.Hash, txLookup]
|
||||
sidecarsCache *lru.Cache[common.Hash, types.BlobSidecars]
|
||||
|
||||
// future blocks are blocks added for later processing
|
||||
futureBlocks *lru.Cache[common.Hash, *types.Block]
|
||||
@@ -316,9 +326,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
if cacheConfig == nil {
|
||||
cacheConfig = defaultCacheConfig
|
||||
}
|
||||
if cacheConfig.TriesInMemory != 128 {
|
||||
log.Warn("TriesInMemory isn't the default value(128), you need specify exact same TriesInMemory when prune data",
|
||||
"triesInMemory", cacheConfig.TriesInMemory)
|
||||
if cacheConfig.StateScheme == rawdb.HashScheme && cacheConfig.TriesInMemory != 128 {
|
||||
log.Warn("TriesInMemory isn't the default value (128), you need specify the same TriesInMemory when pruning data",
|
||||
"triesInMemory", cacheConfig.TriesInMemory, "scheme", cacheConfig.StateScheme)
|
||||
}
|
||||
|
||||
diffLayerCache, _ := exlru.New(diffLayerCacheLimit)
|
||||
@@ -359,6 +369,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
|
||||
bodyRLPCache: lru.NewCache[common.Hash, rlp.RawValue](bodyCacheLimit),
|
||||
receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
|
||||
sidecarsCache: lru.NewCache[common.Hash, types.BlobSidecars](sidecarsCacheLimit),
|
||||
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
|
||||
txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit),
|
||||
futureBlocks: lru.NewCache[common.Hash, *types.Block](maxFutureBlocks),
|
||||
@@ -390,6 +401,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
bc.highestVerifiedHeader.Store(nil)
|
||||
bc.currentBlock.Store(nil)
|
||||
bc.currentSnapBlock.Store(nil)
|
||||
bc.chasingHead.Store(nil)
|
||||
|
||||
// Update chain info data metrics
|
||||
chainInfoGauge.Update(metrics.GaugeInfoValue{"chain_id": bc.chainConfig.ChainID.String()})
|
||||
@@ -407,7 +419,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
// Make sure the state associated with the block is available, or log out
|
||||
// if there is no available state, waiting for state sync.
|
||||
head := bc.CurrentBlock()
|
||||
if !bc.NoTries() && !bc.HasState(head.Root) {
|
||||
if !bc.HasState(head.Root) {
|
||||
if head.Number.Uint64() == 0 {
|
||||
// The genesis state is missing, which is only possible in the path-based
|
||||
// scheme. This situation occurs when the initial state sync is not finished
|
||||
@@ -423,7 +435,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
|
||||
if bc.cacheConfig.SnapshotLimit > 0 {
|
||||
diskRoot = rawdb.ReadSnapshotRoot(bc.db)
|
||||
}
|
||||
if bc.triedb.Scheme() == rawdb.PathScheme {
|
||||
if bc.triedb.Scheme() == rawdb.PathScheme && !bc.NoTries() {
|
||||
recoverable, _ := bc.triedb.Recoverable(diskRoot)
|
||||
if !bc.HasState(diskRoot) && !recoverable {
|
||||
diskRoot = bc.triedb.Head()
|
||||
@@ -646,6 +658,9 @@ func (bc *BlockChain) cacheDiffLayer(diffLayer *types.DiffLayer, diffLayerCh cha
|
||||
|
||||
func (bc *BlockChain) cacheBlock(hash common.Hash, block *types.Block) {
|
||||
bc.blockCache.Add(hash, block)
|
||||
if bc.chainConfig.IsCancun(block.Number(), block.Time()) {
|
||||
bc.sidecarsCache.Add(hash, block.Sidecars())
|
||||
}
|
||||
}
|
||||
|
||||
// empty returns an indicator whether the blockchain is empty.
|
||||
@@ -654,7 +669,7 @@ func (bc *BlockChain) cacheBlock(hash common.Hash, block *types.Block) {
|
||||
// into node seamlessly.
|
||||
func (bc *BlockChain) empty() bool {
|
||||
genesis := bc.genesisBlock.Hash()
|
||||
for _, hash := range []common.Hash{rawdb.ReadHeadBlockHash(bc.db), rawdb.ReadHeadHeaderHash(bc.db), rawdb.ReadHeadFastBlockHash(bc.db)} {
|
||||
for _, hash := range []common.Hash{rawdb.ReadHeadBlockHash(bc.db.BlockStore()), rawdb.ReadHeadHeaderHash(bc.db.BlockStore()), rawdb.ReadHeadFastBlockHash(bc.db)} {
|
||||
if hash != genesis {
|
||||
return false
|
||||
}
|
||||
@@ -690,7 +705,7 @@ func (bc *BlockChain) getFinalizedNumber(header *types.Header) uint64 {
|
||||
// assumes that the chain manager mutex is held.
|
||||
func (bc *BlockChain) loadLastState() error {
|
||||
// Restore the last known head block
|
||||
head := rawdb.ReadHeadBlockHash(bc.db)
|
||||
head := rawdb.ReadHeadBlockHash(bc.db.BlockStore())
|
||||
if head == (common.Hash{}) {
|
||||
// Corrupt or empty database, init from scratch
|
||||
log.Warn("Empty database, resetting chain")
|
||||
@@ -712,7 +727,7 @@ func (bc *BlockChain) loadLastState() error {
|
||||
|
||||
// Restore the last known head header
|
||||
headHeader := headBlock.Header()
|
||||
if head := rawdb.ReadHeadHeaderHash(bc.db); head != (common.Hash{}) {
|
||||
if head := rawdb.ReadHeadHeaderHash(bc.db.BlockStore()); head != (common.Hash{}) {
|
||||
if header := bc.GetHeaderByHash(head); header != nil {
|
||||
headHeader = header
|
||||
}
|
||||
@@ -823,6 +838,183 @@ func (bc *BlockChain) tryRewindBadBlocks() {
|
||||
}
|
||||
}
|
||||
|
||||
// rewindHashHead implements the logic of rewindHead in the context of hash scheme.
|
||||
func (bc *BlockChain) rewindHashHead(head *types.Header, root common.Hash) (*types.Header, uint64) {
|
||||
var (
|
||||
limit uint64 // The oldest block that will be searched for this rewinding
|
||||
beyondRoot = root == common.Hash{} // Flag whether we're beyond the requested root (no root, always true)
|
||||
pivot = rawdb.ReadLastPivotNumber(bc.db) // Associated block number of pivot point state
|
||||
rootNumber uint64 // Associated block number of requested root
|
||||
|
||||
start = time.Now() // Timestamp the rewinding is restarted
|
||||
logged = time.Now() // Timestamp last progress log was printed
|
||||
)
|
||||
// The oldest block to be searched is determined by the pivot block or a constant
|
||||
// searching threshold. The rationale behind this is as follows:
|
||||
//
|
||||
// - Snap sync is selected if the pivot block is available. The earliest available
|
||||
// state is the pivot block itself, so there is no sense in going further back.
|
||||
//
|
||||
// - Full sync is selected if the pivot block does not exist. The hash database
|
||||
// periodically flushes the state to disk, and the used searching threshold is
|
||||
// considered sufficient to find a persistent state, even for the testnet. It
|
||||
// might be not enough for a chain that is nearly empty. In the worst case,
|
||||
// the entire chain is reset to genesis, and snap sync is re-enabled on top,
|
||||
// which is still acceptable.
|
||||
if pivot != nil {
|
||||
limit = *pivot
|
||||
} else if head.Number.Uint64() > params.FullImmutabilityThreshold {
|
||||
limit = head.Number.Uint64() - params.FullImmutabilityThreshold
|
||||
}
|
||||
for {
|
||||
logger := log.Trace
|
||||
if time.Since(logged) > time.Second*8 {
|
||||
logged = time.Now()
|
||||
logger = log.Info
|
||||
}
|
||||
logger("Block state missing, rewinding further", "number", head.Number, "hash", head.Hash(), "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
|
||||
// If a root threshold was requested but not yet crossed, check
|
||||
if !beyondRoot && head.Root == root {
|
||||
beyondRoot, rootNumber = true, head.Number.Uint64()
|
||||
}
|
||||
// If search limit is reached, return the genesis block as the
|
||||
// new chain head.
|
||||
if head.Number.Uint64() < limit {
|
||||
log.Info("Rewinding limit reached, resetting to genesis", "number", head.Number, "hash", head.Hash(), "limit", limit)
|
||||
return bc.genesisBlock.Header(), rootNumber
|
||||
}
|
||||
// If the associated state is not reachable, continue searching
|
||||
// backwards until an available state is found.
|
||||
if !bc.HasState(head.Root) {
|
||||
// If the chain is gapped in the middle, return the genesis
|
||||
// block as the new chain head.
|
||||
parent := bc.GetHeader(head.ParentHash, head.Number.Uint64()-1)
|
||||
if parent == nil {
|
||||
log.Error("Missing block in the middle, resetting to genesis", "number", head.Number.Uint64()-1, "hash", head.ParentHash)
|
||||
return bc.genesisBlock.Header(), rootNumber
|
||||
}
|
||||
head = parent
|
||||
|
||||
// If the genesis block is reached, stop searching.
|
||||
if head.Number.Uint64() == 0 {
|
||||
log.Info("Genesis block reached", "number", head.Number, "hash", head.Hash())
|
||||
return head, rootNumber
|
||||
}
|
||||
continue // keep rewinding
|
||||
}
|
||||
// Once the available state is found, ensure that the requested root
|
||||
// has already been crossed. If not, continue rewinding.
|
||||
if beyondRoot || head.Number.Uint64() == 0 {
|
||||
log.Info("Rewound to block with state", "number", head.Number, "hash", head.Hash())
|
||||
return head, rootNumber
|
||||
}
|
||||
log.Debug("Skipping block with threshold state", "number", head.Number, "hash", head.Hash(), "root", head.Root)
|
||||
head = bc.GetHeader(head.ParentHash, head.Number.Uint64()-1) // Keep rewinding
|
||||
}
|
||||
}
|
||||
|
||||
// rewindPathHead implements the logic of rewindHead in the context of path scheme.
|
||||
func (bc *BlockChain) rewindPathHead(head *types.Header, root common.Hash) (*types.Header, uint64) {
|
||||
var (
|
||||
pivot = rawdb.ReadLastPivotNumber(bc.db) // Associated block number of pivot block
|
||||
rootNumber uint64 // Associated block number of requested root
|
||||
|
||||
// BeyondRoot represents whether the requested root is already
|
||||
// crossed. The flag value is set to true if the root is empty.
|
||||
beyondRoot = root == common.Hash{}
|
||||
|
||||
// noState represents if the target state requested for search
|
||||
// is unavailable and impossible to be recovered.
|
||||
noState = !bc.HasState(root) && !bc.stateRecoverable(root)
|
||||
|
||||
start = time.Now() // Timestamp the rewinding is restarted
|
||||
logged = time.Now() // Timestamp last progress log was printed
|
||||
)
|
||||
// Rewind the head block tag until an available state is found.
|
||||
for {
|
||||
logger := log.Trace
|
||||
if time.Since(logged) > time.Second*8 {
|
||||
logged = time.Now()
|
||||
logger = log.Info
|
||||
}
|
||||
logger("Block state missing, rewinding further", "number", head.Number, "hash", head.Hash(), "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
|
||||
// If a root threshold was requested but not yet crossed, check
|
||||
if !beyondRoot && head.Root == root {
|
||||
beyondRoot, rootNumber = true, head.Number.Uint64()
|
||||
}
|
||||
// If the root threshold hasn't been crossed but the available
|
||||
// state is reached, quickly determine if the target state is
|
||||
// possible to be reached or not.
|
||||
if !beyondRoot && noState && bc.HasState(head.Root) {
|
||||
beyondRoot = true
|
||||
log.Info("Disable the search for unattainable state", "root", root)
|
||||
}
|
||||
// Check if the associated state is available or recoverable if
|
||||
// the requested root has already been crossed.
|
||||
if beyondRoot && (bc.HasState(head.Root) || bc.stateRecoverable(head.Root)) {
|
||||
break
|
||||
}
|
||||
// If pivot block is reached, return the genesis block as the
|
||||
// new chain head. Theoretically there must be a persistent
|
||||
// state before or at the pivot block, prevent endless rewinding
|
||||
// towards the genesis just in case.
|
||||
if pivot != nil && *pivot >= head.Number.Uint64() {
|
||||
log.Info("Pivot block reached, resetting to genesis", "number", head.Number, "hash", head.Hash())
|
||||
return bc.genesisBlock.Header(), rootNumber
|
||||
}
|
||||
// If the chain is gapped in the middle, return the genesis
|
||||
// block as the new chain head
|
||||
parent := bc.GetHeader(head.ParentHash, head.Number.Uint64()-1) // Keep rewinding
|
||||
if parent == nil {
|
||||
log.Error("Missing block in the middle, resetting to genesis", "number", head.Number.Uint64()-1, "hash", head.ParentHash)
|
||||
return bc.genesisBlock.Header(), rootNumber
|
||||
}
|
||||
head = parent
|
||||
|
||||
// If the genesis block is reached, stop searching.
|
||||
if head.Number.Uint64() == 0 {
|
||||
log.Info("Genesis block reached", "number", head.Number, "hash", head.Hash())
|
||||
return head, rootNumber
|
||||
}
|
||||
}
|
||||
// Recover if the target state if it's not available yet.
|
||||
if !bc.HasState(head.Root) {
|
||||
if err := bc.triedb.Recover(head.Root); err != nil {
|
||||
log.Crit("Failed to rollback state", "err", err)
|
||||
}
|
||||
}
|
||||
log.Info("Rewound to block with state", "number", head.Number, "hash", head.Hash())
|
||||
return head, rootNumber
|
||||
}
|
||||
|
||||
// rewindHead searches the available states in the database and returns the associated
|
||||
// block as the new head block.
|
||||
//
|
||||
// If the given root is not empty, then the rewind should attempt to pass the specified
|
||||
// state root and return the associated block number as well. If the root, typically
|
||||
// representing the state corresponding to snapshot disk layer, is deemed impassable,
|
||||
// then block number zero is returned, indicating that snapshot recovery is disabled
|
||||
// and the whole snapshot should be auto-generated in case of head mismatch.
|
||||
func (bc *BlockChain) rewindHead(head *types.Header, root common.Hash) (*types.Header, uint64) {
|
||||
if bc.triedb.Scheme() == rawdb.PathScheme && !bc.NoTries() {
|
||||
return bc.rewindPathHead(head, root)
|
||||
}
|
||||
return bc.rewindHashHead(head, root)
|
||||
}
|
||||
|
||||
// SetFinalized sets the finalized block.
|
||||
// This function differs slightly from Ethereum; we fine-tune it through the outer-layer setting finalizedBlockGauge.
|
||||
func (bc *BlockChain) SetFinalized(header *types.Header) {
|
||||
bc.currentFinalBlock.Store(header)
|
||||
if header != nil {
|
||||
rawdb.WriteFinalizedBlockHash(bc.db.BlockStore(), header.Hash())
|
||||
} else {
|
||||
rawdb.WriteFinalizedBlockHash(bc.db.BlockStore(), common.Hash{})
|
||||
}
|
||||
}
|
||||
|
||||
// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
|
||||
// that the rewind must pass the specified state root. This method is meant to be
|
||||
// used when rewinding with snapshots enabled to ensure that we go back further than
|
||||
@@ -841,82 +1033,53 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||
}
|
||||
defer bc.chainmu.Unlock()
|
||||
|
||||
// Track the block number of the requested root hash
|
||||
var rootNumber uint64 // (no root == always 0)
|
||||
|
||||
// Retrieve the last pivot block to short circuit rollbacks beyond it and the
|
||||
// current freezer limit to start nuking id underflown
|
||||
pivot := rawdb.ReadLastPivotNumber(bc.db)
|
||||
frozen, _ := bc.db.Ancients()
|
||||
var (
|
||||
// Track the block number of the requested root hash
|
||||
rootNumber uint64 // (no root == always 0)
|
||||
|
||||
// Retrieve the last pivot block to short circuit rollbacks beyond it
|
||||
// and the current freezer limit to start nuking it's underflown.
|
||||
pivot = rawdb.ReadLastPivotNumber(bc.db)
|
||||
)
|
||||
updateFn := func(db ethdb.KeyValueWriter, header *types.Header) (*types.Header, bool) {
|
||||
// Rewind the blockchain, ensuring we don't end up with a stateless head
|
||||
// block. Note, depth equality is permitted to allow using SetHead as a
|
||||
// chain reparation mechanism without deleting any data!
|
||||
if currentBlock := bc.CurrentBlock(); currentBlock != nil && header.Number.Uint64() <= currentBlock.Number.Uint64() {
|
||||
newHeadBlock := bc.GetBlock(header.Hash(), header.Number.Uint64())
|
||||
if newHeadBlock == nil {
|
||||
log.Error("Gap in the chain, rewinding to genesis", "number", header.Number, "hash", header.Hash())
|
||||
newHeadBlock = bc.genesisBlock
|
||||
} else {
|
||||
// Block exists. Keep rewinding until either we find one with state
|
||||
// or until we exceed the optional threshold root hash
|
||||
beyondRoot := (root == common.Hash{}) // Flag whether we're beyond the requested root (no root, always true)
|
||||
|
||||
for {
|
||||
// If a root threshold was requested but not yet crossed, check
|
||||
if root != (common.Hash{}) && !beyondRoot && newHeadBlock.Root() == root {
|
||||
beyondRoot, rootNumber = true, newHeadBlock.NumberU64()
|
||||
// load bc.snaps for the judge `HasState`
|
||||
if bc.NoTries() {
|
||||
if bc.cacheConfig.SnapshotLimit > 0 {
|
||||
snapconfig := snapshot.Config{
|
||||
CacheSize: bc.cacheConfig.SnapshotLimit,
|
||||
NoBuild: bc.cacheConfig.SnapshotNoBuild,
|
||||
AsyncBuild: !bc.cacheConfig.SnapshotWait,
|
||||
}
|
||||
if !bc.HasState(newHeadBlock.Root()) && !bc.stateRecoverable(newHeadBlock.Root()) {
|
||||
log.Trace("Block state missing, rewinding further", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
||||
if pivot == nil || newHeadBlock.NumberU64() > *pivot {
|
||||
parent := bc.GetBlock(newHeadBlock.ParentHash(), newHeadBlock.NumberU64()-1)
|
||||
if parent != nil {
|
||||
newHeadBlock = parent
|
||||
continue
|
||||
}
|
||||
log.Error("Missing block in the middle, aiming genesis", "number", newHeadBlock.NumberU64()-1, "hash", newHeadBlock.ParentHash())
|
||||
newHeadBlock = bc.genesisBlock
|
||||
} else {
|
||||
log.Info("Rewind passed pivot, aiming genesis", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "pivot", *pivot)
|
||||
newHeadBlock = bc.genesisBlock
|
||||
}
|
||||
}
|
||||
if beyondRoot || newHeadBlock.NumberU64() == 0 {
|
||||
if !bc.HasState(newHeadBlock.Root()) && bc.stateRecoverable(newHeadBlock.Root()) {
|
||||
// Rewind to a block with recoverable state. If the state is
|
||||
// missing, run the state recovery here.
|
||||
if err := bc.triedb.Recover(newHeadBlock.Root()); err != nil {
|
||||
log.Crit("Failed to rollback state", "err", err) // Shouldn't happen
|
||||
}
|
||||
log.Debug("Rewound to block with state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
||||
}
|
||||
log.Info("Rewound to block with state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
|
||||
break
|
||||
}
|
||||
log.Debug("Skipping block with threshold state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "root", newHeadBlock.Root())
|
||||
newHeadBlock = bc.GetBlock(newHeadBlock.ParentHash(), newHeadBlock.NumberU64()-1) // Keep rewinding
|
||||
bc.snaps, _ = snapshot.New(snapconfig, bc.db, bc.triedb, header.Root, int(bc.cacheConfig.TriesInMemory), bc.NoTries())
|
||||
}
|
||||
defer func() { bc.snaps = nil }()
|
||||
}
|
||||
|
||||
var newHeadBlock *types.Header
|
||||
newHeadBlock, rootNumber = bc.rewindHead(header, root)
|
||||
rawdb.WriteHeadBlockHash(db, newHeadBlock.Hash())
|
||||
|
||||
// Degrade the chain markers if they are explicitly reverted.
|
||||
// In theory we should update all in-memory markers in the
|
||||
// last step, however the direction of SetHead is from high
|
||||
// to low, so it's safe to update in-memory markers directly.
|
||||
bc.currentBlock.Store(newHeadBlock.Header())
|
||||
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
|
||||
justifiedBlockGauge.Update(int64(bc.GetJustifiedNumber(newHeadBlock.Header())))
|
||||
finalizedBlockGauge.Update(int64(bc.getFinalizedNumber(newHeadBlock.Header())))
|
||||
bc.currentBlock.Store(newHeadBlock)
|
||||
headBlockGauge.Update(int64(newHeadBlock.Number.Uint64()))
|
||||
|
||||
// The head state is missing, which is only possible in the path-based
|
||||
// scheme. This situation occurs when the chain head is rewound below
|
||||
// the pivot point. In this scenario, there is no possible recovery
|
||||
// approach except for rerunning a snap sync. Do nothing here until the
|
||||
// state syncer picks it up.
|
||||
if !bc.HasState(newHeadBlock.Root()) {
|
||||
log.Info("Chain is stateless, wait state sync", "number", newHeadBlock.Number(), "hash", newHeadBlock.Hash())
|
||||
if !bc.HasState(newHeadBlock.Root) {
|
||||
if newHeadBlock.Number.Uint64() != 0 {
|
||||
log.Crit("Chain is stateless at a non-genesis block")
|
||||
}
|
||||
log.Info("Chain is stateless, wait state sync", "number", newHeadBlock.Number, "hash", newHeadBlock.Hash())
|
||||
}
|
||||
}
|
||||
// Rewind the snap block in a simpleton way to the target head
|
||||
@@ -943,6 +1106,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||
// intent afterwards is full block importing, delete the chain segment
|
||||
// between the stateful-block and the sethead target.
|
||||
var wipe bool
|
||||
frozen, _ := bc.db.Ancients()
|
||||
if headNumber+1 < frozen {
|
||||
wipe = pivot == nil || headNumber >= *pivot
|
||||
}
|
||||
@@ -965,6 +1129,7 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||
// The header, total difficulty and canonical hash will be
|
||||
// removed in the hc.SetHead function.
|
||||
rawdb.DeleteBody(db, hash, num)
|
||||
rawdb.DeleteBlobSidecars(db, hash, num)
|
||||
rawdb.DeleteReceipts(db, hash, num)
|
||||
}
|
||||
// Todo(rjl493456442) txlookup, bloombits, etc
|
||||
@@ -990,10 +1155,16 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, time uint64, root common.Ha
|
||||
bc.bodyCache.Purge()
|
||||
bc.bodyRLPCache.Purge()
|
||||
bc.receiptsCache.Purge()
|
||||
bc.sidecarsCache.Purge()
|
||||
bc.blockCache.Purge()
|
||||
bc.txLookupCache.Purge()
|
||||
bc.futureBlocks.Purge()
|
||||
|
||||
if finalized := bc.CurrentFinalBlock(); finalized != nil && head < finalized.Number.Uint64() {
|
||||
log.Error("SetHead invalidated finalized block")
|
||||
bc.SetFinalized(nil)
|
||||
}
|
||||
|
||||
return rootNumber, bc.loadLastState()
|
||||
}
|
||||
|
||||
@@ -1034,6 +1205,16 @@ func (bc *BlockChain) SnapSyncCommitHead(hash common.Hash) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateChasingHead update remote best chain head, used by DA check now.
|
||||
func (bc *BlockChain) UpdateChasingHead(head *types.Header) {
|
||||
bc.chasingHead.Store(head)
|
||||
}
|
||||
|
||||
// ChasingHead return the best chain head of peers.
|
||||
func (bc *BlockChain) ChasingHead() *types.Header {
|
||||
return bc.chasingHead.Load()
|
||||
}
|
||||
|
||||
// Reset purges the entire blockchain, restoring it to its genesis state.
|
||||
func (bc *BlockChain) Reset() error {
|
||||
return bc.ResetWithGenesisBlock(bc.genesisBlock)
|
||||
@@ -1052,10 +1233,10 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
|
||||
defer bc.chainmu.Unlock()
|
||||
|
||||
// Prepare the genesis block and reinitialise the chain
|
||||
batch := bc.db.NewBatch()
|
||||
rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty())
|
||||
rawdb.WriteBlock(batch, genesis)
|
||||
if err := batch.Write(); err != nil {
|
||||
blockBatch := bc.db.BlockStore().NewBatch()
|
||||
rawdb.WriteTd(blockBatch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty())
|
||||
rawdb.WriteBlock(blockBatch, genesis)
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
log.Crit("Failed to write genesis block", "err", err)
|
||||
}
|
||||
bc.writeHeadBlock(genesis)
|
||||
@@ -1118,12 +1299,13 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
||||
// Note, this function assumes that the `mu` mutex is held!
|
||||
func (bc *BlockChain) writeHeadBlock(block *types.Block) {
|
||||
// Add the block to the canonical chain number scheme and mark as the head
|
||||
rawdb.WriteCanonicalHash(bc.db.BlockStore(), block.Hash(), block.NumberU64())
|
||||
rawdb.WriteHeadHeaderHash(bc.db.BlockStore(), block.Hash())
|
||||
rawdb.WriteHeadBlockHash(bc.db.BlockStore(), block.Hash())
|
||||
|
||||
batch := bc.db.NewBatch()
|
||||
rawdb.WriteHeadHeaderHash(batch, block.Hash())
|
||||
rawdb.WriteHeadFastBlockHash(batch, block.Hash())
|
||||
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
|
||||
rawdb.WriteTxLookupEntriesByBlock(batch, block)
|
||||
rawdb.WriteHeadBlockHash(batch, block.Hash())
|
||||
|
||||
// Flush the whole batch into the disk, exit the node if failed
|
||||
if err := batch.Write(); err != nil {
|
||||
@@ -1209,7 +1391,7 @@ func (bc *BlockChain) Stop() {
|
||||
} else {
|
||||
rawdb.WriteSafePointBlockNumber(bc.db, recent.NumberU64())
|
||||
once.Do(func() {
|
||||
rawdb.WriteHeadBlockHash(bc.db, recent.Hash())
|
||||
rawdb.WriteHeadBlockHash(bc.db.BlockStore(), recent.Hash())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1317,6 +1499,15 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
}
|
||||
}
|
||||
|
||||
// check DA after cancun
|
||||
lastBlk := blockChain[len(blockChain)-1]
|
||||
if bc.chainConfig.Parlia != nil && bc.chainConfig.IsCancun(lastBlk.Number(), lastBlk.Time()) {
|
||||
if _, err := CheckDataAvailableInBatch(bc, blockChain); err != nil {
|
||||
log.Debug("CheckDataAvailableInBatch", "err", err)
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
stats = struct{ processed, ignored int32 }{}
|
||||
start = time.Now()
|
||||
@@ -1377,7 +1568,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
|
||||
// Write all chain data to ancients.
|
||||
td := bc.GetTd(first.Hash(), first.NumberU64())
|
||||
writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain, td)
|
||||
writeSize, err := rawdb.WriteAncientBlocksWithBlobs(bc.db, blockChain, receiptChain, td)
|
||||
if err != nil {
|
||||
log.Error("Error importing chain data to ancients", "err", err)
|
||||
return 0, err
|
||||
@@ -1401,24 +1592,24 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
|
||||
// Delete block data from the main database.
|
||||
var (
|
||||
batch = bc.db.NewBatch()
|
||||
canonHashes = make(map[common.Hash]struct{})
|
||||
blockBatch = bc.db.BlockStore().NewBatch()
|
||||
)
|
||||
for _, block := range blockChain {
|
||||
canonHashes[block.Hash()] = struct{}{}
|
||||
if block.NumberU64() == 0 {
|
||||
continue
|
||||
}
|
||||
rawdb.DeleteCanonicalHash(batch, block.NumberU64())
|
||||
rawdb.DeleteBlockWithoutNumber(batch, block.Hash(), block.NumberU64())
|
||||
rawdb.DeleteCanonicalHash(blockBatch, block.NumberU64())
|
||||
rawdb.DeleteBlockWithoutNumber(blockBatch, block.Hash(), block.NumberU64())
|
||||
}
|
||||
// Delete side chain hash-to-number mappings.
|
||||
for _, nh := range rawdb.ReadAllHashesInRange(bc.db, first.NumberU64(), last.NumberU64()) {
|
||||
if _, canon := canonHashes[nh.Hash]; !canon {
|
||||
rawdb.DeleteHeader(batch, nh.Hash, nh.Number)
|
||||
rawdb.DeleteHeader(blockBatch, nh.Hash, nh.Number)
|
||||
}
|
||||
}
|
||||
if err := batch.Write(); err != nil {
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
stats.processed += int32(len(blockChain))
|
||||
@@ -1430,6 +1621,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
var (
|
||||
skipPresenceCheck = false
|
||||
batch = bc.db.NewBatch()
|
||||
blockBatch = bc.db.BlockStore().NewBatch()
|
||||
)
|
||||
for i, block := range blockChain {
|
||||
// Short circuit insertion if shutting down or processing failed
|
||||
@@ -1453,8 +1645,11 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
}
|
||||
}
|
||||
// Write all the data out into the database
|
||||
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
|
||||
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
|
||||
rawdb.WriteBody(blockBatch, block.Hash(), block.NumberU64(), block.Body())
|
||||
rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receiptChain[i])
|
||||
if bc.chainConfig.IsCancun(block.Number(), block.Time()) {
|
||||
rawdb.WriteBlobSidecars(blockBatch, block.Hash(), block.NumberU64(), block.Sidecars())
|
||||
}
|
||||
|
||||
// Write everything belongs to the blocks into the database. So that
|
||||
// we can ensure all components of body is completed(body, receipts)
|
||||
@@ -1466,6 +1661,13 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
size += int64(batch.ValueSize())
|
||||
batch.Reset()
|
||||
}
|
||||
if blockBatch.ValueSize() >= ethdb.IdealBatchSize {
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
size += int64(blockBatch.ValueSize())
|
||||
blockBatch.Reset()
|
||||
}
|
||||
stats.processed++
|
||||
}
|
||||
// Write everything belongs to the blocks into the database. So that
|
||||
@@ -1477,6 +1679,12 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if blockBatch.ValueSize() > 0 {
|
||||
size += int64(blockBatch.ValueSize())
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
updateHead(blockChain[len(blockChain)-1])
|
||||
return 0, nil
|
||||
}
|
||||
@@ -1521,10 +1729,14 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (e
|
||||
if bc.insertStopped() {
|
||||
return errInsertionInterrupted
|
||||
}
|
||||
batch := bc.db.NewBatch()
|
||||
rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td)
|
||||
rawdb.WriteBlock(batch, block)
|
||||
if err := batch.Write(); err != nil {
|
||||
blockBatch := bc.db.BlockStore().NewBatch()
|
||||
rawdb.WriteTd(blockBatch, block.Hash(), block.NumberU64(), td)
|
||||
rawdb.WriteBlock(blockBatch, block)
|
||||
// if cancun is enabled, here need to write sidecars too
|
||||
if bc.chainConfig.IsCancun(block.Number(), block.Time()) {
|
||||
rawdb.WriteBlobSidecars(blockBatch, block.Hash(), block.NumberU64(), block.Sidecars())
|
||||
}
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
log.Crit("Failed to write block into disk", "err", err)
|
||||
}
|
||||
return nil
|
||||
@@ -1562,10 +1774,15 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
blockBatch := bc.db.NewBatch()
|
||||
rawdb.WritePreimages(bc.db, state.Preimages())
|
||||
blockBatch := bc.db.BlockStore().NewBatch()
|
||||
rawdb.WriteTd(blockBatch, block.Hash(), block.NumberU64(), externTd)
|
||||
rawdb.WriteBlock(blockBatch, block)
|
||||
rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts)
|
||||
// if cancun is enabled, here need to write sidecars too
|
||||
if bc.chainConfig.IsCancun(block.Number(), block.Time()) {
|
||||
rawdb.WriteBlobSidecars(blockBatch, block.Hash(), block.NumberU64(), block.Sidecars())
|
||||
}
|
||||
rawdb.WritePreimages(blockBatch, state.Preimages())
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
log.Crit("Failed to write block into disk", "err", err)
|
||||
@@ -1727,12 +1944,16 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types
|
||||
// canonical blocks. Avoid firing too many ChainHeadEvents,
|
||||
// we will fire an accumulated ChainHeadEvent and disable fire
|
||||
// event here.
|
||||
var finalizedHeader *types.Header
|
||||
if posa, ok := bc.Engine().(consensus.PoSA); ok {
|
||||
if finalizedHeader = posa.GetFinalizedHeader(bc, block.Header()); finalizedHeader != nil {
|
||||
bc.SetFinalized(finalizedHeader)
|
||||
}
|
||||
}
|
||||
if emitHeadEvent {
|
||||
bc.chainHeadFeed.Send(ChainHeadEvent{Block: block})
|
||||
if posa, ok := bc.Engine().(consensus.PoSA); ok {
|
||||
if finalizedHeader := posa.GetFinalizedHeader(bc, block.Header()); finalizedHeader != nil {
|
||||
bc.finalizedHeaderFeed.Send(FinalizedHeaderEvent{finalizedHeader})
|
||||
}
|
||||
if finalizedHeader != nil {
|
||||
bc.finalizedHeaderFeed.Send(FinalizedHeaderEvent{finalizedHeader})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1828,6 +2049,14 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// check block data available first
|
||||
if bc.chainConfig.Parlia != nil {
|
||||
if index, err := CheckDataAvailableInBatch(bc, chain); err != nil {
|
||||
return index, err
|
||||
}
|
||||
}
|
||||
|
||||
// Start the parallel header verifier
|
||||
headers := make([]*types.Header, len(chain))
|
||||
for i, block := range chain {
|
||||
@@ -1963,7 +2192,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||
// state, but if it's this special case here(skip reexecution) we will lose
|
||||
// the empty receipt entry.
|
||||
if len(block.Transactions()) == 0 {
|
||||
rawdb.WriteReceipts(bc.db, block.Hash(), block.NumberU64(), nil)
|
||||
rawdb.WriteReceipts(bc.db.BlockStore(), block.Hash(), block.NumberU64(), nil)
|
||||
} else {
|
||||
log.Error("Please file an issue, skip known block execution without receipt",
|
||||
"hash", block.Hash(), "number", block.NumberU64())
|
||||
@@ -1985,6 +2214,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||
if parent == nil {
|
||||
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
|
||||
}
|
||||
|
||||
statedb, err := state.NewWithSharedPool(parent.Root, bc.stateCache, bc.snaps)
|
||||
if err != nil {
|
||||
return it.index, err
|
||||
@@ -2008,7 +2238,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||
go throwaway.TriePrefetchInAdvance(block, signer)
|
||||
}
|
||||
|
||||
//Process block using the parent state as reference point
|
||||
// Process block using the parent state as reference point
|
||||
if bc.pipeCommit {
|
||||
statedb.EnablePipeCommit()
|
||||
}
|
||||
@@ -2087,7 +2317,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
|
||||
snapDiffItems, snapBufItems, _ = bc.snaps.Size()
|
||||
}
|
||||
trieDiffNodes, trieBufNodes, trieImmutableBufNodes, _ := bc.triedb.Size()
|
||||
stats.report(chain, it.index, snapDiffItems, snapBufItems, trieDiffNodes, trieBufNodes, trieImmutableBufNodes, setHead)
|
||||
stats.report(chain, it.index, snapDiffItems, snapBufItems, trieDiffNodes, trieBufNodes, trieImmutableBufNodes, status == CanonStatTy)
|
||||
|
||||
if !setHead {
|
||||
// After merge we expect few side chains. Simply count
|
||||
@@ -2286,7 +2516,9 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
|
||||
if block == nil {
|
||||
log.Crit("Importing heavy sidechain block is nil", "hash", hashes[i], "number", numbers[i])
|
||||
}
|
||||
|
||||
if bc.chainConfig.IsCancun(block.Number(), block.Time()) {
|
||||
block = block.WithSidecars(bc.GetSidecarsByHash(hashes[i]))
|
||||
}
|
||||
blocks = append(blocks, block)
|
||||
memory += block.Size()
|
||||
|
||||
@@ -2358,6 +2590,9 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error)
|
||||
} else {
|
||||
b = bc.GetBlock(hashes[i], numbers[i])
|
||||
}
|
||||
if bc.chainConfig.IsCancun(b.Number(), b.Time()) {
|
||||
b = b.WithSidecars(bc.GetSidecarsByHash(b.Hash()))
|
||||
}
|
||||
if _, err := bc.insertChain(types.Blocks{b}, false); err != nil {
|
||||
return b.ParentHash(), err
|
||||
}
|
||||
@@ -2502,6 +2737,7 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error {
|
||||
var (
|
||||
indexesBatch = bc.db.NewBatch()
|
||||
diffs = types.HashDifference(deletedTxs, addedTxs)
|
||||
blockBatch = bc.db.BlockStore().NewBatch()
|
||||
)
|
||||
for _, tx := range diffs {
|
||||
rawdb.DeleteTxLookupEntry(indexesBatch, tx)
|
||||
@@ -2518,11 +2754,14 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error {
|
||||
if hash == (common.Hash{}) {
|
||||
break
|
||||
}
|
||||
rawdb.DeleteCanonicalHash(indexesBatch, i)
|
||||
rawdb.DeleteCanonicalHash(blockBatch, i)
|
||||
}
|
||||
if err := indexesBatch.Write(); err != nil {
|
||||
log.Crit("Failed to delete useless indexes", "err", err)
|
||||
}
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
log.Crit("Failed to delete useless indexes use block batch", "err", err)
|
||||
}
|
||||
|
||||
// Send out events for logs from the old canon chain, and 'reborn'
|
||||
// logs from the new canon chain. The number of logs can be very
|
||||
@@ -2804,6 +3043,7 @@ func (bc *BlockChain) isCachedBadBlock(block *types.Block) bool {
|
||||
}
|
||||
|
||||
// reportBlock logs a bad block error.
|
||||
// bad block need not save receipts & sidecars.
|
||||
func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
|
||||
rawdb.WriteBadBlock(bc.db, block)
|
||||
log.Error(summarizeBadBlock(block, receipts, bc.Config(), err))
|
||||
|
||||
@@ -48,18 +48,23 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
|
||||
// If we're at the last block of the batch or report period reached, log
|
||||
if index == len(chain)-1 || elapsed >= statsReportLimit {
|
||||
// Count the number of transactions in this segment
|
||||
var txs int
|
||||
var txs, blobs int
|
||||
for _, block := range chain[st.lastIndex : index+1] {
|
||||
txs += len(block.Transactions())
|
||||
for _, sidecar := range block.Sidecars() {
|
||||
blobs += len(sidecar.Blobs)
|
||||
}
|
||||
}
|
||||
end := chain[index]
|
||||
|
||||
// Assemble the log context and send it to the logger
|
||||
mgasps := float64(st.usedGas) * 1000 / float64(elapsed)
|
||||
context := []interface{}{
|
||||
"number", end.Number(), "hash", end.Hash(), "miner", end.Coinbase(),
|
||||
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
|
||||
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
|
||||
"blocks", st.processed, "txs", txs, "blobs", blobs, "mgas", float64(st.usedGas) / 1000000,
|
||||
"elapsed", common.PrettyDuration(elapsed), "mgasps", mgasps,
|
||||
}
|
||||
blockInsertMgaspsGauge.Update(int64(mgasps))
|
||||
if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute {
|
||||
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||
if receipts, ok := bc.receiptsCache.Get(hash); ok {
|
||||
return receipts
|
||||
}
|
||||
number := rawdb.ReadHeaderNumber(bc.db, hash)
|
||||
number := rawdb.ReadHeaderNumber(bc.db.BlockStore(), hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -247,6 +247,23 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||
return receipts
|
||||
}
|
||||
|
||||
// GetSidecarsByHash retrieves the sidecars for all transactions in a given block.
|
||||
func (bc *BlockChain) GetSidecarsByHash(hash common.Hash) types.BlobSidecars {
|
||||
if sidecars, ok := bc.sidecarsCache.Get(hash); ok {
|
||||
return sidecars
|
||||
}
|
||||
number := rawdb.ReadHeaderNumber(bc.db, hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
sidecars := rawdb.ReadBlobSidecars(bc.db, hash, *number)
|
||||
if sidecars == nil {
|
||||
return nil
|
||||
}
|
||||
bc.sidecarsCache.Add(hash, sidecars)
|
||||
return sidecars
|
||||
}
|
||||
|
||||
// GetUnclesInChain retrieves all the uncles from a given block backwards until
|
||||
// a specific distance is reached.
|
||||
func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
|
||||
@@ -379,7 +396,20 @@ func (bc *BlockChain) State() (*state.StateDB, error) {
|
||||
|
||||
// StateAt returns a new mutable state based on a particular point in time.
|
||||
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
||||
return state.New(root, bc.stateCache, bc.snaps)
|
||||
stateDb, err := state.New(root, bc.stateCache, bc.snaps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If there's no trie and the specified snapshot is not available, getting
|
||||
// any state will by default return nil.
|
||||
// Instead of that, it will be more useful to return an error to indicate
|
||||
// the state is not available.
|
||||
if stateDb.NoTrie() && stateDb.GetSnap() == nil {
|
||||
return nil, errors.New("state is not available")
|
||||
}
|
||||
|
||||
return stateDb, err
|
||||
}
|
||||
|
||||
// Config retrieves the chain's fork configuration.
|
||||
@@ -481,3 +511,12 @@ func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscr
|
||||
func (bc *BlockChain) SubscribeFinalizedHeaderEvent(ch chan<- FinalizedHeaderEvent) event.Subscription {
|
||||
return bc.scope.Track(bc.finalizedHeaderFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// AncientTail retrieves the tail the ancients blocks
|
||||
func (bc *BlockChain) AncientTail() (uint64, error) {
|
||||
tail, err := bc.db.Tail()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return tail, nil
|
||||
}
|
||||
|
||||
@@ -1832,10 +1832,14 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||
}
|
||||
// Force run a freeze cycle
|
||||
type freezer interface {
|
||||
Freeze(threshold uint64) error
|
||||
Freeze() error
|
||||
Ancients() (uint64, error)
|
||||
}
|
||||
db.(freezer).Freeze(tt.freezeThreshold)
|
||||
if tt.freezeThreshold < uint64(tt.canonicalBlocks) {
|
||||
final := uint64(tt.canonicalBlocks) - tt.freezeThreshold
|
||||
chain.SetFinalized(canonblocks[int(final)-1].Header())
|
||||
}
|
||||
db.(freezer).Freeze()
|
||||
|
||||
// Set the simulated pivot block
|
||||
if tt.pivotBlock != nil {
|
||||
|
||||
@@ -2045,10 +2045,14 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||
|
||||
// Force run a freeze cycle
|
||||
type freezer interface {
|
||||
Freeze(threshold uint64) error
|
||||
Freeze() error
|
||||
Ancients() (uint64, error)
|
||||
}
|
||||
db.(freezer).Freeze(tt.freezeThreshold)
|
||||
if tt.freezeThreshold < uint64(tt.canonicalBlocks) {
|
||||
final := uint64(tt.canonicalBlocks) - tt.freezeThreshold
|
||||
chain.SetFinalized(canonblocks[int(final)-1].Header())
|
||||
}
|
||||
db.(freezer).Freeze()
|
||||
|
||||
// Set the simulated pivot block
|
||||
if tt.pivotBlock != nil {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
@@ -26,6 +27,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
@@ -3585,10 +3590,20 @@ func TestEIP2718TransitionWithTestChainConfig(t *testing.T) {
|
||||
testEIP2718TransitionWithConfig(t, rawdb.HashScheme, params.TestChainConfig)
|
||||
}
|
||||
|
||||
func preShanghaiConfig() *params.ChainConfig {
|
||||
config := *params.ParliaTestChainConfig
|
||||
config.ShanghaiTime = nil
|
||||
config.KeplerTime = nil
|
||||
config.FeynmanTime = nil
|
||||
config.FeynmanFixTime = nil
|
||||
config.CancunTime = nil
|
||||
return &config
|
||||
}
|
||||
|
||||
// TestEIP2718TransitionWithParliaConfig tests EIP-2718 with Parlia Config.
|
||||
func TestEIP2718TransitionWithParliaConfig(t *testing.T) {
|
||||
testEIP2718TransitionWithConfig(t, rawdb.HashScheme, params.ParliaTestChainConfig)
|
||||
testEIP2718TransitionWithConfig(t, rawdb.PathScheme, params.ParliaTestChainConfig)
|
||||
testEIP2718TransitionWithConfig(t, rawdb.HashScheme, preShanghaiConfig())
|
||||
testEIP2718TransitionWithConfig(t, rawdb.PathScheme, preShanghaiConfig())
|
||||
}
|
||||
|
||||
// testEIP2718TransitionWithConfig tests EIP02718 with given ChainConfig.
|
||||
@@ -4418,3 +4433,134 @@ func TestEIP3651(t *testing.T) {
|
||||
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
type mockParlia struct {
|
||||
consensus.Engine
|
||||
}
|
||||
|
||||
func (c *mockParlia) Author(header *types.Header) (common.Address, error) {
|
||||
return header.Coinbase, nil
|
||||
}
|
||||
|
||||
func (c *mockParlia) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *mockParlia) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *mockParlia) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) {
|
||||
abort := make(chan<- struct{})
|
||||
results := make(chan error, len(headers))
|
||||
for i := 0; i < len(headers); i++ {
|
||||
results <- nil
|
||||
}
|
||||
return abort, results
|
||||
}
|
||||
|
||||
func (c *mockParlia) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, _ *[]*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal,
|
||||
_ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *mockParlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
|
||||
uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, []*types.Receipt, error) {
|
||||
// Finalize block
|
||||
c.Finalize(chain, header, state, &txs, uncles, nil, nil, nil, nil)
|
||||
|
||||
// Assign the final state root to header.
|
||||
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||
|
||||
// Header seems complete, assemble into a block and return
|
||||
return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), receipts, nil
|
||||
}
|
||||
|
||||
func (c *mockParlia) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
|
||||
return big.NewInt(1)
|
||||
}
|
||||
|
||||
func TestParliaBlobFeeReward(t *testing.T) {
|
||||
// Have N headers in the freezer
|
||||
frdir := t.TempDir()
|
||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create database with ancient backend")
|
||||
}
|
||||
config := params.ParliaTestChainConfig
|
||||
gspec := &Genesis{
|
||||
Config: config,
|
||||
Alloc: types.GenesisAlloc{testAddr: {Balance: new(big.Int).SetUint64(10 * params.Ether)}},
|
||||
}
|
||||
engine := &mockParlia{}
|
||||
chain, _ := NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
signer := types.LatestSigner(config)
|
||||
|
||||
_, bs, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, gen *BlockGen) {
|
||||
tx, _ := makeMockTx(config, signer, testKey, gen.TxNonce(testAddr), gen.BaseFee().Uint64(), eip4844.CalcBlobFee(gen.ExcessBlobGas()).Uint64(), false)
|
||||
gen.AddTxWithChain(chain, tx)
|
||||
tx, sidecar := makeMockTx(config, signer, testKey, gen.TxNonce(testAddr), gen.BaseFee().Uint64(), eip4844.CalcBlobFee(gen.ExcessBlobGas()).Uint64(), true)
|
||||
gen.AddTxWithChain(chain, tx)
|
||||
gen.AddBlobSidecar(&types.BlobSidecar{
|
||||
BlobTxSidecar: *sidecar,
|
||||
TxIndex: 1,
|
||||
TxHash: tx.Hash(),
|
||||
})
|
||||
})
|
||||
if _, err := chain.InsertChain(bs); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stateDB, err := chain.State()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
expect := new(big.Int)
|
||||
for _, block := range bs {
|
||||
receipts := chain.GetReceiptsByHash(block.Hash())
|
||||
for _, receipt := range receipts {
|
||||
if receipt.BlobGasPrice != nil {
|
||||
blob := receipt.BlobGasPrice.Mul(receipt.BlobGasPrice, new(big.Int).SetUint64(receipt.BlobGasUsed))
|
||||
expect.Add(expect, blob)
|
||||
}
|
||||
plain := receipt.EffectiveGasPrice.Mul(receipt.EffectiveGasPrice, new(big.Int).SetUint64(receipt.GasUsed))
|
||||
expect.Add(expect, plain)
|
||||
}
|
||||
}
|
||||
actual := stateDB.GetBalance(params.SystemAddress)
|
||||
require.Equal(t, expect.Uint64(), actual.Uint64())
|
||||
}
|
||||
|
||||
func makeMockTx(config *params.ChainConfig, signer types.Signer, key *ecdsa.PrivateKey, nonce uint64, baseFee uint64, blobBaseFee uint64, isBlobTx bool) (*types.Transaction, *types.BlobTxSidecar) {
|
||||
if !isBlobTx {
|
||||
raw := &types.DynamicFeeTx{
|
||||
ChainID: config.ChainID,
|
||||
Nonce: nonce,
|
||||
GasTipCap: big.NewInt(10),
|
||||
GasFeeCap: new(big.Int).SetUint64(baseFee + 10),
|
||||
Gas: params.TxGas,
|
||||
To: &common.Address{0x00},
|
||||
Value: big.NewInt(0),
|
||||
}
|
||||
tx, _ := types.SignTx(types.NewTx(raw), signer, key)
|
||||
return tx, nil
|
||||
}
|
||||
sidecar := &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof},
|
||||
}
|
||||
raw := &types.BlobTx{
|
||||
ChainID: uint256.MustFromBig(config.ChainID),
|
||||
Nonce: nonce,
|
||||
GasTipCap: uint256.NewInt(10),
|
||||
GasFeeCap: uint256.NewInt(baseFee + 10),
|
||||
Gas: params.TxGas,
|
||||
To: common.Address{0x00},
|
||||
Value: uint256.NewInt(0),
|
||||
BlobFeeCap: uint256.NewInt(blobBaseFee),
|
||||
BlobHashes: sidecar.BlobHashes(),
|
||||
}
|
||||
tx, _ := types.SignTx(types.NewTx(raw), signer, key)
|
||||
return tx, sidecar
|
||||
}
|
||||
|
||||
@@ -227,8 +227,8 @@ func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainH
|
||||
// Reorg to the common ancestor if needed (might not exist in light sync mode, skip reorg then)
|
||||
// TODO(karalabe, zsfelfoldi): This seems a bit brittle, can we detect this case explicitly?
|
||||
|
||||
if rawdb.ReadCanonicalHash(c.chainDb, prevHeader.Number.Uint64()) != prevHash {
|
||||
if h := rawdb.FindCommonAncestor(c.chainDb, prevHeader, header); h != nil {
|
||||
if rawdb.ReadCanonicalHash(c.chainDb.BlockStore(), prevHeader.Number.Uint64()) != prevHash {
|
||||
if h := rawdb.FindCommonAncestor(c.chainDb.BlockStore(), prevHeader, header); h != nil {
|
||||
c.newHead(h.Number.Uint64(), true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,9 @@ type BlockGen struct {
|
||||
withdrawals []*types.Withdrawal
|
||||
|
||||
engine consensus.Engine
|
||||
|
||||
// extra data of block
|
||||
sidecars types.BlobSidecars
|
||||
}
|
||||
|
||||
// SetCoinbase sets the coinbase of the generated block.
|
||||
@@ -171,6 +174,11 @@ func (b *BlockGen) AddUncheckedTx(tx *types.Transaction) {
|
||||
b.txs = append(b.txs, tx)
|
||||
}
|
||||
|
||||
// AddBlobSidecar add block's blob sidecar for DA checking.
|
||||
func (b *BlockGen) AddBlobSidecar(sidecar *types.BlobSidecar) {
|
||||
b.sidecars = append(b.sidecars, sidecar)
|
||||
}
|
||||
|
||||
// Number returns the block number of the block being generated.
|
||||
func (b *BlockGen) Number() *big.Int {
|
||||
return new(big.Int).Set(b.header.Number)
|
||||
@@ -186,6 +194,15 @@ func (b *BlockGen) BaseFee() *big.Int {
|
||||
return new(big.Int).Set(b.header.BaseFee)
|
||||
}
|
||||
|
||||
// ExcessBlobGas returns the EIP-4844 ExcessBlobGas of the block.
|
||||
func (b *BlockGen) ExcessBlobGas() uint64 {
|
||||
excessBlobGas := b.header.ExcessBlobGas
|
||||
if excessBlobGas == nil {
|
||||
return 0
|
||||
}
|
||||
return *excessBlobGas
|
||||
}
|
||||
|
||||
// Gas returns the amount of gas left in the current block.
|
||||
func (b *BlockGen) Gas() uint64 {
|
||||
return b.header.GasLimit - b.header.GasUsed
|
||||
@@ -355,6 +372,16 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if block.Header().EmptyWithdrawalsHash() {
|
||||
block = block.WithWithdrawals(make([]*types.Withdrawal, 0))
|
||||
}
|
||||
if config.IsCancun(block.Number(), block.Time()) {
|
||||
for _, s := range b.sidecars {
|
||||
s.BlockNumber = block.Number()
|
||||
s.BlockHash = block.Hash()
|
||||
}
|
||||
block = block.WithSidecars(b.sidecars)
|
||||
}
|
||||
|
||||
// Write state changes to db
|
||||
root, _, err := statedb.Commit(b.header.Number.Uint64(), nil)
|
||||
@@ -456,6 +483,9 @@ func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engi
|
||||
excessBlobGas := eip4844.CalcExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed)
|
||||
header.ExcessBlobGas = &excessBlobGas
|
||||
header.BlobGasUsed = new(uint64)
|
||||
if cm.config.Parlia != nil {
|
||||
header.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
||||
}
|
||||
if cm.config.Parlia == nil {
|
||||
header.ParentBeaconRoot = new(common.Hash)
|
||||
}
|
||||
@@ -590,3 +620,7 @@ func (cm *chainMaker) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||
func (cm *chainMaker) GetHighestVerifiedHeader() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (cm *chainMaker) ChasingHead() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
@@ -47,8 +47,8 @@ func TestGeneratePOSChain(t *testing.T) {
|
||||
gspec = &Genesis{
|
||||
Config: &config,
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
params.BeaconRootsStorageAddress: {Balance: common.Big0, Code: asm4788},
|
||||
address: {Balance: funds},
|
||||
params.BeaconRootsAddress: {Balance: common.Big0, Code: asm4788},
|
||||
},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Difficulty: common.Big1,
|
||||
@@ -180,7 +180,7 @@ func TestGeneratePOSChain(t *testing.T) {
|
||||
}
|
||||
state, _ := blockchain.State()
|
||||
idx := block.Time()%8191 + 8191
|
||||
got := state.GetState(params.BeaconRootsStorageAddress, common.BigToHash(new(big.Int).SetUint64(idx)))
|
||||
got := state.GetState(params.BeaconRootsAddress, common.BigToHash(new(big.Int).SetUint64(idx)))
|
||||
if got != want {
|
||||
t.Fatalf("block %d, wrong parent beacon root in state: got %s, want %s", i, got, want)
|
||||
}
|
||||
|
||||
162
core/data_availability.go
Normal file
162
core/data_availability.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/gopool"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
daCheckTimer = metrics.NewRegisteredTimer("chain/dacheck", nil)
|
||||
)
|
||||
|
||||
// validateBlobSidecar it is same as validateBlobSidecar in core/txpool/validation.go
|
||||
func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobSidecar) error {
|
||||
if len(sidecar.Blobs) != len(hashes) {
|
||||
return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes))
|
||||
}
|
||||
if len(sidecar.Commitments) != len(hashes) {
|
||||
return fmt.Errorf("invalid number of %d blob commitments compared to %d blob hashes", len(sidecar.Commitments), len(hashes))
|
||||
}
|
||||
if len(sidecar.Proofs) != len(hashes) {
|
||||
return fmt.Errorf("invalid number of %d blob proofs compared to %d blob hashes", len(sidecar.Proofs), len(hashes))
|
||||
}
|
||||
// Blob quantities match up, validate that the provers match with the
|
||||
// transaction hash before getting to the cryptography
|
||||
hasher := sha256.New()
|
||||
for i, vhash := range hashes {
|
||||
computed := kzg4844.CalcBlobHashV1(hasher, &sidecar.Commitments[i])
|
||||
if vhash != computed {
|
||||
return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, computed, vhash)
|
||||
}
|
||||
}
|
||||
// Blob commitments match with the hashes in the transaction, verify the
|
||||
// blobs themselves via KZG
|
||||
for i := range sidecar.Blobs {
|
||||
if err := kzg4844.VerifyBlobProof(sidecar.Blobs[i], sidecar.Commitments[i], sidecar.Proofs[i]); err != nil {
|
||||
return fmt.Errorf("invalid blob %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsDataAvailable it checks that the blobTx block has available blob data
|
||||
func IsDataAvailable(chain consensus.ChainHeaderReader, block *types.Block) (err error) {
|
||||
defer func(start time.Time) {
|
||||
daCheckTimer.Update(time.Since(start))
|
||||
}(time.Now())
|
||||
|
||||
// refer logic in ValidateBody
|
||||
if !chain.Config().IsCancun(block.Number(), block.Time()) {
|
||||
if block.Sidecars() != nil {
|
||||
return errors.New("sidecars present in block body before cancun")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// only required to check within MinBlocksForBlobRequests block's DA
|
||||
highest := chain.ChasingHead()
|
||||
current := chain.CurrentHeader()
|
||||
if highest == nil || highest.Number.Cmp(current.Number) < 0 {
|
||||
highest = current
|
||||
}
|
||||
if block.NumberU64()+params.MinBlocksForBlobRequests < highest.Number.Uint64() {
|
||||
// if we needn't check DA of this block, just clean it
|
||||
block.CleanSidecars()
|
||||
return nil
|
||||
}
|
||||
|
||||
// if sidecar is nil, just clean it. And it will be used for saving in ancient.
|
||||
if block.Sidecars() == nil {
|
||||
block.CleanSidecars()
|
||||
}
|
||||
sidecars := block.Sidecars()
|
||||
for _, s := range sidecars {
|
||||
if err := s.SanityCheck(block.Number(), block.Hash()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// alloc block's blobTx
|
||||
blobTxs := make([]*types.Transaction, 0, len(sidecars))
|
||||
blobTxIndexes := make([]uint64, 0, len(sidecars))
|
||||
for i, tx := range block.Transactions() {
|
||||
if tx.Type() != types.BlobTxType {
|
||||
continue
|
||||
}
|
||||
blobTxs = append(blobTxs, tx)
|
||||
blobTxIndexes = append(blobTxIndexes, uint64(i))
|
||||
}
|
||||
if len(blobTxs) != len(sidecars) {
|
||||
return fmt.Errorf("blob info mismatch: sidecars %d, versionedHashes:%d", len(sidecars), len(blobTxs))
|
||||
}
|
||||
|
||||
// check blob amount
|
||||
blobCnt := 0
|
||||
for _, s := range sidecars {
|
||||
blobCnt += len(s.Blobs)
|
||||
}
|
||||
if blobCnt > params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob {
|
||||
return fmt.Errorf("too many blobs in block: have %d, permitted %d", blobCnt, params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob)
|
||||
}
|
||||
|
||||
// check blob and versioned hash
|
||||
for i, tx := range blobTxs {
|
||||
// check sidecar tx related
|
||||
if sidecars[i].TxHash != tx.Hash() {
|
||||
return fmt.Errorf("sidecar's TxHash mismatch with expected transaction, want: %v, have: %v", sidecars[i].TxHash, tx.Hash())
|
||||
}
|
||||
if sidecars[i].TxIndex != blobTxIndexes[i] {
|
||||
return fmt.Errorf("sidecar's TxIndex mismatch with expected transaction, want: %v, have: %v", sidecars[i].TxIndex, blobTxIndexes[i])
|
||||
}
|
||||
if err := validateBlobSidecar(tx.BlobHashes(), sidecars[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckDataAvailableInBatch(chainReader consensus.ChainHeaderReader, chain types.Blocks) (int, error) {
|
||||
if len(chain) == 1 {
|
||||
return 0, IsDataAvailable(chainReader, chain[0])
|
||||
}
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
errs sync.Map
|
||||
)
|
||||
|
||||
for i := range chain {
|
||||
wg.Add(1)
|
||||
func(index int, block *types.Block) {
|
||||
gopool.Submit(func() {
|
||||
defer wg.Done()
|
||||
errs.Store(index, IsDataAvailable(chainReader, block))
|
||||
})
|
||||
}(i, chain[i])
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
for i := range chain {
|
||||
val, exist := errs.Load(i)
|
||||
if !exist || val == nil {
|
||||
continue
|
||||
}
|
||||
err := val.(error)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
436
core/data_availability_test.go
Normal file
436
core/data_availability_test.go
Normal file
@@ -0,0 +1,436 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
emptyBlob = kzg4844.Blob{}
|
||||
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
|
||||
emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
|
||||
)
|
||||
|
||||
func TestIsDataAvailable(t *testing.T) {
|
||||
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
||||
tests := []struct {
|
||||
block *types.Block
|
||||
chasingHead uint64
|
||||
withSidecar bool
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
chasingHead: 1,
|
||||
withSidecar: true,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), nil),
|
||||
}, nil),
|
||||
chasingHead: 1,
|
||||
withSidecar: true,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
chasingHead: 1,
|
||||
withSidecar: false,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
chasingHead: 1,
|
||||
withSidecar: true,
|
||||
err: false,
|
||||
},
|
||||
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
chasingHead: params.MinBlocksForBlobRequests + 1,
|
||||
withSidecar: true,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(0),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
chasingHead: params.MinBlocksForBlobRequests + 1,
|
||||
withSidecar: false,
|
||||
err: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, item := range tests {
|
||||
if item.withSidecar {
|
||||
item.block = item.block.WithSidecars(collectBlobsFromTxs(item.block.Header(), item.block.Transactions()))
|
||||
}
|
||||
hr.setChasingHead(item.chasingHead)
|
||||
err := IsDataAvailable(hr, item.block)
|
||||
if item.err {
|
||||
require.Error(t, err, i)
|
||||
t.Log(err)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err, i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckDataAvailableInBatch(t *testing.T) {
|
||||
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
||||
tests := []struct {
|
||||
chain types.Blocks
|
||||
err bool
|
||||
index int
|
||||
}{
|
||||
{
|
||||
chain: types.Blocks{
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(2),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
chain: types.Blocks{
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(2),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(3),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
},
|
||||
err: true,
|
||||
index: 1,
|
||||
},
|
||||
{
|
||||
chain: types.Blocks{
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
},
|
||||
err: true,
|
||||
index: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for i, item := range tests {
|
||||
for j, block := range item.chain {
|
||||
item.chain[j] = block.WithSidecars(collectBlobsFromTxs(block.Header(), block.Transactions()))
|
||||
}
|
||||
index, err := CheckDataAvailableInBatch(hr, item.chain)
|
||||
if item.err {
|
||||
t.Log(index, err)
|
||||
require.Error(t, err, i)
|
||||
require.Equal(t, item.index, index, i)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err, i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEmptySidecarDAChecking(b *testing.B) {
|
||||
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
||||
block := types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
}, nil)
|
||||
block = block.WithSidecars(collectBlobsFromTxs(block.Header(), block.Transactions()))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
IsDataAvailable(hr, block)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRandomSidecarDAChecking(b *testing.B) {
|
||||
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
||||
const count = 10
|
||||
blocks := make([]*types.Block, count)
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
block := types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
}, nil)
|
||||
block = block.WithSidecars(collectBlobsFromTxs(block.Header(), block.Transactions()))
|
||||
blocks[i] = block
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
IsDataAvailable(hr, blocks[i%count])
|
||||
}
|
||||
}
|
||||
|
||||
func collectBlobsFromTxs(header *types.Header, txs types.Transactions) types.BlobSidecars {
|
||||
sidecars := make(types.BlobSidecars, 0, len(txs))
|
||||
for i, tx := range txs {
|
||||
sidecar := types.NewBlobSidecarFromTx(tx)
|
||||
if sidecar == nil {
|
||||
continue
|
||||
}
|
||||
sidecar.TxIndex = uint64(i)
|
||||
sidecar.TxHash = tx.Hash()
|
||||
sidecar.BlockNumber = header.Number
|
||||
sidecar.BlockHash = header.Hash()
|
||||
sidecars = append(sidecars, sidecar)
|
||||
}
|
||||
return sidecars
|
||||
}
|
||||
|
||||
type mockDAHeaderReader struct {
|
||||
config *params.ChainConfig
|
||||
chasingHead uint64
|
||||
}
|
||||
|
||||
func NewMockDAHeaderReader(config *params.ChainConfig) *mockDAHeaderReader {
|
||||
return &mockDAHeaderReader{
|
||||
config: config,
|
||||
chasingHead: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) setChasingHead(h uint64) {
|
||||
r.chasingHead = h
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) Config() *params.ChainConfig {
|
||||
return r.config
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) CurrentHeader() *types.Header {
|
||||
return &types.Header{
|
||||
Number: new(big.Int).SetUint64(r.chasingHead),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) ChasingHead() *types.Header {
|
||||
return &types.Header{
|
||||
Number: new(big.Int).SetUint64(r.chasingHead),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GenesisHeader() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetHeader(hash common.Hash, number uint64) *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetHeaderByNumber(number uint64) *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetHeaderByHash(hash common.Hash) *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetHighestVerifiedHeader() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func createMockDATx(config *params.ChainConfig, sidecar *types.BlobTxSidecar) *types.Transaction {
|
||||
if sidecar == nil {
|
||||
tx := &types.DynamicFeeTx{
|
||||
ChainID: config.ChainID,
|
||||
Nonce: 0,
|
||||
GasTipCap: big.NewInt(22),
|
||||
GasFeeCap: big.NewInt(5),
|
||||
Gas: 25000,
|
||||
To: &common.Address{0x03, 0x04, 0x05},
|
||||
Value: big.NewInt(99),
|
||||
Data: make([]byte, 50),
|
||||
}
|
||||
return types.NewTx(tx)
|
||||
}
|
||||
tx := &types.BlobTx{
|
||||
ChainID: uint256.MustFromBig(config.ChainID),
|
||||
Nonce: 5,
|
||||
GasTipCap: uint256.NewInt(22),
|
||||
GasFeeCap: uint256.NewInt(5),
|
||||
Gas: 25000,
|
||||
To: common.Address{0x03, 0x04, 0x05},
|
||||
Value: uint256.NewInt(99),
|
||||
Data: make([]byte, 50),
|
||||
BlobFeeCap: uint256.NewInt(15),
|
||||
BlobHashes: sidecar.BlobHashes(),
|
||||
Sidecar: sidecar,
|
||||
}
|
||||
return types.NewTx(tx)
|
||||
}
|
||||
|
||||
func randFieldElement() [32]byte {
|
||||
bytes := make([]byte, 32)
|
||||
_, err := rand.Read(bytes)
|
||||
if err != nil {
|
||||
panic("failed to get random field element")
|
||||
}
|
||||
var r fr.Element
|
||||
r.SetBytes(bytes)
|
||||
|
||||
return gokzg4844.SerializeScalar(r)
|
||||
}
|
||||
|
||||
func randBlob() kzg4844.Blob {
|
||||
var blob kzg4844.Blob
|
||||
for i := 0; i < len(blob); i += gokzg4844.SerializedScalarSize {
|
||||
fieldElementBytes := randFieldElement()
|
||||
copy(blob[i:i+gokzg4844.SerializedScalarSize], fieldElementBytes[:])
|
||||
}
|
||||
return blob
|
||||
}
|
||||
|
||||
func randomSidecar() *types.BlobTxSidecar {
|
||||
blob := randBlob()
|
||||
commitment, _ := kzg4844.BlobToCommitment(blob)
|
||||
proof, _ := kzg4844.ComputeBlobProof(blob, commitment)
|
||||
return &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{blob},
|
||||
Commitments: []kzg4844.Commitment{commitment},
|
||||
Proofs: []kzg4844.Proof{proof},
|
||||
}
|
||||
}
|
||||
|
||||
func emptySidecar() *types.BlobTxSidecar {
|
||||
return &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,13 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func postHertzConfig() *params.ChainConfig {
|
||||
func postHertzPreShanghaiConfig() *params.ChainConfig {
|
||||
config := *params.ParliaTestChainConfig
|
||||
config.ShanghaiTime = nil
|
||||
config.KeplerTime = nil
|
||||
config.FeynmanTime = nil
|
||||
config.FeynmanFixTime = nil
|
||||
config.CancunTime = nil
|
||||
return &config
|
||||
}
|
||||
|
||||
@@ -20,6 +25,11 @@ func preHertzConfig() *params.ChainConfig {
|
||||
config.BerlinBlock = nil
|
||||
config.HertzBlock = nil
|
||||
config.HertzfixBlock = nil
|
||||
config.ShanghaiTime = nil
|
||||
config.KeplerTime = nil
|
||||
config.FeynmanTime = nil
|
||||
config.FeynmanFixTime = nil
|
||||
config.CancunTime = nil
|
||||
return &config
|
||||
}
|
||||
|
||||
@@ -91,7 +101,7 @@ func TestSelfDestructGasPostHertz(t *testing.T) {
|
||||
// Expected gas is intrinsic + pc + cold load (due to legacy tx) + SelfDestructGas
|
||||
// i.e. No refund
|
||||
expectedGasUsed := params.TxGas + vm.GasQuickStep + params.ColdAccountAccessCostEIP2929 + params.SelfdestructGasEIP150
|
||||
TestGasUsage(t, postHertzConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
|
||||
TestGasUsage(t, postHertzPreShanghaiConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
|
||||
}
|
||||
|
||||
func TestSstoreGasPostHertz(t *testing.T) {
|
||||
@@ -103,7 +113,7 @@ func TestSstoreGasPostHertz(t *testing.T) {
|
||||
// Expected gas is intrinsic + 2*pushGas + cold load (due to legacy tx) + SstoreGas
|
||||
// i.e. No refund
|
||||
expectedGasUsed := params.TxGas + 2*vm.GasFastestStep + params.ColdSloadCostEIP2929 + params.SstoreSetGasEIP2200
|
||||
TestGasUsage(t, postHertzConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
|
||||
TestGasUsage(t, postHertzPreShanghaiConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
|
||||
}
|
||||
|
||||
func TestSstoreModifyGasPostHertz(t *testing.T) {
|
||||
@@ -120,7 +130,7 @@ func TestSstoreModifyGasPostHertz(t *testing.T) {
|
||||
// Expected gas is intrinsic + 2*pushGas + cold load (due to legacy tx) + SstoreReset (a->b such that a!=0)
|
||||
// i.e. No refund
|
||||
expectedGasUsed := params.TxGas + 2*vm.GasFastestStep + params.SstoreResetGasEIP2200
|
||||
TestGasUsage(t, postHertzConfig(), ethash.NewFaker(), bytecode, initialStorage, 60_000, expectedGasUsed)
|
||||
TestGasUsage(t, postHertzPreShanghaiConfig(), ethash.NewFaker(), bytecode, initialStorage, 60_000, expectedGasUsed)
|
||||
}
|
||||
|
||||
func TestSstoreClearGasPostHertz(t *testing.T) {
|
||||
@@ -137,5 +147,5 @@ func TestSstoreClearGasPostHertz(t *testing.T) {
|
||||
|
||||
// Expected gas is intrinsic + 2*pushGas + SstoreReset (a->b such that a!=0) - sstoreClearGasRefund
|
||||
expectedGasUsage := params.TxGas + 2*vm.GasFastestStep + params.SstoreResetGasEIP2200 - params.SstoreClearsScheduleRefundEIP3529
|
||||
TestGasUsage(t, postHertzConfig(), ethash.NewFaker(), bytecode, initialStorage, 60_000, expectedGasUsage)
|
||||
TestGasUsage(t, postHertzPreShanghaiConfig(), ethash.NewFaker(), bytecode, initialStorage, 60_000, expectedGasUsage)
|
||||
}
|
||||
|
||||
@@ -19,21 +19,21 @@ var _ = (*genesisSpecMarshaling)(nil)
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||
type Genesis struct {
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData hexutil.Bytes `json:"extraData"`
|
||||
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash common.Hash `json:"mixHash"`
|
||||
Coinbase common.Address `json:"coinbase"`
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData hexutil.Bytes `json:"extraData"`
|
||||
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash common.Hash `json:"mixHash"`
|
||||
Coinbase common.Address `json:"coinbase"`
|
||||
Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
|
||||
Number math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||
Number math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||
}
|
||||
var enc Genesis
|
||||
enc.Config = g.Config
|
||||
@@ -62,21 +62,21 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||
type Genesis struct {
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData *hexutil.Bytes `json:"extraData"`
|
||||
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash *common.Hash `json:"mixHash"`
|
||||
Coinbase *common.Address `json:"coinbase"`
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData *hexutil.Bytes `json:"extraData"`
|
||||
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash *common.Hash `json:"mixHash"`
|
||||
Coinbase *common.Address `json:"coinbase"`
|
||||
Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
|
||||
Number *math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash *common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||
Number *math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash *common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||
}
|
||||
var dec Genesis
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
|
||||
@@ -216,11 +216,9 @@ func (e *GenesisMismatchError) Error() string {
|
||||
// ChainOverrides contains the changes to chain config
|
||||
// Typically, these modifications involve hardforks that are not enabled on the BSC mainnet, intended for testing purposes.
|
||||
type ChainOverrides struct {
|
||||
OverrideShanghai *uint64
|
||||
OverrideKepler *uint64
|
||||
OverrideCancun *uint64
|
||||
OverrideVerkle *uint64
|
||||
OverrideFeynman *uint64
|
||||
OverrideCancun *uint64
|
||||
OverrideHaber *uint64
|
||||
OverrideVerkle *uint64
|
||||
}
|
||||
|
||||
// SetupGenesisBlock writes or updates the genesis block in db.
|
||||
@@ -246,21 +244,15 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
||||
}
|
||||
applyOverrides := func(config *params.ChainConfig) {
|
||||
if config != nil {
|
||||
if overrides != nil && overrides.OverrideShanghai != nil {
|
||||
config.ShanghaiTime = overrides.OverrideShanghai
|
||||
}
|
||||
if overrides != nil && overrides.OverrideKepler != nil {
|
||||
config.KeplerTime = overrides.OverrideKepler
|
||||
}
|
||||
if overrides != nil && overrides.OverrideCancun != nil {
|
||||
config.CancunTime = overrides.OverrideCancun
|
||||
}
|
||||
if overrides != nil && overrides.OverrideHaber != nil {
|
||||
config.HaberTime = overrides.OverrideHaber
|
||||
}
|
||||
if overrides != nil && overrides.OverrideVerkle != nil {
|
||||
config.VerkleTime = overrides.OverrideVerkle
|
||||
}
|
||||
if overrides != nil && overrides.OverrideFeynman != nil {
|
||||
config.FeynmanTime = overrides.OverrideFeynman
|
||||
}
|
||||
}
|
||||
}
|
||||
// Just commit the new block if there is no stored genesis block.
|
||||
@@ -285,7 +277,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
||||
// is initialized with an external ancient store. Commit genesis state
|
||||
// in this case.
|
||||
header := rawdb.ReadHeader(db, stored, 0)
|
||||
if header.Root != types.EmptyRootHash && !triedb.Initialized(header.Root) {
|
||||
if header.Root != types.EmptyRootHash && !triedb.Initialized(header.Root) && !triedb.Config().NoTries {
|
||||
if genesis == nil {
|
||||
genesis = DefaultBSCGenesisBlock()
|
||||
}
|
||||
@@ -449,6 +441,10 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||
withdrawals = make([]*types.Withdrawal, 0)
|
||||
}
|
||||
if conf.IsCancun(num, g.Timestamp) {
|
||||
if conf.Parlia != nil {
|
||||
head.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
||||
}
|
||||
|
||||
// EIP-4788: The parentBeaconBlockRoot of the genesis block is always
|
||||
// the zero hash. This is because the genesis block does not have a parent
|
||||
// by definition.
|
||||
@@ -493,13 +489,13 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo
|
||||
if err := flushAlloc(&g.Alloc, db, triedb, block.Hash()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
|
||||
rawdb.WriteBlock(db, block)
|
||||
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
|
||||
rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64())
|
||||
rawdb.WriteHeadBlockHash(db, block.Hash())
|
||||
rawdb.WriteTd(db.BlockStore(), block.Hash(), block.NumberU64(), block.Difficulty())
|
||||
rawdb.WriteBlock(db.BlockStore(), block)
|
||||
rawdb.WriteReceipts(db.BlockStore(), block.Hash(), block.NumberU64(), nil)
|
||||
rawdb.WriteCanonicalHash(db.BlockStore(), block.Hash(), block.NumberU64())
|
||||
rawdb.WriteHeadBlockHash(db.BlockStore(), block.Hash())
|
||||
rawdb.WriteHeadFastBlockHash(db, block.Hash())
|
||||
rawdb.WriteHeadHeaderHash(db, block.Hash())
|
||||
rawdb.WriteHeadHeaderHash(db.BlockStore(), block.Hash())
|
||||
rawdb.WriteChainConfig(db, block.Hash(), config)
|
||||
return block, nil
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
|
||||
return nil, ErrNoGenesis
|
||||
}
|
||||
hc.currentHeader.Store(hc.genesisHeader)
|
||||
if head := rawdb.ReadHeadBlockHash(chainDb); head != (common.Hash{}) {
|
||||
if head := rawdb.ReadHeadBlockHash(chainDb.BlockStore()); head != (common.Hash{}) {
|
||||
if chead := hc.GetHeaderByHash(head); chead != nil {
|
||||
hc.currentHeader.Store(chead)
|
||||
}
|
||||
@@ -144,7 +144,7 @@ func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 {
|
||||
if cached, ok := hc.numberCache.Get(hash); ok {
|
||||
return &cached
|
||||
}
|
||||
number := rawdb.ReadHeaderNumber(hc.chainDb, hash)
|
||||
number := rawdb.ReadHeaderNumber(hc.chainDb.BlockStore(), hash)
|
||||
if number != nil {
|
||||
hc.numberCache.Add(hash, *number)
|
||||
}
|
||||
@@ -172,9 +172,9 @@ func (hc *HeaderChain) Reorg(headers []*types.Header) error {
|
||||
// pile them onto the existing chain. Otherwise, do the necessary
|
||||
// reorgs.
|
||||
var (
|
||||
first = headers[0]
|
||||
last = headers[len(headers)-1]
|
||||
batch = hc.chainDb.NewBatch()
|
||||
first = headers[0]
|
||||
last = headers[len(headers)-1]
|
||||
blockBatch = hc.chainDb.BlockStore().NewBatch()
|
||||
)
|
||||
if first.ParentHash != hc.currentHeaderHash {
|
||||
// Delete any canonical number assignments above the new head
|
||||
@@ -183,7 +183,7 @@ func (hc *HeaderChain) Reorg(headers []*types.Header) error {
|
||||
if hash == (common.Hash{}) {
|
||||
break
|
||||
}
|
||||
rawdb.DeleteCanonicalHash(batch, i)
|
||||
rawdb.DeleteCanonicalHash(blockBatch, i)
|
||||
}
|
||||
// Overwrite any stale canonical number assignments, going
|
||||
// backwards from the first header in this import until the
|
||||
@@ -194,7 +194,7 @@ func (hc *HeaderChain) Reorg(headers []*types.Header) error {
|
||||
headHash = header.Hash()
|
||||
)
|
||||
for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash {
|
||||
rawdb.WriteCanonicalHash(batch, headHash, headNumber)
|
||||
rawdb.WriteCanonicalHash(blockBatch, headHash, headNumber)
|
||||
if headNumber == 0 {
|
||||
break // It shouldn't be reached
|
||||
}
|
||||
@@ -209,16 +209,16 @@ func (hc *HeaderChain) Reorg(headers []*types.Header) error {
|
||||
for i := 0; i < len(headers)-1; i++ {
|
||||
hash := headers[i+1].ParentHash // Save some extra hashing
|
||||
num := headers[i].Number.Uint64()
|
||||
rawdb.WriteCanonicalHash(batch, hash, num)
|
||||
rawdb.WriteHeadHeaderHash(batch, hash)
|
||||
rawdb.WriteCanonicalHash(blockBatch, hash, num)
|
||||
rawdb.WriteHeadHeaderHash(blockBatch, hash)
|
||||
}
|
||||
// Write the last header
|
||||
hash := headers[len(headers)-1].Hash()
|
||||
num := headers[len(headers)-1].Number.Uint64()
|
||||
rawdb.WriteCanonicalHash(batch, hash, num)
|
||||
rawdb.WriteHeadHeaderHash(batch, hash)
|
||||
rawdb.WriteCanonicalHash(blockBatch, hash, num)
|
||||
rawdb.WriteHeadHeaderHash(blockBatch, hash)
|
||||
|
||||
if err := batch.Write(); err != nil {
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Last step update all in-memory head header markers
|
||||
@@ -244,7 +244,7 @@ func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) {
|
||||
newTD = new(big.Int).Set(ptd) // Total difficulty of inserted chain
|
||||
inserted []rawdb.NumberHash // Ephemeral lookup of number/hash for the chain
|
||||
parentKnown = true // Set to true to force hc.HasHeader check the first iteration
|
||||
batch = hc.chainDb.NewBatch()
|
||||
blockBatch = hc.chainDb.BlockStore().NewBatch()
|
||||
)
|
||||
for i, header := range headers {
|
||||
var hash common.Hash
|
||||
@@ -264,10 +264,10 @@ func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) {
|
||||
alreadyKnown := parentKnown && hc.HasHeader(hash, number)
|
||||
if !alreadyKnown {
|
||||
// Irrelevant of the canonical status, write the TD and header to the database.
|
||||
rawdb.WriteTd(batch, hash, number, newTD)
|
||||
rawdb.WriteTd(blockBatch, hash, number, newTD)
|
||||
hc.tdCache.Add(hash, new(big.Int).Set(newTD))
|
||||
|
||||
rawdb.WriteHeader(batch, header)
|
||||
rawdb.WriteHeader(blockBatch, header)
|
||||
inserted = append(inserted, rawdb.NumberHash{Number: number, Hash: hash})
|
||||
hc.headerCache.Add(hash, header)
|
||||
hc.numberCache.Add(hash, number)
|
||||
@@ -280,7 +280,7 @@ func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) {
|
||||
return 0, errors.New("aborted")
|
||||
}
|
||||
// Commit to disk!
|
||||
if err := batch.Write(); err != nil {
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
log.Crit("Failed to write headers", "error", err)
|
||||
}
|
||||
return len(inserted), nil
|
||||
@@ -436,6 +436,10 @@ func (hc *HeaderChain) GetHighestVerifiedHeader() *types.Header {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hc *HeaderChain) ChasingHead() *types.Header {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
|
||||
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
|
||||
// number of blocks to be individually checked before we reach the canonical chain.
|
||||
@@ -638,7 +642,7 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
||||
}
|
||||
var (
|
||||
parentHash common.Hash
|
||||
batch = hc.chainDb.NewBatch()
|
||||
blockBatch = hc.chainDb.BlockStore().NewBatch()
|
||||
origin = true
|
||||
)
|
||||
done := func(header *types.Header) bool {
|
||||
@@ -673,7 +677,7 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
||||
}
|
||||
}
|
||||
// Update head header then.
|
||||
rawdb.WriteHeadHeaderHash(markerBatch, parentHash)
|
||||
rawdb.WriteHeadHeaderHash(hc.chainDb.BlockStore(), parentHash)
|
||||
if err := markerBatch.Write(); err != nil {
|
||||
log.Crit("Failed to update chain markers", "error", err)
|
||||
}
|
||||
@@ -704,16 +708,16 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
||||
}
|
||||
for _, hash := range hashes {
|
||||
if delFn != nil {
|
||||
delFn(batch, hash, num)
|
||||
delFn(blockBatch, hash, num)
|
||||
}
|
||||
rawdb.DeleteHeader(batch, hash, num)
|
||||
rawdb.DeleteTd(batch, hash, num)
|
||||
rawdb.DeleteHeader(blockBatch, hash, num)
|
||||
rawdb.DeleteTd(blockBatch, hash, num)
|
||||
}
|
||||
rawdb.DeleteCanonicalHash(batch, num)
|
||||
rawdb.DeleteCanonicalHash(blockBatch, num)
|
||||
}
|
||||
}
|
||||
// Flush all accumulated deletions.
|
||||
if err := batch.Write(); err != nil {
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
log.Crit("Failed to rewind block", "error", err)
|
||||
}
|
||||
// Clear out any stale content from the caches
|
||||
|
||||
@@ -37,11 +37,11 @@ import (
|
||||
// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
|
||||
func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
data, _ = reader.Ancient(ChainFreezerHashTable, number)
|
||||
if len(data) == 0 {
|
||||
// Get it by hash from leveldb
|
||||
data, _ = db.Get(headerHashKey(number))
|
||||
data, _ = db.BlockStoreReader().Get(headerHashKey(number))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -217,6 +217,22 @@ func WriteHeadFastBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFinalizedBlockHash retrieves the hash of the finalized block.
|
||||
func ReadFinalizedBlockHash(db ethdb.KeyValueReader) common.Hash {
|
||||
data, _ := db.Get(headFinalizedBlockKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
// WriteFinalizedBlockHash stores the hash of the finalized block.
|
||||
func WriteFinalizedBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
if err := db.Put(headFinalizedBlockKey, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store last finalized block's hash", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadLastPivotNumber retrieves the number of the last pivot block. If the node
|
||||
// full synced, the last pivot will always be nil.
|
||||
func ReadLastPivotNumber(db ethdb.KeyValueReader) *uint64 {
|
||||
@@ -287,7 +303,7 @@ func ReadHeaderRange(db ethdb.Reader, number uint64, count uint64) []rlp.RawValu
|
||||
// If we need to read live blocks, we need to figure out the hash first
|
||||
hash := ReadCanonicalHash(db, number)
|
||||
for ; i >= limit && count > 0; i-- {
|
||||
if data, _ := db.Get(headerKey(i, hash)); len(data) > 0 {
|
||||
if data, _ := db.BlockStoreReader().Get(headerKey(i, hash)); len(data) > 0 {
|
||||
rlpHeaders = append(rlpHeaders, data)
|
||||
// Get the parent hash for next query
|
||||
hash = types.HeaderParentHashFromRLP(data)
|
||||
@@ -300,8 +316,8 @@ func ReadHeaderRange(db ethdb.Reader, number uint64, count uint64) []rlp.RawValu
|
||||
if count == 0 {
|
||||
return rlpHeaders
|
||||
}
|
||||
// read remaining from ancients
|
||||
data, err := db.AncientRange(ChainFreezerHeaderTable, i+1-count, count, 0)
|
||||
// read remaining from ancients, cap at 2M
|
||||
data, err := db.AncientRange(ChainFreezerHeaderTable, i+1-count, count, 2*1024*1024)
|
||||
if err != nil {
|
||||
log.Error("Failed to read headers from freezer", "err", err)
|
||||
return rlpHeaders
|
||||
@@ -320,7 +336,7 @@ func ReadHeaderRange(db ethdb.Reader, number uint64, count uint64) []rlp.RawValu
|
||||
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
||||
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
// First try to look up the data in ancient database. Extra hash
|
||||
// comparison is necessary since ancient database only maintains
|
||||
// the canonical data.
|
||||
@@ -329,7 +345,7 @@ func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValu
|
||||
return nil
|
||||
}
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(headerKey(number, hash))
|
||||
data, _ = db.BlockStoreReader().Get(headerKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
@@ -337,10 +353,10 @@ func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValu
|
||||
|
||||
// HasHeader verifies the existence of a block header corresponding to the hash.
|
||||
func HasHeader(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if isCanon(db, number, hash) {
|
||||
if isCanon(db.BlockStoreReader(), number, hash) {
|
||||
return true
|
||||
}
|
||||
if has, err := db.Has(headerKey(number, hash)); !has || err != nil {
|
||||
if has, err := db.BlockStoreReader().Has(headerKey(number, hash)); !has || err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -360,6 +376,20 @@ func ReadHeader(db ethdb.Reader, hash common.Hash, number uint64) *types.Header
|
||||
return header
|
||||
}
|
||||
|
||||
// ReadHeaderAndRaw retrieves the block header corresponding to the hash.
|
||||
func ReadHeaderAndRaw(db ethdb.Reader, hash common.Hash, number uint64) (*types.Header, rlp.RawValue) {
|
||||
data := ReadHeaderRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
header := new(types.Header)
|
||||
if err := rlp.DecodeBytes(data, header); err != nil {
|
||||
log.Error("Invalid block header RLP", "hash", hash, "err", err)
|
||||
return nil, nil
|
||||
}
|
||||
return header, data
|
||||
}
|
||||
|
||||
// WriteHeader stores a block header into the database and also stores the hash-
|
||||
// to-number mapping.
|
||||
func WriteHeader(db ethdb.KeyValueWriter, header *types.Header) {
|
||||
@@ -413,14 +443,14 @@ func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue
|
||||
// comparison is necessary since ancient database only maintains
|
||||
// the canonical data.
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
// Check if the data is in ancients
|
||||
if isCanon(reader, number, hash) {
|
||||
data, _ = reader.Ancient(ChainFreezerBodiesTable, number)
|
||||
return nil
|
||||
}
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(blockBodyKey(number, hash))
|
||||
data, _ = db.BlockStoreReader().Get(blockBodyKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
@@ -430,7 +460,7 @@ func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue
|
||||
// block at number, in RLP encoding.
|
||||
func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
data, _ = reader.Ancient(ChainFreezerBodiesTable, number)
|
||||
if len(data) > 0 {
|
||||
return nil
|
||||
@@ -439,7 +469,7 @@ func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
|
||||
// Note: ReadCanonicalHash cannot be used here because it also
|
||||
// calls ReadAncients internally.
|
||||
hash, _ := db.Get(headerHashKey(number))
|
||||
data, _ = db.Get(blockBodyKey(number, common.BytesToHash(hash)))
|
||||
data, _ = db.BlockStoreReader().Get(blockBodyKey(number, common.BytesToHash(hash)))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
@@ -454,10 +484,10 @@ func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp
|
||||
|
||||
// HasBody verifies the existence of a block body corresponding to the hash.
|
||||
func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if isCanon(db, number, hash) {
|
||||
if isCanon(db.BlockStoreReader(), number, hash) {
|
||||
return true
|
||||
}
|
||||
if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil {
|
||||
if has, err := db.BlockStoreReader().Has(blockBodyKey(number, hash)); !has || err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -534,14 +564,14 @@ func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
// ReadTdRLP retrieves a block's total difficulty corresponding to the hash in RLP encoding.
|
||||
func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
// Check if the data is in ancients
|
||||
if isCanon(reader, number, hash) {
|
||||
data, _ = reader.Ancient(ChainFreezerDifficultyTable, number)
|
||||
return nil
|
||||
}
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(headerTDKey(number, hash))
|
||||
data, _ = db.BlockStoreReader().Get(headerTDKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
@@ -582,10 +612,10 @@ func DeleteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
// HasReceipts verifies the existence of all the transaction receipts belonging
|
||||
// to a block.
|
||||
func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if isCanon(db, number, hash) {
|
||||
if isCanon(db.BlockStoreReader(), number, hash) {
|
||||
return true
|
||||
}
|
||||
if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil {
|
||||
if has, err := db.BlockStoreReader().Has(blockReceiptsKey(number, hash)); !has || err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -594,14 +624,14 @@ func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding.
|
||||
func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
// Check if the data is in ancients
|
||||
if isCanon(reader, number, hash) {
|
||||
data, _ = reader.Ancient(ChainFreezerReceiptTable, number)
|
||||
return nil
|
||||
}
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(blockReceiptsKey(number, hash))
|
||||
data, _ = db.BlockStoreReader().Get(blockReceiptsKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
@@ -784,6 +814,48 @@ func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
|
||||
WriteHeader(db, block.Header())
|
||||
}
|
||||
|
||||
// WriteAncientBlocksWithBlobs writes entire block data with blobs into ancient store and returns the total written size.
|
||||
func WriteAncientBlocksWithBlobs(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts, td *big.Int) (int64, error) {
|
||||
// find cancun index, it's used for new added blob ancient table
|
||||
cancunIndex := -1
|
||||
for i, block := range blocks {
|
||||
if block.Sidecars() != nil {
|
||||
cancunIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
log.Debug("WriteAncientBlocks", "startAt", blocks[0].Number(), "cancunIndex", cancunIndex, "len", len(blocks))
|
||||
|
||||
var (
|
||||
tdSum = new(big.Int).Set(td)
|
||||
preSize int64
|
||||
err error
|
||||
)
|
||||
if cancunIndex > 0 {
|
||||
preSize, err = WriteAncientBlocks(db, blocks[:cancunIndex], receipts[:cancunIndex], td)
|
||||
if err != nil {
|
||||
return preSize, err
|
||||
}
|
||||
for i, block := range blocks[:cancunIndex] {
|
||||
if i > 0 {
|
||||
tdSum.Add(tdSum, block.Difficulty())
|
||||
}
|
||||
}
|
||||
tdSum.Add(tdSum, blocks[cancunIndex].Difficulty())
|
||||
}
|
||||
|
||||
// It will reset blob ancient table at cancunIndex
|
||||
if cancunIndex >= 0 {
|
||||
if err = ResetEmptyBlobAncientTable(db, blocks[cancunIndex].NumberU64()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
blocks = blocks[cancunIndex:]
|
||||
receipts = receipts[cancunIndex:]
|
||||
}
|
||||
postSize, err := WriteAncientBlocks(db, blocks, receipts, tdSum)
|
||||
return preSize + postSize, err
|
||||
}
|
||||
|
||||
// WriteAncientBlocks writes entire block data into ancient store and returns the total written size.
|
||||
func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts, td *big.Int) (int64, error) {
|
||||
var (
|
||||
@@ -809,6 +881,56 @@ func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts
|
||||
})
|
||||
}
|
||||
|
||||
// ReadBlobSidecarsRLP retrieves all the transaction blobs belonging to a block in RLP encoding.
|
||||
func ReadBlobSidecarsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
// Check if the data is in ancients
|
||||
if isCanon(reader, number, hash) {
|
||||
data, _ = reader.Ancient(ChainFreezerBlobSidecarTable, number)
|
||||
return nil
|
||||
}
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.BlockStoreReader().Get(blockBlobSidecarsKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadBlobSidecars retrieves all the transaction blobs belonging to a block.
|
||||
func ReadBlobSidecars(db ethdb.Reader, hash common.Hash, number uint64) types.BlobSidecars {
|
||||
data := ReadBlobSidecarsRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
var ret types.BlobSidecars
|
||||
if err := rlp.DecodeBytes(data, &ret); err != nil {
|
||||
log.Error("Invalid blob array RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// WriteBlobSidecars stores all the transaction blobs belonging to a block.
|
||||
// It could input nil for empty blobs.
|
||||
func WriteBlobSidecars(db ethdb.KeyValueWriter, hash common.Hash, number uint64, blobs types.BlobSidecars) {
|
||||
data, err := rlp.EncodeToBytes(blobs)
|
||||
if err != nil {
|
||||
log.Crit("Failed to encode block blobs", "err", err)
|
||||
}
|
||||
// Store the flattened receipt slice
|
||||
if err := db.Put(blockBlobSidecarsKey(number, hash), data); err != nil {
|
||||
log.Crit("Failed to store block blobs", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteBlobSidecars removes all blob data associated with a block hash.
|
||||
func DeleteBlobSidecars(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(blockBlobSidecarsKey(number, hash)); err != nil {
|
||||
log.Crit("Failed to delete block blobs", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage, td *big.Int) error {
|
||||
num := block.NumberU64()
|
||||
if err := op.AppendRaw(ChainFreezerHashTable, num, block.Hash().Bytes()); err != nil {
|
||||
@@ -826,6 +948,11 @@ func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *type
|
||||
if err := op.Append(ChainFreezerDifficultyTable, num, td); err != nil {
|
||||
return fmt.Errorf("can't append block %d total difficulty: %v", num, err)
|
||||
}
|
||||
if block.Sidecars() != nil {
|
||||
if err := op.Append(ChainFreezerBlobSidecarTable, num, block.Sidecars()); err != nil {
|
||||
return fmt.Errorf("can't append block %d blobs: %v", num, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -835,6 +962,7 @@ func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
DeleteHeader(db, hash, number)
|
||||
DeleteBody(db, hash, number)
|
||||
DeleteTd(db, hash, number)
|
||||
DeleteBlobSidecars(db, hash, number) // it is safe to delete non-exist blob
|
||||
}
|
||||
|
||||
// DeleteBlockWithoutNumber removes all block data associated with a hash, except
|
||||
@@ -844,6 +972,7 @@ func DeleteBlockWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
|
||||
deleteHeaderWithoutNumber(db, hash, number)
|
||||
DeleteBody(db, hash, number)
|
||||
DeleteTd(db, hash, number)
|
||||
DeleteBlobSidecars(db, hash, number)
|
||||
}
|
||||
|
||||
const badBlockToKeep = 10
|
||||
@@ -964,24 +1093,24 @@ func FindCommonAncestor(db ethdb.Reader, a, b *types.Header) *types.Header {
|
||||
|
||||
// ReadHeadHeader returns the current canonical head header.
|
||||
func ReadHeadHeader(db ethdb.Reader) *types.Header {
|
||||
headHeaderHash := ReadHeadHeaderHash(db)
|
||||
headHeaderHash := ReadHeadHeaderHash(db.BlockStoreReader())
|
||||
if headHeaderHash == (common.Hash{}) {
|
||||
return nil
|
||||
}
|
||||
headHeaderNumber := ReadHeaderNumber(db, headHeaderHash)
|
||||
headHeaderNumber := ReadHeaderNumber(db.BlockStoreReader(), headHeaderHash)
|
||||
if headHeaderNumber == nil {
|
||||
return nil
|
||||
}
|
||||
return ReadHeader(db, headHeaderHash, *headHeaderNumber)
|
||||
return ReadHeader(db.BlockStoreReader(), headHeaderHash, *headHeaderNumber)
|
||||
}
|
||||
|
||||
// ReadHeadBlock returns the current canonical head block.
|
||||
func ReadHeadBlock(db ethdb.Reader) *types.Block {
|
||||
headBlockHash := ReadHeadBlockHash(db)
|
||||
headBlockHash := ReadHeadBlockHash(db.BlockStoreReader())
|
||||
if headBlockHash == (common.Hash{}) {
|
||||
return nil
|
||||
}
|
||||
headBlockNumber := ReadHeaderNumber(db, headBlockHash)
|
||||
headBlockNumber := ReadHeaderNumber(db.BlockStoreReader(), headBlockHash)
|
||||
if headBlockNumber == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -18,14 +18,21 @@ package rawdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
rand2 "crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/holiman/uint256"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -412,6 +419,62 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockBlobSidecarsStorage(t *testing.T) {
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
// Create a live block since we need metadata to reconstruct the receipt
|
||||
genBlobs := makeBlkSidecars(1, 2)
|
||||
tx1 := types.NewTx(&types.BlobTx{
|
||||
ChainID: new(uint256.Int).SetUint64(1),
|
||||
GasTipCap: new(uint256.Int),
|
||||
GasFeeCap: new(uint256.Int),
|
||||
Gas: 0,
|
||||
Value: new(uint256.Int),
|
||||
Data: nil,
|
||||
BlobFeeCap: new(uint256.Int),
|
||||
BlobHashes: []common.Hash{common.HexToHash("0x34ec6e64f9cda8fe0451a391e4798085a3ef51a65ed1bfb016e34fc1a2028f8f"), common.HexToHash("0xb9a412e875f29fac436acde234f954e91173c4cf79814f6dcf630d8a6345747f")},
|
||||
Sidecar: genBlobs[0],
|
||||
V: new(uint256.Int),
|
||||
R: new(uint256.Int),
|
||||
S: new(uint256.Int),
|
||||
})
|
||||
tx2 := types.NewTx(&types.DynamicFeeTx{
|
||||
ChainID: new(big.Int).SetUint64(1),
|
||||
GasTipCap: new(big.Int),
|
||||
GasFeeCap: new(big.Int),
|
||||
Gas: 0,
|
||||
Value: new(big.Int),
|
||||
Data: nil,
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
})
|
||||
|
||||
blkHash := common.BytesToHash([]byte{0x03, 0x14})
|
||||
body := &types.Body{Transactions: types.Transactions{tx1, tx2}}
|
||||
sidecars := types.BlobSidecars{types.NewBlobSidecarFromTx(tx1)}
|
||||
|
||||
// Check that no sidecars entries are in a pristine database
|
||||
if bs := ReadBlobSidecars(db, blkHash, 0); len(bs) != 0 {
|
||||
t.Fatalf("non existent sidecars returned: %v", bs)
|
||||
}
|
||||
WriteBody(db, blkHash, 0, body)
|
||||
WriteBlobSidecars(db, blkHash, 0, sidecars)
|
||||
|
||||
if bs := ReadBlobSidecars(db, blkHash, 0); len(bs) == 0 {
|
||||
t.Fatalf("no sidecars returned")
|
||||
} else {
|
||||
if err := checkBlobSidecarsRLP(bs, sidecars); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
DeleteBlobSidecars(db, blkHash, 0)
|
||||
if bs := ReadBlobSidecars(db, blkHash, 0); len(bs) != 0 {
|
||||
t.Fatalf("deleted sidecars returned: %v", bs)
|
||||
}
|
||||
}
|
||||
|
||||
func checkReceiptsRLP(have, want types.Receipts) error {
|
||||
if len(have) != len(want) {
|
||||
return fmt.Errorf("receipts sizes mismatch: have %d, want %d", len(have), len(want))
|
||||
@@ -432,6 +495,26 @@ func checkReceiptsRLP(have, want types.Receipts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkBlobSidecarsRLP(have, want types.BlobSidecars) error {
|
||||
if len(have) != len(want) {
|
||||
return fmt.Errorf("blobs sizes mismatch: have %d, want %d", len(have), len(want))
|
||||
}
|
||||
for i := 0; i < len(want); i++ {
|
||||
rlpHave, err := rlp.EncodeToBytes(have[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rlpWant, err := rlp.EncodeToBytes(want[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(rlpHave, rlpWant) {
|
||||
return fmt.Errorf("blob #%d: receipt mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAncientStorage(t *testing.T) {
|
||||
// Freezer style fast import the chain.
|
||||
frdir := t.TempDir()
|
||||
@@ -465,7 +548,8 @@ func TestAncientStorage(t *testing.T) {
|
||||
}
|
||||
|
||||
// Write and verify the header in the database
|
||||
WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil}, big.NewInt(100))
|
||||
_, err = WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil}, big.NewInt(100))
|
||||
require.NoError(t, err)
|
||||
|
||||
if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
|
||||
t.Fatalf("no header returned")
|
||||
@@ -586,6 +670,7 @@ func BenchmarkWriteAncientBlocks(b *testing.B) {
|
||||
const blockTxs = 20
|
||||
allBlocks := makeTestBlocks(b.N, blockTxs)
|
||||
batchReceipts := makeTestReceipts(batchSize, blockTxs)
|
||||
batchSidecars := makeTestSidecars(batchSize, blockTxs)
|
||||
b.ResetTimer()
|
||||
|
||||
// The benchmark loop writes batches of blocks, but note that the total block count is
|
||||
@@ -601,6 +686,9 @@ func BenchmarkWriteAncientBlocks(b *testing.B) {
|
||||
|
||||
blocks := allBlocks[i : i+length]
|
||||
receipts := batchReceipts[:length]
|
||||
for j := 0; j < length; j++ {
|
||||
blocks[i+j] = blocks[i+j].WithSidecars(batchSidecars[j])
|
||||
}
|
||||
writeSize, err := WriteAncientBlocks(db, blocks, receipts, td)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@@ -663,6 +751,43 @@ func makeTestReceipts(n int, nPerBlock int) []types.Receipts {
|
||||
return allReceipts
|
||||
}
|
||||
|
||||
func makeBlkSidecars(n, nPerTx int) []*types.BlobTxSidecar {
|
||||
if n <= 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make([]*types.BlobTxSidecar, n)
|
||||
for i := 0; i < n; i++ {
|
||||
blobs := make([]kzg4844.Blob, nPerTx)
|
||||
commitments := make([]kzg4844.Commitment, nPerTx)
|
||||
proofs := make([]kzg4844.Proof, nPerTx)
|
||||
for i := 0; i < nPerTx; i++ {
|
||||
io.ReadFull(rand2.Reader, blobs[i][:])
|
||||
commitments[i], _ = kzg4844.BlobToCommitment(blobs[i])
|
||||
proofs[i], _ = kzg4844.ComputeBlobProof(blobs[i], commitments[i])
|
||||
}
|
||||
ret[i] = &types.BlobTxSidecar{
|
||||
Blobs: blobs,
|
||||
Commitments: commitments,
|
||||
Proofs: proofs,
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// makeTestSidecars creates fake blobs for the ancient write benchmark.
|
||||
func makeTestSidecars(n int, nPerBlock int) []types.BlobSidecars {
|
||||
allBlobs := make([]types.BlobSidecars, n)
|
||||
for i := 0; i < n; i++ {
|
||||
raws := makeBlkSidecars(nPerBlock, i%3)
|
||||
var sidecars types.BlobSidecars
|
||||
for _, s := range raws {
|
||||
sidecars = append(sidecars, &types.BlobSidecar{BlobTxSidecar: *s})
|
||||
}
|
||||
allBlobs[i] = sidecars
|
||||
}
|
||||
return allBlobs
|
||||
}
|
||||
|
||||
type fullLogRLP struct {
|
||||
Address common.Address
|
||||
Topics []common.Hash
|
||||
|
||||
@@ -42,7 +42,7 @@ func ReadTxLookupEntry(db ethdb.Reader, hash common.Hash) *uint64 {
|
||||
}
|
||||
// Database v4-v5 tx lookup format just stores the hash
|
||||
if len(data) == common.HashLength {
|
||||
return ReadHeaderNumber(db, common.BytesToHash(data))
|
||||
return ReadHeaderNumber(db.BlockStoreReader(), common.BytesToHash(data))
|
||||
}
|
||||
// Finally try database v3 tx lookup format
|
||||
var entry LegacyTxLookupEntry
|
||||
|
||||
@@ -324,7 +324,7 @@ func ValidateStateScheme(stateScheme string) bool {
|
||||
// the stored state.
|
||||
//
|
||||
// - If the provided scheme is none, use the scheme consistent with persistent
|
||||
// state, or fallback to hash-based scheme if state is empty.
|
||||
// state, or fallback to path-based scheme if state is empty.
|
||||
//
|
||||
// - If the provided scheme is hash, use hash-based scheme or error out if not
|
||||
// compatible with persistent state scheme.
|
||||
@@ -338,10 +338,8 @@ func ParseStateScheme(provided string, disk ethdb.Database) (string, error) {
|
||||
stored := ReadStateScheme(disk)
|
||||
if provided == "" {
|
||||
if stored == "" {
|
||||
// use default scheme for empty database, flip it when
|
||||
// path mode is chosen as default
|
||||
log.Info("State scheme set to default", "scheme", "hash")
|
||||
return HashScheme, nil
|
||||
log.Info("State scheme set to default", "scheme", "path")
|
||||
return PathScheme, nil // use default scheme for empty database
|
||||
}
|
||||
log.Info("State scheme set to already existing disk db", "scheme", stored)
|
||||
return stored, nil // reuse scheme of persistent scheme
|
||||
|
||||
@@ -34,18 +34,24 @@ const (
|
||||
|
||||
// ChainFreezerDifficultyTable indicates the name of the freezer total difficulty table.
|
||||
ChainFreezerDifficultyTable = "diffs"
|
||||
|
||||
// ChainFreezerBlobSidecarTable indicates the name of the freezer total blob table.
|
||||
ChainFreezerBlobSidecarTable = "blobs"
|
||||
)
|
||||
|
||||
// chainFreezerNoSnappy configures whether compression is disabled for the ancient-tables.
|
||||
// Hashes and difficulties don't compress well.
|
||||
var chainFreezerNoSnappy = map[string]bool{
|
||||
ChainFreezerHeaderTable: false,
|
||||
ChainFreezerHashTable: true,
|
||||
ChainFreezerBodiesTable: false,
|
||||
ChainFreezerReceiptTable: false,
|
||||
ChainFreezerDifficultyTable: true,
|
||||
ChainFreezerHeaderTable: false,
|
||||
ChainFreezerHashTable: true,
|
||||
ChainFreezerBodiesTable: false,
|
||||
ChainFreezerReceiptTable: false,
|
||||
ChainFreezerDifficultyTable: true,
|
||||
ChainFreezerBlobSidecarTable: false,
|
||||
}
|
||||
|
||||
var additionTables = []string{ChainFreezerBlobSidecarTable}
|
||||
|
||||
const (
|
||||
// stateHistoryTableSize defines the maximum size of freezer data files.
|
||||
stateHistoryTableSize = 2 * 1000 * 1000 * 1000
|
||||
|
||||
@@ -121,16 +121,25 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) {
|
||||
// ancient indicates the path of root ancient directory where the chain freezer can
|
||||
// be opened. Start and end specify the range for dumping out indexes.
|
||||
// Note this function can only be used for debugging purposes.
|
||||
func InspectFreezerTable(ancient string, freezerName string, tableName string, start, end int64) error {
|
||||
func InspectFreezerTable(ancient string, freezerName string, tableName string, start, end int64, multiDatabase bool) error {
|
||||
var (
|
||||
path string
|
||||
tables map[string]bool
|
||||
)
|
||||
switch freezerName {
|
||||
case ChainFreezerName:
|
||||
path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
|
||||
if multiDatabase {
|
||||
path, tables = resolveChainFreezerDir(filepath.Dir(ancient)+"/block/ancient"), chainFreezerNoSnappy
|
||||
} else {
|
||||
path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
|
||||
}
|
||||
|
||||
case StateFreezerName:
|
||||
path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy
|
||||
if multiDatabase {
|
||||
path, tables = filepath.Join(filepath.Dir(ancient)+"/state/ancient", freezerName), stateFreezerNoSnappy
|
||||
} else {
|
||||
path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown freezer, supported ones: %v", freezers)
|
||||
}
|
||||
|
||||
@@ -17,11 +17,15 @@
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
@@ -39,16 +43,20 @@ const (
|
||||
freezerBatchLimit = 30000
|
||||
)
|
||||
|
||||
var (
|
||||
missFreezerEnvErr = errors.New("missing freezer env error")
|
||||
)
|
||||
|
||||
// chainFreezer is a wrapper of freezer with additional chain freezing feature.
|
||||
// The background thread will keep moving ancient chain segments from key-value
|
||||
// database to flat files for saving space on live database.
|
||||
type chainFreezer struct {
|
||||
threshold atomic.Uint64 // Number of recent blocks not to freeze (params.FullImmutabilityThreshold apart from tests)
|
||||
|
||||
*Freezer
|
||||
quit chan struct{}
|
||||
wg sync.WaitGroup
|
||||
trigger chan chan struct{} // Manual blocking freeze trigger, test determinism
|
||||
|
||||
freezeEnv atomic.Value
|
||||
}
|
||||
|
||||
// newChainFreezer initializes the freezer for ancient chain data.
|
||||
@@ -57,13 +65,11 @@ func newChainFreezer(datadir string, namespace string, readonly bool, offset uin
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cf := chainFreezer{
|
||||
return &chainFreezer{
|
||||
Freezer: freezer,
|
||||
quit: make(chan struct{}),
|
||||
trigger: make(chan chan struct{}),
|
||||
}
|
||||
cf.threshold.Store(params.FullImmutabilityThreshold)
|
||||
return &cf, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the chain freezer instance and terminates the background thread.
|
||||
@@ -77,6 +83,57 @@ func (f *chainFreezer) Close() error {
|
||||
return f.Freezer.Close()
|
||||
}
|
||||
|
||||
// readHeadNumber returns the number of chain head block. 0 is returned if the
|
||||
// block is unknown or not available yet.
|
||||
func (f *chainFreezer) readHeadNumber(db ethdb.KeyValueReader) uint64 {
|
||||
hash := ReadHeadBlockHash(db)
|
||||
if hash == (common.Hash{}) {
|
||||
log.Error("Head block is not reachable")
|
||||
return 0
|
||||
}
|
||||
number := ReadHeaderNumber(db, hash)
|
||||
if number == nil {
|
||||
log.Error("Number of head block is missing")
|
||||
return 0
|
||||
}
|
||||
return *number
|
||||
}
|
||||
|
||||
// readFinalizedNumber returns the number of finalized block. 0 is returned
|
||||
// if the block is unknown or not available yet.
|
||||
func (f *chainFreezer) readFinalizedNumber(db ethdb.KeyValueReader) uint64 {
|
||||
hash := ReadFinalizedBlockHash(db)
|
||||
if hash == (common.Hash{}) {
|
||||
return 0
|
||||
}
|
||||
number := ReadHeaderNumber(db, hash)
|
||||
if number == nil {
|
||||
log.Error("Number of finalized block is missing")
|
||||
return 0
|
||||
}
|
||||
return *number
|
||||
}
|
||||
|
||||
// freezeThreshold returns the threshold for chain freezing. It's determined
|
||||
// by formula: max(finality, HEAD-params.FullImmutabilityThreshold).
|
||||
func (f *chainFreezer) freezeThreshold(db ethdb.KeyValueReader) (uint64, error) {
|
||||
var (
|
||||
head = f.readHeadNumber(db)
|
||||
final = f.readFinalizedNumber(db)
|
||||
headLimit uint64
|
||||
)
|
||||
if head > params.FullImmutabilityThreshold {
|
||||
headLimit = head - params.FullImmutabilityThreshold
|
||||
}
|
||||
if final == 0 && headLimit == 0 {
|
||||
return 0, errors.New("freezing threshold is not available")
|
||||
}
|
||||
if final > headLimit {
|
||||
return final, nil
|
||||
}
|
||||
return headLimit, nil
|
||||
}
|
||||
|
||||
// freeze is a background thread that periodically checks the blockchain for any
|
||||
// import progress and moves ancient data from the fast database into the freezer.
|
||||
//
|
||||
@@ -114,60 +171,54 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Retrieve the freezing threshold.
|
||||
hash := ReadHeadBlockHash(nfdb)
|
||||
if hash == (common.Hash{}) {
|
||||
log.Debug("Current full block hash unavailable") // new chain, empty database
|
||||
|
||||
// check freezer env first, it must wait a while when the env is necessary
|
||||
err := f.checkFreezerEnv()
|
||||
if err == missFreezerEnvErr {
|
||||
log.Warn("Freezer need related env, may wait for a while", "err", err)
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
number := ReadHeaderNumber(nfdb, hash)
|
||||
threshold := f.threshold.Load()
|
||||
if err != nil {
|
||||
log.Error("Freezer check FreezerEnv err", "err", err)
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
|
||||
threshold, err := f.freezeThreshold(nfdb)
|
||||
if err != nil {
|
||||
backoff = true
|
||||
log.Debug("Current full block not old enough to freeze", "err", err)
|
||||
continue
|
||||
}
|
||||
frozen := f.frozen.Load()
|
||||
switch {
|
||||
case number == nil:
|
||||
log.Error("Current full block number unavailable", "hash", hash)
|
||||
backoff = true
|
||||
continue
|
||||
|
||||
case *number < threshold:
|
||||
log.Debug("Current full block not old enough to freeze", "number", *number, "hash", hash, "delay", threshold)
|
||||
backoff = true
|
||||
continue
|
||||
|
||||
case *number-threshold <= frozen:
|
||||
log.Debug("Ancient blocks frozen already", "number", *number, "hash", hash, "frozen", frozen)
|
||||
// Short circuit if the blocks below threshold are already frozen.
|
||||
if frozen != 0 && frozen-1 >= threshold {
|
||||
backoff = true
|
||||
log.Debug("Ancient blocks frozen already", "threshold", threshold, "frozen", frozen)
|
||||
continue
|
||||
}
|
||||
head := ReadHeader(nfdb, hash, *number)
|
||||
if head == nil {
|
||||
log.Error("Current full block unavailable", "number", *number, "hash", hash)
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Seems we have data ready to be frozen, process in usable batches
|
||||
var (
|
||||
start = time.Now()
|
||||
first, _ = f.Ancients()
|
||||
limit = *number - threshold
|
||||
start = time.Now()
|
||||
first = frozen // the first block to freeze
|
||||
last = threshold // the last block to freeze
|
||||
)
|
||||
if limit-first > freezerBatchLimit {
|
||||
limit = first + freezerBatchLimit
|
||||
if last-first+1 > freezerBatchLimit {
|
||||
last = freezerBatchLimit + first - 1
|
||||
}
|
||||
ancients, err := f.freezeRange(nfdb, first, limit)
|
||||
|
||||
ancients, err := f.freezeRangeWithBlobs(nfdb, first, last)
|
||||
if err != nil {
|
||||
log.Error("Error in block freeze operation", "err", err)
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Batch of blocks have been frozen, flush them before wiping from leveldb
|
||||
if err := f.Sync(); err != nil {
|
||||
log.Crit("Failed to flush frozen tables", "err", err)
|
||||
}
|
||||
|
||||
// Wipe out all data from the active database
|
||||
batch := db.NewBatch()
|
||||
for i := 0; i < len(ancients); i++ {
|
||||
@@ -243,6 +294,30 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
||||
}
|
||||
log.Debug("Deep froze chain segment", context...)
|
||||
|
||||
env, _ := f.freezeEnv.Load().(*ethdb.FreezerEnv)
|
||||
hash := ReadHeadBlockHash(nfdb)
|
||||
if hash == (common.Hash{}) {
|
||||
log.Debug("Current full block hash unavailable") // new chain, empty database
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
number := ReadHeaderNumber(nfdb, hash)
|
||||
if number == nil {
|
||||
log.Error("Current full block number unavailable", "hash", hash)
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
head := ReadHeader(nfdb, hash, *number)
|
||||
if head == nil {
|
||||
log.Error("Current full block unavailable", "number", *number, "hash", hash)
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
// try prune blob data after cancun fork
|
||||
if isCancun(env, head.Number, head.Time) {
|
||||
f.tryPruneBlobAncientTable(env, *number)
|
||||
}
|
||||
|
||||
// Avoid database thrashing with tiny writes
|
||||
if frozen-first < freezerBatchLimit {
|
||||
backoff = true
|
||||
@@ -250,9 +325,93 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
||||
}
|
||||
}
|
||||
|
||||
func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hashes []common.Hash, err error) {
|
||||
hashes = make([]common.Hash, 0, limit-number)
|
||||
func (f *chainFreezer) tryPruneBlobAncientTable(env *ethdb.FreezerEnv, num uint64) {
|
||||
extraReserve := getBlobExtraReserveFromEnv(env)
|
||||
// It means that there is no need for pruning
|
||||
if extraReserve == 0 {
|
||||
return
|
||||
}
|
||||
reserveThreshold := params.MinBlocksForBlobRequests + extraReserve
|
||||
if num <= reserveThreshold {
|
||||
return
|
||||
}
|
||||
expectTail := num - reserveThreshold
|
||||
start := time.Now()
|
||||
if _, err := f.TruncateTableTail(ChainFreezerBlobSidecarTable, expectTail); err != nil {
|
||||
log.Error("Cannot prune blob ancient", "block", num, "expectTail", expectTail, "err", err)
|
||||
return
|
||||
}
|
||||
log.Debug("Chain freezer prune useless blobs, now ancient data is", "from", expectTail, "to", num, "cost", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
|
||||
func getBlobExtraReserveFromEnv(env *ethdb.FreezerEnv) uint64 {
|
||||
if env == nil {
|
||||
return params.DefaultExtraReserveForBlobRequests
|
||||
}
|
||||
return env.BlobExtraReserve
|
||||
}
|
||||
|
||||
func (f *chainFreezer) freezeRangeWithBlobs(nfdb *nofreezedb, number, limit uint64) (hashes []common.Hash, err error) {
|
||||
defer func() {
|
||||
log.Debug("freezeRangeWithBlobs", "from", number, "to", limit, "err", err)
|
||||
}()
|
||||
lastHash := ReadCanonicalHash(nfdb, limit)
|
||||
if lastHash == (common.Hash{}) {
|
||||
return nil, fmt.Errorf("canonical hash missing, can't freeze block %d", limit)
|
||||
}
|
||||
last, _ := ReadHeaderAndRaw(nfdb, lastHash, limit)
|
||||
if last == nil {
|
||||
return nil, fmt.Errorf("block header missing, can't freeze block %d", limit)
|
||||
}
|
||||
env, _ := f.freezeEnv.Load().(*ethdb.FreezerEnv)
|
||||
if !isCancun(env, last.Number, last.Time) {
|
||||
return f.freezeRange(nfdb, number, limit)
|
||||
}
|
||||
|
||||
var (
|
||||
cancunNumber uint64
|
||||
preHashes []common.Hash
|
||||
)
|
||||
for i := number; i <= limit; i++ {
|
||||
hash := ReadCanonicalHash(nfdb, i)
|
||||
if hash == (common.Hash{}) {
|
||||
return nil, fmt.Errorf("canonical hash missing, can't freeze block %d", i)
|
||||
}
|
||||
h, header := ReadHeaderAndRaw(nfdb, hash, i)
|
||||
if len(header) == 0 {
|
||||
return nil, fmt.Errorf("block header missing, can't freeze block %d", i)
|
||||
}
|
||||
if isCancun(env, h.Number, h.Time) {
|
||||
cancunNumber = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// freeze pre cancun
|
||||
preHashes, err = f.freezeRange(nfdb, number, cancunNumber-1)
|
||||
if err != nil {
|
||||
return preHashes, err
|
||||
}
|
||||
|
||||
if err = ResetEmptyBlobAncientTable(f, cancunNumber); err != nil {
|
||||
return preHashes, err
|
||||
}
|
||||
// freeze post cancun
|
||||
postHashes, err := f.freezeRange(nfdb, cancunNumber, limit)
|
||||
hashes = append(preHashes, postHashes...)
|
||||
return hashes, err
|
||||
}
|
||||
|
||||
// freezeRange moves a batch of chain segments from the fast database to the freezer.
|
||||
// The parameters (number, limit) specify the relevant block range, both of which
|
||||
// are included.
|
||||
func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hashes []common.Hash, err error) {
|
||||
if number > limit {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
env, _ := f.freezeEnv.Load().(*ethdb.FreezerEnv)
|
||||
hashes = make([]common.Hash, 0, limit-number+1)
|
||||
_, err = f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for ; number <= limit; number++ {
|
||||
// Retrieve all the components of the canonical block.
|
||||
@@ -260,7 +419,7 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
|
||||
if hash == (common.Hash{}) {
|
||||
return fmt.Errorf("canonical hash missing, can't freeze block %d", number)
|
||||
}
|
||||
header := ReadHeaderRLP(nfdb, hash, number)
|
||||
h, header := ReadHeaderAndRaw(nfdb, hash, number)
|
||||
if len(header) == 0 {
|
||||
return fmt.Errorf("block header missing, can't freeze block %d", number)
|
||||
}
|
||||
@@ -276,6 +435,14 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
|
||||
if len(td) == 0 {
|
||||
return fmt.Errorf("total difficulty missing, can't freeze block %d", number)
|
||||
}
|
||||
// blobs is nil before cancun fork
|
||||
var sidecars rlp.RawValue
|
||||
if isCancun(env, h.Number, h.Time) {
|
||||
sidecars = ReadBlobSidecarsRLP(nfdb, hash, number)
|
||||
if len(sidecars) == 0 {
|
||||
return fmt.Errorf("block blobs missing, can't freeze block %d", number)
|
||||
}
|
||||
}
|
||||
|
||||
// Write to the batch.
|
||||
if err := op.AppendRaw(ChainFreezerHashTable, number, hash[:]); err != nil {
|
||||
@@ -293,6 +460,11 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
|
||||
if err := op.AppendRaw(ChainFreezerDifficultyTable, number, td); err != nil {
|
||||
return fmt.Errorf("can't write td to Freezer: %v", err)
|
||||
}
|
||||
if isCancun(env, h.Number, h.Time) {
|
||||
if err := op.AppendRaw(ChainFreezerBlobSidecarTable, number, sidecars); err != nil {
|
||||
return fmt.Errorf("can't write blobs to Freezer: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
hashes = append(hashes, hash)
|
||||
}
|
||||
@@ -301,3 +473,35 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
|
||||
|
||||
return hashes, err
|
||||
}
|
||||
|
||||
func (f *chainFreezer) SetupFreezerEnv(env *ethdb.FreezerEnv) error {
|
||||
f.freezeEnv.Store(env)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *chainFreezer) checkFreezerEnv() error {
|
||||
_, exist := f.freezeEnv.Load().(*ethdb.FreezerEnv)
|
||||
if exist {
|
||||
return nil
|
||||
}
|
||||
blobFrozen, err := f.TableAncients(ChainFreezerBlobSidecarTable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if blobFrozen > 0 {
|
||||
return missFreezerEnvErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isCancun(env *ethdb.FreezerEnv, num *big.Int, time uint64) bool {
|
||||
if env == nil || env.ChainCfg == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return env.ChainCfg.IsCancun(num, time)
|
||||
}
|
||||
|
||||
func ResetEmptyBlobAncientTable(db ethdb.AncientWriter, next uint64) error {
|
||||
return db.ResetTable(ChainFreezerBlobSidecarTable, next, true)
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func InitDatabaseFromFreezer(db ethdb.Database) {
|
||||
}
|
||||
batch.Reset()
|
||||
|
||||
WriteHeadHeaderHash(db, hash)
|
||||
WriteHeadHeaderHash(db.BlockStore(), hash)
|
||||
WriteHeadFastBlockHash(db, hash)
|
||||
log.Info("Initialized database from freezer", "blocks", frozen, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
@@ -122,7 +122,7 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
|
||||
}
|
||||
defer close(rlpCh)
|
||||
for n != end {
|
||||
data := ReadCanonicalBodyRLP(db, n)
|
||||
data := ReadCanonicalBodyRLP(db.BlockStore(), n)
|
||||
// Feed the block to the aggregator, or abort on interrupt
|
||||
select {
|
||||
case rlpCh <- &numberRlp{n, data}:
|
||||
|
||||
@@ -26,14 +26,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/leveldb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/pebble"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
// freezerdb is a database wrapper that enables freezer data retrievals.
|
||||
@@ -41,8 +40,10 @@ type freezerdb struct {
|
||||
ancientRoot string
|
||||
ethdb.KeyValueStore
|
||||
ethdb.AncientStore
|
||||
ethdb.AncientFreezer
|
||||
diffStore ethdb.KeyValueStore
|
||||
stateStore ethdb.Database
|
||||
blockStore ethdb.Database
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) StateStoreReader() ethdb.Reader {
|
||||
@@ -52,6 +53,18 @@ func (frdb *freezerdb) StateStoreReader() ethdb.Reader {
|
||||
return frdb.stateStore
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) BlockStoreReader() ethdb.Reader {
|
||||
if frdb.blockStore == nil {
|
||||
return frdb
|
||||
}
|
||||
return frdb.blockStore
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) BlockStoreWriter() ethdb.Writer {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
// AncientDatadir returns the path of root ancient directory.
|
||||
func (frdb *freezerdb) AncientDatadir() (string, error) {
|
||||
return frdb.ancientRoot, nil
|
||||
@@ -77,6 +90,11 @@ func (frdb *freezerdb) Close() error {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if frdb.blockStore != nil {
|
||||
if err := frdb.blockStore.Close(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if len(errs) != 0 {
|
||||
return fmt.Errorf("%v", errs)
|
||||
}
|
||||
@@ -105,19 +123,28 @@ func (frdb *freezerdb) SetStateStore(state ethdb.Database) {
|
||||
frdb.stateStore = state
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) BlockStore() ethdb.Database {
|
||||
if frdb.blockStore != nil {
|
||||
return frdb.blockStore
|
||||
} else {
|
||||
return frdb
|
||||
}
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) SetBlockStore(block ethdb.Database) {
|
||||
if frdb.blockStore != nil {
|
||||
frdb.blockStore.Close()
|
||||
}
|
||||
frdb.blockStore = block
|
||||
}
|
||||
|
||||
// Freeze is a helper method used for external testing to trigger and block until
|
||||
// a freeze cycle completes, without having to sleep for a minute to trigger the
|
||||
// automatic background run.
|
||||
func (frdb *freezerdb) Freeze(threshold uint64) error {
|
||||
func (frdb *freezerdb) Freeze() error {
|
||||
if frdb.AncientStore.(*chainFreezer).readonly {
|
||||
return errReadOnly
|
||||
}
|
||||
// Set the freezer threshold to a temporary value
|
||||
defer func(old uint64) {
|
||||
frdb.AncientStore.(*chainFreezer).threshold.Store(old)
|
||||
}(frdb.AncientStore.(*chainFreezer).threshold.Load())
|
||||
frdb.AncientStore.(*chainFreezer).threshold.Store(threshold)
|
||||
|
||||
// Trigger a freeze cycle and block until it's done
|
||||
trigger := make(chan struct{}, 1)
|
||||
frdb.AncientStore.(*chainFreezer).trigger <- trigger
|
||||
@@ -125,11 +152,16 @@ func (frdb *freezerdb) Freeze(threshold uint64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) SetupFreezerEnv(env *ethdb.FreezerEnv) error {
|
||||
return frdb.AncientFreezer.SetupFreezerEnv(env)
|
||||
}
|
||||
|
||||
// nofreezedb is a database wrapper that disables freezer data retrievals.
|
||||
type nofreezedb struct {
|
||||
ethdb.KeyValueStore
|
||||
diffStore ethdb.KeyValueStore
|
||||
stateStore ethdb.Database
|
||||
blockStore ethdb.Database
|
||||
}
|
||||
|
||||
// HasAncient returns an error as we don't have a backing chain freezer.
|
||||
@@ -182,6 +214,16 @@ func (db *nofreezedb) TruncateTail(items uint64) (uint64, error) {
|
||||
return 0, errNotSupported
|
||||
}
|
||||
|
||||
// TruncateTableTail will truncate certain table to new tail
|
||||
func (db *nofreezedb) TruncateTableTail(kind string, tail uint64) (uint64, error) {
|
||||
return 0, errNotSupported
|
||||
}
|
||||
|
||||
// ResetTable will reset certain table with new start point
|
||||
func (db *nofreezedb) ResetTable(kind string, startAt uint64, onlyEmpty bool) error {
|
||||
return errNotSupported
|
||||
}
|
||||
|
||||
// Sync returns an error as we don't have a backing chain freezer.
|
||||
func (db *nofreezedb) Sync() error {
|
||||
return errNotSupported
|
||||
@@ -210,6 +252,31 @@ func (db *nofreezedb) StateStoreReader() ethdb.Reader {
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *nofreezedb) BlockStore() ethdb.Database {
|
||||
if db.blockStore != nil {
|
||||
return db.blockStore
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *nofreezedb) SetBlockStore(block ethdb.Database) {
|
||||
db.blockStore = block
|
||||
}
|
||||
|
||||
func (db *nofreezedb) BlockStoreReader() ethdb.Reader {
|
||||
if db.blockStore != nil {
|
||||
return db.blockStore
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *nofreezedb) BlockStoreWriter() ethdb.Writer {
|
||||
if db.blockStore != nil {
|
||||
return db.blockStore
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *nofreezedb) ReadAncients(fn func(reader ethdb.AncientReaderOp) error) (err error) {
|
||||
// Unlike other ancient-related methods, this method does not return
|
||||
// errNotSupported when invoked.
|
||||
@@ -241,6 +308,10 @@ func (db *nofreezedb) AncientDatadir() (string, error) {
|
||||
return "", errNotSupported
|
||||
}
|
||||
|
||||
func (db *nofreezedb) SetupFreezerEnv(env *ethdb.FreezerEnv) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewDatabase creates a high level database on top of a given key-value data
|
||||
// store without a freezer moving immutable chain segments into cold storage.
|
||||
func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
|
||||
@@ -307,9 +378,10 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
WriteAncientType(db, PruneFreezerType)
|
||||
}
|
||||
return &freezerdb{
|
||||
ancientRoot: ancient,
|
||||
KeyValueStore: db,
|
||||
AncientStore: frdb,
|
||||
ancientRoot: ancient,
|
||||
KeyValueStore: db,
|
||||
AncientStore: frdb,
|
||||
AncientFreezer: frdb,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -409,6 +481,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
// freezer.
|
||||
}
|
||||
}
|
||||
|
||||
// no prune ancient start success
|
||||
if !readonly {
|
||||
WriteAncientType(db, EntireFreezerType)
|
||||
@@ -422,9 +495,10 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
}()
|
||||
}
|
||||
return &freezerdb{
|
||||
ancientRoot: ancient,
|
||||
KeyValueStore: db,
|
||||
AncientStore: frdb,
|
||||
ancientRoot: ancient,
|
||||
KeyValueStore: db,
|
||||
AncientStore: frdb,
|
||||
AncientFreezer: frdb,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -497,6 +571,7 @@ type OpenOptions struct {
|
||||
DisableFreeze bool
|
||||
IsLastOffset bool
|
||||
PruneAncientData bool
|
||||
|
||||
// Ephemeral means that filesystem sync operations should be avoided: data integrity in the face of
|
||||
// a crash is not important. This option should typically be used in tests.
|
||||
Ephemeral bool
|
||||
@@ -506,7 +581,7 @@ type OpenOptions struct {
|
||||
//
|
||||
// type == null type != null
|
||||
// +----------------------------------------
|
||||
// db is non-existent | leveldb default | specified type
|
||||
// db is non-existent | pebble default | specified type
|
||||
// db is existent | from db | specified type (if compatible)
|
||||
func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) {
|
||||
// Reject any unsupported database type
|
||||
@@ -527,12 +602,9 @@ func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) {
|
||||
log.Info("Using leveldb as the backing database")
|
||||
return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
|
||||
}
|
||||
// No pre-existing database, no user-requested one either. Default to Pebble
|
||||
// on supported platforms and LevelDB on anything else.
|
||||
// log.Info("Defaulting to pebble as the backing database")
|
||||
// return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
|
||||
log.Info("Defaulting to leveldb as the backing database")
|
||||
return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
|
||||
// No pre-existing database, no user-requested one either. Default to Pebble.
|
||||
log.Info("Defaulting to pebble as the backing database")
|
||||
return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
|
||||
}
|
||||
|
||||
// Open opens both a disk-based key-value database such as leveldb or pebble, but also
|
||||
@@ -595,7 +667,7 @@ func AncientInspect(db ethdb.Database) error {
|
||||
offset := counter(ReadOffSetOfCurrentAncientFreezer(db))
|
||||
// Get number of ancient rows inside the freezer.
|
||||
ancients := counter(0)
|
||||
if count, err := db.ItemAmountInAncient(); err != nil {
|
||||
if count, err := db.BlockStore().ItemAmountInAncient(); err != nil {
|
||||
log.Error("failed to get the items amount in ancientDB", "err", err)
|
||||
return err
|
||||
} else {
|
||||
@@ -643,6 +715,48 @@ func PruneHashTrieNodeInDataBase(db ethdb.Database) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type DataType int
|
||||
|
||||
const (
|
||||
StateDataType DataType = iota
|
||||
BlockDataType
|
||||
ChainDataType
|
||||
Unknown
|
||||
)
|
||||
|
||||
func DataTypeByKey(key []byte) DataType {
|
||||
switch {
|
||||
// state
|
||||
case IsLegacyTrieNode(key, key),
|
||||
bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength,
|
||||
IsAccountTrieNode(key),
|
||||
IsStorageTrieNode(key):
|
||||
return StateDataType
|
||||
|
||||
// block
|
||||
case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength),
|
||||
bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength),
|
||||
bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength),
|
||||
bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix),
|
||||
bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix),
|
||||
bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
|
||||
return BlockDataType
|
||||
default:
|
||||
for _, meta := range [][]byte{
|
||||
fastTrieProgressKey, persistentStateIDKey, trieJournalKey, snapSyncStatusFlagKey} {
|
||||
if bytes.Equal(key, meta) {
|
||||
return StateDataType
|
||||
}
|
||||
}
|
||||
for _, meta := range [][]byte{headHeaderKey, headFinalizedBlockKey} {
|
||||
if bytes.Equal(key, meta) {
|
||||
return BlockDataType
|
||||
}
|
||||
}
|
||||
return ChainDataType
|
||||
}
|
||||
}
|
||||
|
||||
// InspectDatabase traverses the entire database and checks the size
|
||||
// of all different categories of data.
|
||||
func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
@@ -650,10 +764,15 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
defer it.Release()
|
||||
|
||||
var trieIter ethdb.Iterator
|
||||
var blockIter ethdb.Iterator
|
||||
if db.StateStore() != nil {
|
||||
trieIter = db.StateStore().NewIterator(keyPrefix, nil)
|
||||
defer trieIter.Release()
|
||||
}
|
||||
if db.BlockStore() != db {
|
||||
blockIter = db.BlockStore().NewIterator(keyPrefix, nil)
|
||||
defer blockIter.Release()
|
||||
}
|
||||
var (
|
||||
count int64
|
||||
start = time.Now()
|
||||
@@ -783,6 +902,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
value = trieIter.Value()
|
||||
size = common.StorageSize(len(key) + len(value))
|
||||
)
|
||||
total += size
|
||||
|
||||
switch {
|
||||
case IsLegacyTrieNode(key, value):
|
||||
@@ -796,9 +916,10 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
default:
|
||||
var accounted bool
|
||||
for _, meta := range [][]byte{
|
||||
fastTrieProgressKey, persistentStateIDKey, trieJournalKey} {
|
||||
fastTrieProgressKey, persistentStateIDKey, trieJournalKey, snapSyncStatusFlagKey} {
|
||||
if bytes.Equal(key, meta) {
|
||||
metadata.Add(size)
|
||||
accounted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -812,6 +933,54 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
logged = time.Now()
|
||||
}
|
||||
}
|
||||
log.Info("Inspecting separate state database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
// inspect separate block db
|
||||
if blockIter != nil {
|
||||
count = 0
|
||||
logged = time.Now()
|
||||
|
||||
for blockIter.Next() {
|
||||
var (
|
||||
key = blockIter.Key()
|
||||
value = blockIter.Value()
|
||||
size = common.StorageSize(len(key) + len(value))
|
||||
)
|
||||
total += size
|
||||
|
||||
switch {
|
||||
case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
|
||||
headers.Add(size)
|
||||
case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
|
||||
bodies.Add(size)
|
||||
case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
|
||||
receipts.Add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
|
||||
tds.Add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
|
||||
numHashPairings.Add(size)
|
||||
case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
|
||||
hashNumPairings.Add(size)
|
||||
default:
|
||||
var accounted bool
|
||||
for _, meta := range [][]byte{headHeaderKey, headFinalizedBlockKey} {
|
||||
if bytes.Equal(key, meta) {
|
||||
metadata.Add(size)
|
||||
accounted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !accounted {
|
||||
unaccounted.Add(size)
|
||||
}
|
||||
}
|
||||
count++
|
||||
if count%1000 == 0 && time.Since(logged) > 8*time.Second {
|
||||
log.Info("Inspecting separate block database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
logged = time.Now()
|
||||
}
|
||||
}
|
||||
log.Info("Inspecting separate block database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
// Display the database statistic of key-value store.
|
||||
stats := [][]string{
|
||||
@@ -838,7 +1007,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
{"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()},
|
||||
}
|
||||
// Inspect all registered append-only file store then.
|
||||
ancients, err := inspectFreezers(db)
|
||||
ancients, err := inspectFreezers(db.BlockStore())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -887,6 +1056,62 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteTrieState(db ethdb.Database) error {
|
||||
var (
|
||||
it ethdb.Iterator
|
||||
batch = db.NewBatch()
|
||||
start = time.Now()
|
||||
logged = time.Now()
|
||||
count int64
|
||||
key []byte
|
||||
)
|
||||
|
||||
prefixKeys := map[string]func([]byte) bool{
|
||||
string(trieNodeAccountPrefix): IsAccountTrieNode,
|
||||
string(trieNodeStoragePrefix): IsStorageTrieNode,
|
||||
string(stateIDPrefix): func(key []byte) bool { return len(key) == len(stateIDPrefix)+common.HashLength },
|
||||
}
|
||||
|
||||
for prefix, isValid := range prefixKeys {
|
||||
it = db.NewIterator([]byte(prefix), nil)
|
||||
|
||||
for it.Next() {
|
||||
key = it.Key()
|
||||
if !isValid(key) {
|
||||
continue
|
||||
}
|
||||
|
||||
batch.Delete(it.Key())
|
||||
if batch.ValueSize() > ethdb.IdealBatchSize {
|
||||
if err := batch.Write(); err != nil {
|
||||
it.Release()
|
||||
return err
|
||||
}
|
||||
batch.Reset()
|
||||
}
|
||||
|
||||
count++
|
||||
if time.Since(logged) > 8*time.Second {
|
||||
log.Info("Deleting trie state", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
logged = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
it.Release()
|
||||
}
|
||||
|
||||
if batch.ValueSize() > 0 {
|
||||
if err := batch.Write(); err != nil {
|
||||
return err
|
||||
}
|
||||
batch.Reset()
|
||||
}
|
||||
|
||||
log.Info("Deleted trie state", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// printChainMetadata prints out chain metadata to stderr.
|
||||
func printChainMetadata(db ethdb.KeyValueStore) {
|
||||
fmt.Fprintf(os.Stderr, "Chain metadata\n")
|
||||
@@ -921,3 +1146,26 @@ func ReadChainMetadata(db ethdb.KeyValueStore) [][]string {
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func ReadChainMetadataFromMultiDatabase(db ethdb.Database) [][]string {
|
||||
pp := func(val *uint64) string {
|
||||
if val == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return fmt.Sprintf("%d (%#x)", *val, *val)
|
||||
}
|
||||
data := [][]string{
|
||||
{"databaseVersion", pp(ReadDatabaseVersion(db))},
|
||||
{"headBlockHash", fmt.Sprintf("%v", ReadHeadBlockHash(db.BlockStore()))},
|
||||
{"headFastBlockHash", fmt.Sprintf("%v", ReadHeadFastBlockHash(db))},
|
||||
{"headHeaderHash", fmt.Sprintf("%v", ReadHeadHeaderHash(db.BlockStore()))},
|
||||
{"lastPivotNumber", pp(ReadLastPivotNumber(db))},
|
||||
{"len(snapshotSyncStatus)", fmt.Sprintf("%d bytes", len(ReadSnapshotSyncStatus(db)))},
|
||||
{"snapshotDisabled", fmt.Sprintf("%v", ReadSnapshotDisabled(db))},
|
||||
{"snapshotJournal", fmt.Sprintf("%d bytes", len(ReadSnapshotJournal(db)))},
|
||||
{"snapshotRecoveryNumber", pp(ReadSnapshotRecoveryNumber(db))},
|
||||
{"snapshotRoot", fmt.Sprintf("%v", ReadSnapshotRoot(db))},
|
||||
{"txIndexTail", pp(ReadTxIndexTail(db))},
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
@@ -88,6 +90,7 @@ func NewChainFreezer(datadir string, namespace string, readonly bool, offset uin
|
||||
//
|
||||
// The 'tables' argument defines the data tables. If the value of a map
|
||||
// entry is true, snappy compression is disabled for the table.
|
||||
// additionTables indicates the new add tables for freezerDB, it has some special rules.
|
||||
func NewFreezer(datadir string, namespace string, readonly bool, offset uint64, maxTableSize uint32, tables map[string]bool) (*Freezer, error) {
|
||||
// Create the initial freezer object
|
||||
var (
|
||||
@@ -128,7 +131,15 @@ func NewFreezer(datadir string, namespace string, readonly bool, offset uint64,
|
||||
|
||||
// Create the tables.
|
||||
for name, disableSnappy := range tables {
|
||||
table, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, readonly)
|
||||
var (
|
||||
table *freezerTable
|
||||
err error
|
||||
)
|
||||
if slices.Contains(additionTables, name) {
|
||||
table, err = openAdditionTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, readonly)
|
||||
} else {
|
||||
table, err = newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, readonly)
|
||||
}
|
||||
if err != nil {
|
||||
for _, table := range freezer.tables {
|
||||
table.Close()
|
||||
@@ -167,6 +178,20 @@ func NewFreezer(datadir string, namespace string, readonly bool, offset uint64,
|
||||
return freezer, nil
|
||||
}
|
||||
|
||||
// openAdditionTable create table, it will auto create new files when it was first initialized
|
||||
func openAdditionTable(datadir, name string, readMeter, writeMeter metrics.Meter, sizeGauge metrics.Gauge, maxTableSize uint32, disableSnappy, readonly bool) (*freezerTable, error) {
|
||||
if readonly {
|
||||
f, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = f.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, readonly)
|
||||
}
|
||||
|
||||
// Close terminates the chain freezer, unmapping all the data files.
|
||||
func (f *Freezer) Close() error {
|
||||
f.writeLock.Lock()
|
||||
@@ -214,7 +239,7 @@ func (f *Freezer) Ancient(kind string, number uint64) ([]byte, error) {
|
||||
// - if maxBytes is not specified, 'count' items will be returned if they are present.
|
||||
func (f *Freezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) {
|
||||
if table := f.tables[kind]; table != nil {
|
||||
return table.RetrieveItems(start, count, maxBytes)
|
||||
return table.RetrieveItems(start-f.offset, count, maxBytes)
|
||||
}
|
||||
return nil, errUnknownTable
|
||||
}
|
||||
@@ -224,6 +249,12 @@ func (f *Freezer) Ancients() (uint64, error) {
|
||||
return f.frozen.Load(), nil
|
||||
}
|
||||
|
||||
func (f *Freezer) TableAncients(kind string) (uint64, error) {
|
||||
f.writeLock.RLock()
|
||||
defer f.writeLock.RUnlock()
|
||||
return f.tables[kind].items.Load() + f.offset, nil
|
||||
}
|
||||
|
||||
// ItemAmountInAncient returns the actual length of current ancientDB.
|
||||
func (f *Freezer) ItemAmountInAncient() (uint64, error) {
|
||||
return f.frozen.Load() - atomic.LoadUint64(&f.offset), nil
|
||||
@@ -308,8 +339,23 @@ func (f *Freezer) TruncateHead(items uint64) (uint64, error) {
|
||||
if oitems <= items {
|
||||
return oitems, nil
|
||||
}
|
||||
for _, table := range f.tables {
|
||||
if err := table.truncateHead(items - f.offset); err != nil {
|
||||
for kind, table := range f.tables {
|
||||
err := table.truncateHead(items - f.offset)
|
||||
if err == errTruncationBelowTail {
|
||||
// This often happens in chain rewinds, but the blob table is special.
|
||||
// It has the same head, but a different tail from other tables (like bodies, receipts).
|
||||
// So if the chain is rewound to head below the blob's tail, it needs to reset again.
|
||||
if kind != ChainFreezerBlobSidecarTable {
|
||||
return 0, err
|
||||
}
|
||||
nt, err := table.resetItems(items - f.offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
f.tables[kind] = nt
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
@@ -365,6 +411,10 @@ func (f *Freezer) validate() error {
|
||||
)
|
||||
// Hack to get boundary of any table
|
||||
for kind, table := range f.tables {
|
||||
// addition tables is special cases
|
||||
if slices.Contains(additionTables, kind) {
|
||||
continue
|
||||
}
|
||||
head = table.items.Load()
|
||||
tail = table.itemHidden.Load()
|
||||
name = kind
|
||||
@@ -372,6 +422,21 @@ func (f *Freezer) validate() error {
|
||||
}
|
||||
// Now check every table against those boundaries.
|
||||
for kind, table := range f.tables {
|
||||
// check addition tables, try to align with exist tables
|
||||
if slices.Contains(additionTables, kind) {
|
||||
// if the table is empty, just skip
|
||||
if EmptyTable(table) {
|
||||
continue
|
||||
}
|
||||
// otherwise, just align head
|
||||
if head != table.items.Load() {
|
||||
return fmt.Errorf("freezer tables %s and %s have differing head: %d != %d", kind, name, table.items.Load(), head)
|
||||
}
|
||||
if tail > table.itemHidden.Load() {
|
||||
return fmt.Errorf("freezer tables %s and %s have differing tail: %d != %d", kind, name, table.itemHidden.Load(), tail)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if head != table.items.Load() {
|
||||
return fmt.Errorf("freezer tables %s and %s have differing head: %d != %d", kind, name, table.items.Load(), head)
|
||||
}
|
||||
@@ -390,7 +455,18 @@ func (f *Freezer) repair() error {
|
||||
head = uint64(math.MaxUint64)
|
||||
tail = uint64(0)
|
||||
)
|
||||
for _, table := range f.tables {
|
||||
for kind, table := range f.tables {
|
||||
// addition tables only align head
|
||||
if slices.Contains(additionTables, kind) {
|
||||
if EmptyTable(table) {
|
||||
continue
|
||||
}
|
||||
items := table.items.Load()
|
||||
if head > items {
|
||||
head = items
|
||||
}
|
||||
continue
|
||||
}
|
||||
items := table.items.Load()
|
||||
if head > items {
|
||||
head = items
|
||||
@@ -400,8 +476,27 @@ func (f *Freezer) repair() error {
|
||||
tail = hidden
|
||||
}
|
||||
}
|
||||
for _, table := range f.tables {
|
||||
if err := table.truncateHead(head); err != nil {
|
||||
for kind, table := range f.tables {
|
||||
// try to align with exist tables, skip empty table
|
||||
if slices.Contains(additionTables, kind) && EmptyTable(table) {
|
||||
continue
|
||||
}
|
||||
err := table.truncateHead(head)
|
||||
if err == errTruncationBelowTail {
|
||||
// This often happens in chain rewinds, but the blob table is special.
|
||||
// It has the same head, but a different tail from other tables (like bodies, receipts).
|
||||
// So if the chain is rewound to head below the blob's tail, it needs to reset again.
|
||||
if kind != ChainFreezerBlobSidecarTable {
|
||||
return err
|
||||
}
|
||||
nt, err := table.resetItems(head)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.tables[kind] = nt
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := table.truncateTail(tail); err != nil {
|
||||
@@ -602,3 +697,78 @@ func (f *Freezer) MigrateTable(kind string, convert convertLegacyFn) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TruncateTableTail will truncate certain table to new tail
|
||||
func (f *Freezer) TruncateTableTail(kind string, tail uint64) (uint64, error) {
|
||||
if f.readonly {
|
||||
return 0, errReadOnly
|
||||
}
|
||||
|
||||
f.writeLock.Lock()
|
||||
defer f.writeLock.Unlock()
|
||||
|
||||
if !slices.Contains(additionTables, kind) {
|
||||
return 0, errors.New("only new added table could be truncated independently")
|
||||
}
|
||||
if tail < f.offset {
|
||||
return 0, errors.New("the input tail&head is less than offset")
|
||||
}
|
||||
t, exist := f.tables[kind]
|
||||
if !exist {
|
||||
return 0, errors.New("you reset a non-exist table")
|
||||
}
|
||||
|
||||
old := t.itemHidden.Load() + f.offset
|
||||
if err := t.truncateTail(tail - f.offset); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return old, nil
|
||||
}
|
||||
|
||||
// ResetTable will reset certain table with new start point
|
||||
// only used for ChainFreezerBlobSidecarTable now
|
||||
func (f *Freezer) ResetTable(kind string, startAt uint64, onlyEmpty bool) error {
|
||||
if f.readonly {
|
||||
return errReadOnly
|
||||
}
|
||||
|
||||
f.writeLock.Lock()
|
||||
defer f.writeLock.Unlock()
|
||||
|
||||
t, exist := f.tables[kind]
|
||||
if !exist {
|
||||
return errors.New("you reset a non-exist table")
|
||||
}
|
||||
|
||||
// if you reset a non empty table just skip
|
||||
if onlyEmpty && !EmptyTable(t) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := f.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
nt, err := t.resetItems(startAt - f.offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.tables[kind] = nt
|
||||
|
||||
// repair all tables with same tail & head
|
||||
if err := f.repair(); err != nil {
|
||||
for _, table := range f.tables {
|
||||
table.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
f.frozen.Add(f.offset)
|
||||
f.tail.Add(f.offset)
|
||||
f.writeBatch = newFreezerBatch(f)
|
||||
log.Debug("Reset Table", "kind", kind, "tail", f.tables[kind].itemHidden.Load(), "frozen", f.tables[kind].items.Load())
|
||||
return nil
|
||||
}
|
||||
|
||||
func EmptyTable(t *freezerTable) bool {
|
||||
return t.items.Load() == 0
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/golang/snappy"
|
||||
@@ -65,6 +67,10 @@ func (batch *freezerBatch) commit() (item uint64, writeSize int64, err error) {
|
||||
// Check that count agrees on all batches.
|
||||
item = uint64(math.MaxUint64)
|
||||
for name, tb := range batch.tables {
|
||||
// skip empty addition tables
|
||||
if slices.Contains(additionTables, name) && EmptyTable(tb.t) {
|
||||
continue
|
||||
}
|
||||
if item < math.MaxUint64 && tb.curItem != item {
|
||||
return 0, 0, fmt.Errorf("table %s is at item %d, want %d", name, tb.curItem, item)
|
||||
}
|
||||
|
||||
@@ -205,6 +205,22 @@ func (f *ResettableFreezer) TruncateTail(tail uint64) (uint64, error) {
|
||||
return f.freezer.TruncateTail(tail)
|
||||
}
|
||||
|
||||
// TruncateTableTail will truncate certain table to new tail
|
||||
func (f *ResettableFreezer) TruncateTableTail(kind string, tail uint64) (uint64, error) {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
|
||||
return f.freezer.TruncateTableTail(kind, tail)
|
||||
}
|
||||
|
||||
// ResetTable will reset certain table with new start point
|
||||
func (f *ResettableFreezer) ResetTable(kind string, startAt uint64, onlyEmpty bool) error {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
|
||||
return f.freezer.ResetTable(kind, startAt, onlyEmpty)
|
||||
}
|
||||
|
||||
// Sync flushes all data tables to disk.
|
||||
func (f *ResettableFreezer) Sync() error {
|
||||
f.lock.RLock()
|
||||
|
||||
@@ -44,6 +44,8 @@ var (
|
||||
|
||||
// errNotSupported is returned if the database doesn't support the required operation.
|
||||
errNotSupported = errors.New("this operation is not supported")
|
||||
|
||||
errTruncationBelowTail = errors.New("truncation below tail")
|
||||
)
|
||||
|
||||
// indexEntry contains the number/id of the file that the data resides in, as well as the
|
||||
@@ -406,7 +408,7 @@ func (t *freezerTable) truncateHead(items uint64) error {
|
||||
return nil
|
||||
}
|
||||
if items < t.itemHidden.Load() {
|
||||
return errors.New("truncation below tail")
|
||||
return errTruncationBelowTail
|
||||
}
|
||||
// We need to truncate, save the old size for metrics tracking
|
||||
oldSize, err := t.sizeNolock()
|
||||
@@ -1026,3 +1028,54 @@ func (t *freezerTable) ResetItemsOffset(virtualTail uint64) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// resetItems reset freezer table to 0 items with new startAt
|
||||
// only used for ChainFreezerBlobSidecarTable now
|
||||
func (t *freezerTable) resetItems(startAt uint64) (*freezerTable, error) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
if t.readonly {
|
||||
return nil, errors.New("resetItems in readonly mode")
|
||||
}
|
||||
|
||||
// remove all data files
|
||||
t.head.Close()
|
||||
t.releaseFilesAfter(0, true)
|
||||
t.releaseFile(0)
|
||||
|
||||
// overwrite metadata file
|
||||
if err := writeMetadata(t.meta, newMetadata(startAt)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := t.meta.Sync(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.meta.Close()
|
||||
|
||||
// recreate the index file
|
||||
t.index.Close()
|
||||
os.Remove(t.index.Name())
|
||||
var idxName string
|
||||
if t.noCompression {
|
||||
idxName = fmt.Sprintf("%s.ridx", t.name) // raw index file
|
||||
} else {
|
||||
idxName = fmt.Sprintf("%s.cidx", t.name) // compressed index file
|
||||
}
|
||||
index, err := openFreezerFileForAppend(filepath.Join(t.path, idxName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tailIndex := indexEntry{
|
||||
filenum: 0,
|
||||
offset: uint32(startAt),
|
||||
}
|
||||
if _, err = index.Write(tailIndex.append(nil)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := index.Sync(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
index.Close()
|
||||
|
||||
return newFreezerTable(t.path, t.name, t.noCompression, t.readonly)
|
||||
}
|
||||
|
||||
@@ -1370,3 +1370,76 @@ func TestRandom(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetItems(t *testing.T) {
|
||||
t.Parallel()
|
||||
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
|
||||
fname := fmt.Sprintf("truncate-tail-%d", rand.Uint64())
|
||||
|
||||
// Fill table
|
||||
f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Write 7 x 20 bytes, splitting out into four files
|
||||
batch := f.newBatch(0)
|
||||
require.NoError(t, batch.AppendRaw(0, getChunk(20, 0x00)))
|
||||
require.NoError(t, batch.AppendRaw(1, getChunk(20, 0x11)))
|
||||
require.NoError(t, batch.AppendRaw(2, getChunk(20, 0x22)))
|
||||
require.NoError(t, batch.AppendRaw(3, getChunk(20, 0x33)))
|
||||
require.NoError(t, batch.AppendRaw(4, getChunk(20, 0x44)))
|
||||
require.NoError(t, batch.AppendRaw(5, getChunk(20, 0x55)))
|
||||
require.NoError(t, batch.AppendRaw(6, getChunk(20, 0x66)))
|
||||
require.NoError(t, batch.commit())
|
||||
|
||||
// nothing to do, all the items should still be there.
|
||||
f, err = f.resetItems(0)
|
||||
require.NoError(t, err)
|
||||
f, err = f.resetItems(8)
|
||||
require.NoError(t, err)
|
||||
f, err = f.resetItems(7)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(f.dumpIndexString(0, 1000))
|
||||
checkRetrieveError(t, f, map[uint64]error{
|
||||
0: errOutOfBounds,
|
||||
6: errOutOfBounds,
|
||||
})
|
||||
|
||||
// append
|
||||
batch = f.newBatch(0)
|
||||
require.Error(t, batch.AppendRaw(0, getChunk(20, 0xa0)))
|
||||
require.NoError(t, batch.AppendRaw(7, getChunk(20, 0x77)))
|
||||
require.NoError(t, batch.AppendRaw(8, getChunk(20, 0x88)))
|
||||
require.NoError(t, batch.AppendRaw(9, getChunk(20, 0x99)))
|
||||
require.NoError(t, batch.commit())
|
||||
fmt.Println(f.dumpIndexString(0, 1000))
|
||||
checkRetrieve(t, f, map[uint64][]byte{
|
||||
7: getChunk(20, 0x77),
|
||||
9: getChunk(20, 0x99),
|
||||
})
|
||||
|
||||
// Reopen the table, the deletion information should be persisted as well
|
||||
f.Close()
|
||||
f, err = newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(f.dumpIndexString(0, 1000))
|
||||
checkRetrieveError(t, f, map[uint64]error{
|
||||
0: errOutOfBounds,
|
||||
6: errOutOfBounds,
|
||||
10: errOutOfBounds,
|
||||
})
|
||||
checkRetrieve(t, f, map[uint64][]byte{
|
||||
7: getChunk(20, 0x77),
|
||||
9: getChunk(20, 0x99),
|
||||
})
|
||||
|
||||
// truncate all, the entire freezer should be deleted
|
||||
f.truncateTail(10)
|
||||
checkRetrieveError(t, f, map[uint64]error{
|
||||
0: errOutOfBounds,
|
||||
9: errOutOfBounds,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -334,6 +334,107 @@ func TestFreezerConcurrentReadonly(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFreezer_AdditionTables(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
// Open non-readonly freezer and fill individual tables
|
||||
// with different amount of data.
|
||||
f, err := NewFreezer(dir, "", false, 0, 2049, map[string]bool{"o1": true, "o2": true})
|
||||
if err != nil {
|
||||
t.Fatal("can't open freezer", err)
|
||||
}
|
||||
|
||||
var item = make([]byte, 1024)
|
||||
_, err = f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
if err := op.AppendRaw("o1", 0, item); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := op.AppendRaw("o1", 1, item); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := op.AppendRaw("o2", 0, item); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := op.AppendRaw("o2", 1, item); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
// check read only
|
||||
additionTables = []string{"a1"}
|
||||
f, err = NewFreezer(dir, "", true, 0, 2049, map[string]bool{"o1": true, "o2": true, "a1": true})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
f, err = NewFreezer(dir, "", false, 0, 2049, map[string]bool{"o1": true, "o2": true, "a1": true})
|
||||
require.NoError(t, err)
|
||||
frozen, _ := f.Ancients()
|
||||
require.NoError(t, f.ResetTable("a1", frozen, true))
|
||||
_, err = f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
if err := appendSameItem(op, []string{"o1", "o2", "a1"}, 2, item); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := appendSameItem(op, []string{"o1", "o2", "a1"}, 3, item); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := appendSameItem(op, []string{"o1", "o2", "a1"}, 4, item); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// check additional table boundary
|
||||
_, err = f.Ancient("a1", 1)
|
||||
require.Error(t, err)
|
||||
actual, err := f.Ancient("a1", 2)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, item, actual)
|
||||
|
||||
// truncate additional table, and check boundary
|
||||
_, err = f.TruncateTableTail("o1", 3)
|
||||
require.Error(t, err)
|
||||
_, err = f.TruncateTableTail("a1", 3)
|
||||
require.NoError(t, err)
|
||||
_, err = f.Ancient("a1", 2)
|
||||
require.Error(t, err)
|
||||
actual, err = f.Ancient("a1", 3)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, item, actual)
|
||||
|
||||
// check additional table head
|
||||
ancients, err := f.TableAncients("a1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(5), ancients)
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
// reopen and read
|
||||
f, err = NewFreezer(dir, "", true, 0, 2049, map[string]bool{"o1": true, "o2": true, "a1": true})
|
||||
require.NoError(t, err)
|
||||
|
||||
// recheck additional table boundary
|
||||
_, err = f.Ancient("a1", 2)
|
||||
require.Error(t, err)
|
||||
actual, err = f.Ancient("a1", 3)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, item, actual)
|
||||
ancients, err = f.TableAncients("a1")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(5), ancients)
|
||||
require.NoError(t, f.Close())
|
||||
}
|
||||
|
||||
func appendSameItem(op ethdb.AncientWriteOp, tables []string, i uint64, item []byte) error {
|
||||
for _, t := range tables {
|
||||
if err := op.AppendRaw(t, i, item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newFreezerForTesting(t *testing.T, tables map[string]bool) (*Freezer, string) {
|
||||
t.Helper()
|
||||
|
||||
|
||||
@@ -312,6 +312,10 @@ func (f *prunedfreezer) freeze() {
|
||||
}
|
||||
}
|
||||
|
||||
func (f *prunedfreezer) SetupFreezerEnv(env *ethdb.FreezerEnv) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *prunedfreezer) ReadAncients(fn func(ethdb.AncientReaderOp) error) (err error) {
|
||||
return fn(f)
|
||||
}
|
||||
@@ -323,3 +327,13 @@ func (f *prunedfreezer) AncientRange(kind string, start, count, maxBytes uint64)
|
||||
func (f *prunedfreezer) ModifyAncients(func(ethdb.AncientWriteOp) error) (int64, error) {
|
||||
return 0, errNotSupported
|
||||
}
|
||||
|
||||
// TruncateTableTail will truncate certain table to new tail
|
||||
func (f *prunedfreezer) TruncateTableTail(kind string, tail uint64) (uint64, error) {
|
||||
return 0, errNotSupported
|
||||
}
|
||||
|
||||
// ResetTable will reset certain table with new start point
|
||||
func (f *prunedfreezer) ResetTable(kind string, startAt uint64, onlyEmpty bool) error {
|
||||
return errNotSupported
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ var (
|
||||
// headFastBlockKey tracks the latest known incomplete block's hash during fast sync.
|
||||
headFastBlockKey = []byte("LastFast")
|
||||
|
||||
// headFinalizedBlockKey tracks the latest known finalized block hash.
|
||||
headFinalizedBlockKey = []byte("LastFinalized")
|
||||
|
||||
// persistentStateIDKey tracks the id of latest stored state(for path-based only).
|
||||
persistentStateIDKey = []byte("LastStateID")
|
||||
|
||||
@@ -149,6 +152,8 @@ var (
|
||||
CliqueSnapshotPrefix = []byte("clique-")
|
||||
ParliaSnapshotPrefix = []byte("parlia-")
|
||||
|
||||
BlockBlobSidecarsPrefix = []byte("blobs")
|
||||
|
||||
preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil)
|
||||
preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil)
|
||||
)
|
||||
@@ -203,6 +208,11 @@ func blockReceiptsKey(number uint64, hash common.Hash) []byte {
|
||||
return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
}
|
||||
|
||||
// blockBlobSidecarsKey = BlockBlobSidecarsPrefix + blockNumber (uint64 big endian) + blockHash
|
||||
func blockBlobSidecarsKey(number uint64, hash common.Hash) []byte {
|
||||
return append(append(BlockBlobSidecarsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||
}
|
||||
|
||||
// diffLayerKey = diffLayerKeyPrefix + hash
|
||||
func diffLayerKey(hash common.Hash) []byte {
|
||||
return append(diffLayerPrefix, hash.Bytes()...)
|
||||
|
||||
@@ -27,6 +27,22 @@ type table struct {
|
||||
prefix string
|
||||
}
|
||||
|
||||
func (t *table) BlockStoreReader() ethdb.Reader {
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *table) BlockStoreWriter() ethdb.Writer {
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *table) BlockStore() ethdb.Database {
|
||||
return t
|
||||
}
|
||||
|
||||
func (t *table) SetBlockStore(block ethdb.Database) {
|
||||
panic("not implement")
|
||||
}
|
||||
|
||||
// NewTable returns a database object that prefixes all keys with a given string.
|
||||
func NewTable(db ethdb.Database, prefix string) ethdb.Database {
|
||||
return &table{
|
||||
@@ -101,6 +117,16 @@ func (t *table) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (int64, erro
|
||||
return t.db.ModifyAncients(fn)
|
||||
}
|
||||
|
||||
// TruncateTableTail will truncate certain table to new tail
|
||||
func (t *table) TruncateTableTail(kind string, tail uint64) (uint64, error) {
|
||||
return t.db.TruncateTableTail(kind, tail)
|
||||
}
|
||||
|
||||
// ResetTable will reset certain table with new start point
|
||||
func (t *table) ResetTable(kind string, startAt uint64, onlyEmpty bool) error {
|
||||
return t.db.ResetTable(kind, startAt, onlyEmpty)
|
||||
}
|
||||
|
||||
func (t *table) ReadAncients(fn func(reader ethdb.AncientReaderOp) error) (err error) {
|
||||
return t.db.ReadAncients(fn)
|
||||
}
|
||||
@@ -237,6 +263,10 @@ func (t *table) NewSnapshot() (ethdb.Snapshot, error) {
|
||||
return t.db.NewSnapshot()
|
||||
}
|
||||
|
||||
func (t *table) SetupFreezerEnv(env *ethdb.FreezerEnv) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// tableBatch is a wrapper around a database batch that prefixes each key access
|
||||
// with a pre-configured string.
|
||||
type tableBatch struct {
|
||||
|
||||
@@ -444,6 +444,10 @@ func (mode VerifyMode) NeedRemoteVerify() bool {
|
||||
return mode == FullVerify || mode == InsecureVerify
|
||||
}
|
||||
|
||||
func (mode VerifyMode) NoTries() bool {
|
||||
return mode != LocalVerify
|
||||
}
|
||||
|
||||
func newVerifyMsgTypeGauge(msgType uint16, peerId string) metrics.Gauge {
|
||||
m := fmt.Sprintf("verifymanager/message/%d/peer/%s", msgType, peerId)
|
||||
return metrics.GetOrRegisterGauge(m, nil)
|
||||
|
||||
@@ -402,7 +402,7 @@ func (p *BlockPruner) backUpOldDb(name string, cache, handles int, namespace str
|
||||
|
||||
var oldOffSet uint64
|
||||
if interrupt {
|
||||
// The interrupt scecario within this function is specific for old and new ancientDB exsisted concurrently,
|
||||
// The interrupt scecario within this function is specific for old and new ancientDB existed concurrently,
|
||||
// should use last version of offset for oldAncientDB, because current offset is
|
||||
// actually of the new ancientDB_Backup, but what we want is the offset of ancientDB being backup.
|
||||
oldOffSet = rawdb.ReadOffSetOfLastAncientFreezer(chainDb)
|
||||
@@ -453,8 +453,11 @@ func (p *BlockPruner) backUpOldDb(name string, cache, handles int, namespace str
|
||||
if td == nil {
|
||||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
// if there has blobs, it needs to back up too.
|
||||
blobs := rawdb.ReadBlobSidecars(chainDb, blockHash, blockNumber)
|
||||
block = block.WithSidecars(blobs)
|
||||
// Write into new ancient_back db.
|
||||
if _, err := rawdb.WriteAncientBlocks(frdbBack, []*types.Block{block}, []types.Receipts{receipts}, td); err != nil {
|
||||
if _, err := rawdb.WriteAncientBlocksWithBlobs(frdbBack, []*types.Block{block}, []types.Receipts{receipts}, td); err != nil {
|
||||
log.Error("failed to write new ancient", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1535,11 +1535,11 @@ func (s *StateDB) Commit(block uint64, failPostCommitFunc func(), postCommitFunc
|
||||
}
|
||||
|
||||
tasks := make(chan func())
|
||||
type tastResult struct {
|
||||
type taskResult struct {
|
||||
err error
|
||||
nodeSet *trienode.NodeSet
|
||||
}
|
||||
taskResults := make(chan tastResult, len(s.stateObjectsDirty))
|
||||
taskResults := make(chan taskResult, len(s.stateObjectsDirty))
|
||||
tasksNum := 0
|
||||
finishCh := make(chan struct{})
|
||||
|
||||
@@ -1566,13 +1566,13 @@ func (s *StateDB) Commit(block uint64, failPostCommitFunc func(), postCommitFunc
|
||||
// Write any storage changes in the state object to its storage trie
|
||||
if !s.noTrie {
|
||||
if set, err := obj.commit(); err != nil {
|
||||
taskResults <- tastResult{err, nil}
|
||||
taskResults <- taskResult{err, nil}
|
||||
return
|
||||
} else {
|
||||
taskResults <- tastResult{nil, set}
|
||||
taskResults <- taskResult{nil, set}
|
||||
}
|
||||
} else {
|
||||
taskResults <- tastResult{nil, nil}
|
||||
taskResults <- taskResult{nil, nil}
|
||||
}
|
||||
}
|
||||
tasksNum++
|
||||
@@ -1896,6 +1896,10 @@ func (s *StateDB) convertAccountSet(set map[common.Address]*types.StateAccount)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *StateDB) GetSnap() snapshot.Snapshot {
|
||||
return s.snap
|
||||
}
|
||||
|
||||
// copySet returns a deep-copied set.
|
||||
func copySet[k comparable](set map[k][]byte) map[k][]byte {
|
||||
copied := make(map[k][]byte, len(set))
|
||||
|
||||
@@ -113,6 +113,12 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
continue
|
||||
}
|
||||
}
|
||||
if p.config.IsCancun(block.Number(), block.Time()) {
|
||||
if len(systemTxs) > 0 {
|
||||
// systemTxs should be always at the end of block.
|
||||
return statedb, nil, nil, 0, fmt.Errorf("normal tx %d [%v] after systemTx", i, tx.Hash().Hex())
|
||||
}
|
||||
}
|
||||
|
||||
msg, err := TransactionToMessage(tx, signer, header.BaseFee)
|
||||
if err != nil {
|
||||
@@ -233,11 +239,11 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
|
||||
GasPrice: common.Big0,
|
||||
GasFeeCap: common.Big0,
|
||||
GasTipCap: common.Big0,
|
||||
To: ¶ms.BeaconRootsStorageAddress,
|
||||
To: ¶ms.BeaconRootsAddress,
|
||||
Data: beaconRoot[:],
|
||||
}
|
||||
vmenv.Reset(NewEVMTxContext(msg), statedb)
|
||||
statedb.AddAddressToAccessList(params.BeaconRootsStorageAddress)
|
||||
statedb.AddAddressToAccessList(params.BeaconRootsAddress)
|
||||
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
|
||||
statedb.Finalise(true)
|
||||
}
|
||||
|
||||
@@ -465,6 +465,13 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
|
||||
// consensus engine is parlia
|
||||
if st.evm.ChainConfig().Parlia != nil {
|
||||
st.state.AddBalance(consensus.SystemAddress, fee)
|
||||
// add extra blob fee reward
|
||||
if rules.IsCancun {
|
||||
blobFee := new(big.Int).SetUint64(st.blobGasUsed())
|
||||
blobFee.Mul(blobFee, st.evm.Context.BlobBaseFee)
|
||||
blobFeeU256, _ := uint256.FromBig(blobFee)
|
||||
st.state.AddBalance(consensus.SystemAddress, blobFeeU256)
|
||||
}
|
||||
} else {
|
||||
st.state.AddBalance(st.evm.Context.Coinbase, fee)
|
||||
}
|
||||
|
||||
1
core/systemcontracts/bruno/chapel/ValidatorContract
Normal file
1
core/systemcontracts/bruno/chapel/ValidatorContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/bruno/mainnet/ValidatorContract
Normal file
1
core/systemcontracts/bruno/mainnet/ValidatorContract
Normal file
File diff suppressed because one or more lines are too long
15
core/systemcontracts/bruno/types.go
Normal file
15
core/systemcontracts/bruno/types.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package bruno
|
||||
|
||||
import _ "embed"
|
||||
|
||||
// contract codes for Mainnet upgrade
|
||||
var (
|
||||
//go:embed mainnet/ValidatorContract
|
||||
MainnetValidatorContract string
|
||||
)
|
||||
|
||||
// contract codes for Chapel upgrade
|
||||
var (
|
||||
//go:embed chapel/ValidatorContract
|
||||
ChapelValidatorContract string
|
||||
)
|
||||
1
core/systemcontracts/euler/chapel/SlashContract
Normal file
1
core/systemcontracts/euler/chapel/SlashContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/euler/chapel/ValidatorContract
Normal file
1
core/systemcontracts/euler/chapel/ValidatorContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/euler/mainnet/SlashContract
Normal file
1
core/systemcontracts/euler/mainnet/SlashContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/euler/mainnet/ValidatorContract
Normal file
1
core/systemcontracts/euler/mainnet/ValidatorContract
Normal file
File diff suppressed because one or more lines are too long
19
core/systemcontracts/euler/types.go
Normal file
19
core/systemcontracts/euler/types.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package euler
|
||||
|
||||
import _ "embed"
|
||||
|
||||
// contract codes for Mainnet upgrade
|
||||
var (
|
||||
//go:embed mainnet/ValidatorContract
|
||||
MainnetValidatorContract string
|
||||
//go:embed mainnet/SlashContract
|
||||
MainnetSlashContract string
|
||||
)
|
||||
|
||||
// contract codes for Chapel upgrade
|
||||
var (
|
||||
//go:embed chapel/ValidatorContract
|
||||
ChapelValidatorContract string
|
||||
//go:embed chapel/SlashContract
|
||||
ChapelSlashContract string
|
||||
)
|
||||
1
core/systemcontracts/feynman/chapel/CrossChainContract
Normal file
1
core/systemcontracts/feynman/chapel/CrossChainContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/chapel/GovHubContract
Normal file
1
core/systemcontracts/feynman/chapel/GovHubContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/chapel/GovTokenContract
Normal file
1
core/systemcontracts/feynman/chapel/GovTokenContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/chapel/GovernorContract
Normal file
1
core/systemcontracts/feynman/chapel/GovernorContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/chapel/SlashContract
Normal file
1
core/systemcontracts/feynman/chapel/SlashContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/chapel/StakeCreditContract
Normal file
1
core/systemcontracts/feynman/chapel/StakeCreditContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/chapel/StakeHubContract
Normal file
1
core/systemcontracts/feynman/chapel/StakeHubContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/chapel/StakingContract
Normal file
1
core/systemcontracts/feynman/chapel/StakingContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/chapel/TimelockContract
Normal file
1
core/systemcontracts/feynman/chapel/TimelockContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/chapel/TokenHubContract
Normal file
1
core/systemcontracts/feynman/chapel/TokenHubContract
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/chapel/ValidatorContract
Normal file
1
core/systemcontracts/feynman/chapel/ValidatorContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/mainnet/CrossChainContract
Normal file
1
core/systemcontracts/feynman/mainnet/CrossChainContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/mainnet/GovHubContract
Normal file
1
core/systemcontracts/feynman/mainnet/GovHubContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/mainnet/GovTokenContract
Normal file
1
core/systemcontracts/feynman/mainnet/GovTokenContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/mainnet/GovernorContract
Normal file
1
core/systemcontracts/feynman/mainnet/GovernorContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/mainnet/SlashContract
Normal file
1
core/systemcontracts/feynman/mainnet/SlashContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/mainnet/StakeCreditContract
Normal file
1
core/systemcontracts/feynman/mainnet/StakeCreditContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/mainnet/StakeHubContract
Normal file
1
core/systemcontracts/feynman/mainnet/StakeHubContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/mainnet/StakingContract
Normal file
1
core/systemcontracts/feynman/mainnet/StakingContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/mainnet/TimelockContract
Normal file
1
core/systemcontracts/feynman/mainnet/TimelockContract
Normal file
File diff suppressed because one or more lines are too long
1
core/systemcontracts/feynman/mainnet/TokenHubContract
Normal file
1
core/systemcontracts/feynman/mainnet/TokenHubContract
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user