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.BlobPoolDataCapFlag,
|
||||||
utils.BlobPoolPriceBumpFlag,
|
utils.BlobPoolPriceBumpFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
|
utils.SnapSkipReceiptsFlag,
|
||||||
utils.SyncTargetFlag,
|
utils.SyncTargetFlag,
|
||||||
utils.ExitWhenSyncedFlag,
|
utils.ExitWhenSyncedFlag,
|
||||||
utils.GCModeFlag,
|
utils.GCModeFlag,
|
||||||
|
|
|
||||||
|
|
@ -280,6 +280,15 @@ var (
|
||||||
Value: ethconfig.Defaults.SyncMode.String(),
|
Value: ethconfig.Defaults.SyncMode.String(),
|
||||||
Category: flags.StateCategory,
|
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{
|
GCModeFlag = &cli.StringFlag{
|
||||||
Name: "gcmode",
|
Name: "gcmode",
|
||||||
Usage: `Blockchain garbage collection mode ("full", "archive")`,
|
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) {
|
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheDatabaseFlag.Name) {
|
||||||
cfg.DatabaseCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheDatabaseFlag.Name) / 100
|
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
|
// Permit the downloader to use the trie cache allowance during fast sync
|
||||||
cacheLimit := options.TrieCleanLimit + options.TrieDirtyLimit + options.SnapshotLimit
|
cacheLimit := options.TrieCleanLimit + options.TrieDirtyLimit + options.SnapshotLimit
|
||||||
if eth.handler, err = newHandler(&handlerConfig{
|
if eth.handler, err = newHandler(&handlerConfig{
|
||||||
NodeID: eth.p2pServer.Self().ID(),
|
NodeID: eth.p2pServer.Self().ID(),
|
||||||
Database: chainDb,
|
Database: chainDb,
|
||||||
Chain: eth.blockchain,
|
Chain: eth.blockchain,
|
||||||
TxPool: eth.txPool,
|
TxPool: eth.txPool,
|
||||||
Network: networkID,
|
Network: networkID,
|
||||||
Sync: config.SyncMode,
|
Sync: config.SyncMode,
|
||||||
BloomCache: uint64(cacheLimit),
|
SnapSkipReceipts: config.SnapSkipReceipts,
|
||||||
RequiredBlocks: config.RequiredBlocks,
|
BloomCache: uint64(cacheLimit),
|
||||||
SnapV2: config.SnapV2,
|
RequiredBlocks: config.RequiredBlocks,
|
||||||
|
SnapV2: config.SnapV2,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,12 @@ func New(stateDb ethdb.Database, mode ethconfig.SyncMode, chain BlockChain, drop
|
||||||
return dl
|
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
|
// Progress retrieves the synchronisation boundaries, specifically the origin
|
||||||
// block where synchronisation started at (may have failed/suspended); the block
|
// 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.
|
// 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
|
active *sync.Cond
|
||||||
closed bool
|
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
|
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()))
|
q.blockTaskQueue.Push(header, -int64(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
// Queue for receipt retrieval
|
// 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 {
|
if _, ok := q.receiptTaskPool[hash]; ok {
|
||||||
log.Warn("Header already scheduled for receipt fetch", "number", header.Number, "hash", hash)
|
log.Warn("Header already scheduled for receipt fetch", "number", header.Number, "hash", hash)
|
||||||
} else {
|
} 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
|
// we can ask the resultcache if this header is within the
|
||||||
// "prioritized" segment of blocks. If it is not, we need to throttle
|
// "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 {
|
if stale {
|
||||||
// Don't put back in the task queue, this item has already been
|
// Don't put back in the task queue, this item has already been
|
||||||
// delivered upstream
|
// 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) {
|
func TestEmptyBlocks(t *testing.T) {
|
||||||
numOfBlocks := len(emptyChain.blocks)
|
numOfBlocks := len(emptyChain.blocks)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,10 @@ type Config struct {
|
||||||
NetworkId uint64
|
NetworkId uint64
|
||||||
SyncMode SyncMode
|
SyncMode SyncMode
|
||||||
|
|
||||||
|
// SnapSkipReceipts skips the historical receipt backfill during snap sync,
|
||||||
|
// reconstructing state only.
|
||||||
|
SnapSkipReceipts bool
|
||||||
|
|
||||||
// HistoryMode configures chain history retention.
|
// HistoryMode configures chain history retention.
|
||||||
HistoryMode history.HistoryMode
|
HistoryMode history.HistoryMode
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,15 +100,16 @@ type txPool interface {
|
||||||
// handlerConfig is the collection of initialization parameters to create a full
|
// handlerConfig is the collection of initialization parameters to create a full
|
||||||
// node network handler.
|
// node network handler.
|
||||||
type handlerConfig struct {
|
type handlerConfig struct {
|
||||||
NodeID enode.ID // P2P node ID used for tx propagation topology
|
NodeID enode.ID // P2P node ID used for tx propagation topology
|
||||||
Database ethdb.Database // Database for direct sync insertions
|
Database ethdb.Database // Database for direct sync insertions
|
||||||
Chain *core.BlockChain // Blockchain to serve data from
|
Chain *core.BlockChain // Blockchain to serve data from
|
||||||
TxPool txPool // Transaction pool to propagate from
|
TxPool txPool // Transaction pool to propagate from
|
||||||
Network uint64 // Network identifier to advertise
|
Network uint64 // Network identifier to advertise
|
||||||
Sync ethconfig.SyncMode // Whether to snap or full sync
|
Sync ethconfig.SyncMode // Whether to snap or full sync
|
||||||
BloomCache uint64 // Megabytes to alloc for snap sync bloom
|
SnapSkipReceipts bool // Skip historical receipt backfill during snap sync (state-only)
|
||||||
RequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges
|
BloomCache uint64 // Megabytes to alloc for snap sync bloom
|
||||||
SnapV2 bool // Whether to advertise and sync via the snap/2 protocol
|
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 {
|
type handler struct {
|
||||||
|
|
@ -158,6 +159,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
|
||||||
}
|
}
|
||||||
// Construct the downloader (long sync)
|
// Construct the downloader (long sync)
|
||||||
h.downloader = downloader.New(config.Database, config.Sync, h.chain, h.removePeer, h.enableSyncedFeatures, config.SnapV2)
|
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 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) {
|
if h.downloader.ConfigSyncMode() == ethconfig.SnapSync && (config.Chain.Snapshots() == nil && config.Chain.TrieDB().Scheme() == rawdb.HashScheme) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue