mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-19 11:20:45 +00:00
eth/downloader, cmd: add --snap.skipreceipts for state-only snap sync
Add an opt-in --snap.skipreceipts flag that makes snap sync neither schedule, fetch nor validate historical block receipts. The state trie is still reconstructed and verified in full; only the receipt backfill is skipped. Receipts are execution outputs (re-derivable by re-executing blocks) and are not required to reconstruct or validate state. Skipping them lets a node snap-sync the state from a peer that does not retain historical receipts (for example a snapshot-restored, state-only source), where the default receipt backfill otherwise fails receipt-root validation and drops the only peer. This mirrors the state-only snap behaviour Nethermind already supports via its static snap pivot. The flag defaults to false, so default behaviour is unchanged.
This commit is contained in:
parent
7f3afa1b71
commit
370e60bab6
8 changed files with 78 additions and 20 deletions
|
|
@ -76,6 +76,7 @@ var (
|
|||
utils.BlobPoolDataCapFlag,
|
||||
utils.BlobPoolPriceBumpFlag,
|
||||
utils.SyncModeFlag,
|
||||
utils.SnapSkipReceiptsFlag,
|
||||
utils.SyncTargetFlag,
|
||||
utils.ExitWhenSyncedFlag,
|
||||
utils.GCModeFlag,
|
||||
|
|
|
|||
|
|
@ -280,6 +280,15 @@ var (
|
|||
Value: ethconfig.Defaults.SyncMode.String(),
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
SnapSkipReceiptsFlag = &cli.BoolFlag{
|
||||
Name: "snap.skipreceipts",
|
||||
Usage: "Skip the historical receipt backfill during snap sync, reconstructing state only. " +
|
||||
"Lets a node snap-sync from a peer that does not retain historical receipts (e.g. a state-only / " +
|
||||
"snapshot-restored source). The resulting node has no receipts for the pre-sync range, so " +
|
||||
"eth_getLogs / eth_getTransactionReceipt / eth_getBlockReceipts return a pruned-history error there. " +
|
||||
"Intended for state-growth benchmarking and trusted-peer setups, not general mainnet operation.",
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
GCModeFlag = &cli.StringFlag{
|
||||
Name: "gcmode",
|
||||
Usage: `Blockchain garbage collection mode ("full", "archive")`,
|
||||
|
|
@ -1764,6 +1773,15 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
}
|
||||
}
|
||||
|
||||
if ctx.IsSet(SnapSkipReceiptsFlag.Name) {
|
||||
cfg.SnapSkipReceipts = ctx.Bool(SnapSkipReceiptsFlag.Name)
|
||||
if cfg.SnapSkipReceipts {
|
||||
log.Warn("Snap sync receipt backfill disabled via --snap.skipreceipts: state is reconstructed in full, " +
|
||||
"but the node will have no receipts for the pre-sync range. eth_getLogs, eth_getTransactionReceipt and " +
|
||||
"eth_getBlockReceipts will report pruned history there. Use only for benchmarking / trusted-peer setups.")
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheDatabaseFlag.Name) {
|
||||
cfg.DatabaseCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheDatabaseFlag.Name) / 100
|
||||
}
|
||||
|
|
|
|||
|
|
@ -346,15 +346,16 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
|
|||
// Permit the downloader to use the trie cache allowance during fast sync
|
||||
cacheLimit := options.TrieCleanLimit + options.TrieDirtyLimit + options.SnapshotLimit
|
||||
if eth.handler, err = newHandler(&handlerConfig{
|
||||
NodeID: eth.p2pServer.Self().ID(),
|
||||
Database: chainDb,
|
||||
Chain: eth.blockchain,
|
||||
TxPool: eth.txPool,
|
||||
Network: networkID,
|
||||
Sync: config.SyncMode,
|
||||
BloomCache: uint64(cacheLimit),
|
||||
RequiredBlocks: config.RequiredBlocks,
|
||||
SnapV2: config.SnapV2,
|
||||
NodeID: eth.p2pServer.Self().ID(),
|
||||
Database: chainDb,
|
||||
Chain: eth.blockchain,
|
||||
TxPool: eth.txPool,
|
||||
Network: networkID,
|
||||
Sync: config.SyncMode,
|
||||
SnapSkipReceipts: config.SnapSkipReceipts,
|
||||
BloomCache: uint64(cacheLimit),
|
||||
RequiredBlocks: config.RequiredBlocks,
|
||||
SnapV2: config.SnapV2,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -261,6 +261,12 @@ func New(stateDb ethdb.Database, mode ethconfig.SyncMode, chain BlockChain, drop
|
|||
return dl
|
||||
}
|
||||
|
||||
// SetSkipReceipts skips the historical receipt backfill during snap sync,
|
||||
// reconstructing state only. Must be set before synchronisation starts.
|
||||
func (d *Downloader) SetSkipReceipts(v bool) {
|
||||
d.queue.skipReceipts = v
|
||||
}
|
||||
|
||||
// Progress retrieves the synchronisation boundaries, specifically the origin
|
||||
// block where synchronisation started at (may have failed/suspended); the block
|
||||
// or header sync is currently at; and the latest known block which the sync targets.
|
||||
|
|
|
|||
|
|
@ -150,6 +150,10 @@ type queue struct {
|
|||
active *sync.Cond
|
||||
closed bool
|
||||
|
||||
// skipReceipts skips scheduling and fetching receipts during snap sync;
|
||||
// state is still reconstructed in full.
|
||||
skipReceipts bool
|
||||
|
||||
logTime time.Time // Time instance when status was last reported
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +276,7 @@ func (q *queue) Schedule(headers []*types.Header, hashes []common.Hash, from uin
|
|||
q.blockTaskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||
}
|
||||
// Queue for receipt retrieval
|
||||
if q.mode == ethconfig.SnapSync && !header.EmptyReceipts() {
|
||||
if q.mode == ethconfig.SnapSync && !q.skipReceipts && !header.EmptyReceipts() {
|
||||
if _, ok := q.receiptTaskPool[hash]; ok {
|
||||
log.Warn("Header already scheduled for receipt fetch", "number", header.Number, "hash", hash)
|
||||
} else {
|
||||
|
|
@ -425,7 +429,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common
|
|||
// we can ask the resultcache if this header is within the
|
||||
// "prioritized" segment of blocks. If it is not, we need to throttle
|
||||
|
||||
stale, throttle, item, err := q.resultCache.AddFetch(header, q.mode == ethconfig.SnapSync)
|
||||
stale, throttle, item, err := q.resultCache.AddFetch(header, q.mode == ethconfig.SnapSync && !q.skipReceipts)
|
||||
if stale {
|
||||
// Don't put back in the task queue, this item has already been
|
||||
// delivered upstream
|
||||
|
|
|
|||
|
|
@ -195,6 +195,28 @@ func TestBasics(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestSkipReceipts verifies that skipReceipts schedules bodies in full but no
|
||||
// receipt tasks.
|
||||
func TestSkipReceipts(t *testing.T) {
|
||||
q := newQueue(10, 10)
|
||||
q.skipReceipts = true
|
||||
q.Prepare(1, SnapSync)
|
||||
|
||||
headers := chain.headers()
|
||||
hashes := make([]common.Hash, len(headers))
|
||||
for i, header := range headers {
|
||||
hashes[i] = header.Hash()
|
||||
}
|
||||
q.Schedule(headers, hashes, 1)
|
||||
|
||||
if got, exp := q.PendingBodies(), chain.Len(); got != exp {
|
||||
t.Errorf("wrong pending block count, got %d, exp %d", got, exp)
|
||||
}
|
||||
if got, exp := q.PendingReceipts(), 0; got != exp {
|
||||
t.Errorf("wrong pending receipt count, got %d, exp %d", got, exp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyBlocks(t *testing.T) {
|
||||
numOfBlocks := len(emptyChain.blocks)
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,10 @@ type Config struct {
|
|||
NetworkId uint64
|
||||
SyncMode SyncMode
|
||||
|
||||
// SnapSkipReceipts skips the historical receipt backfill during snap sync,
|
||||
// reconstructing state only.
|
||||
SnapSkipReceipts bool
|
||||
|
||||
// HistoryMode configures chain history retention.
|
||||
HistoryMode history.HistoryMode
|
||||
|
||||
|
|
|
|||
|
|
@ -100,15 +100,16 @@ type txPool interface {
|
|||
// handlerConfig is the collection of initialization parameters to create a full
|
||||
// node network handler.
|
||||
type handlerConfig struct {
|
||||
NodeID enode.ID // P2P node ID used for tx propagation topology
|
||||
Database ethdb.Database // Database for direct sync insertions
|
||||
Chain *core.BlockChain // Blockchain to serve data from
|
||||
TxPool txPool // Transaction pool to propagate from
|
||||
Network uint64 // Network identifier to advertise
|
||||
Sync ethconfig.SyncMode // Whether to snap or full sync
|
||||
BloomCache uint64 // Megabytes to alloc for snap sync bloom
|
||||
RequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges
|
||||
SnapV2 bool // Whether to advertise and sync via the snap/2 protocol
|
||||
NodeID enode.ID // P2P node ID used for tx propagation topology
|
||||
Database ethdb.Database // Database for direct sync insertions
|
||||
Chain *core.BlockChain // Blockchain to serve data from
|
||||
TxPool txPool // Transaction pool to propagate from
|
||||
Network uint64 // Network identifier to advertise
|
||||
Sync ethconfig.SyncMode // Whether to snap or full sync
|
||||
SnapSkipReceipts bool // Skip historical receipt backfill during snap sync (state-only)
|
||||
BloomCache uint64 // Megabytes to alloc for snap sync bloom
|
||||
RequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges
|
||||
SnapV2 bool // Whether to advertise and sync via the snap/2 protocol
|
||||
}
|
||||
|
||||
type handler struct {
|
||||
|
|
@ -158,6 +159,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
|
|||
}
|
||||
// Construct the downloader (long sync)
|
||||
h.downloader = downloader.New(config.Database, config.Sync, h.chain, h.removePeer, h.enableSyncedFeatures, config.SnapV2)
|
||||
h.downloader.SetSkipReceipts(config.SnapSkipReceipts)
|
||||
|
||||
// If snap sync is requested but snapshots are disabled, fail loudly
|
||||
if h.downloader.ConfigSyncMode() == ethconfig.SnapSync && (config.Chain.Snapshots() == nil && config.Chain.TrieDB().Scheme() == rawdb.HashScheme) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue