From 76da058ee33f24dab5cece2a82a22b0d45995820 Mon Sep 17 00:00:00 2001 From: buddh0 Date: Tue, 16 Jul 2024 15:39:32 +0800 Subject: [PATCH 1/8] consensus/parlia: support recovery when snapshot of parlia gone in disk --- consensus/parlia/parlia.go | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 7f7b6e493c..8bc3a3673f 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -54,7 +54,7 @@ const ( 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 + defaultEpochLength = uint64(200) // Default number of blocks of checkpoint to update validatorSet from contract defaultTurnLength = uint8(1) // Default consecutive number of blocks a validator receives priority for block production extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity @@ -739,13 +739,28 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash } } - // If we're at the genesis, snapshot the initial state. - if number == 0 { - checkpoint := chain.GetHeaderByNumber(number) - if checkpoint != nil { - // get checkpoint data - hash := checkpoint.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. + // even BEP-341 enabled, an offset `defaultEpochLength/2` can ensure getting the right validators. + if number == 0 || (number%p.config.Epoch == defaultEpochLength/2 && (len(headers) > int(params.FullImmutabilityThreshold)/10)) { + var ( + checkpoint *types.Header + blockHash common.Hash + ) + if number == 0 { + checkpoint := chain.GetHeaderByNumber(0) + if checkpoint != nil { + blockHash = checkpoint.Hash() + } + } else { + checkpoint = chain.GetHeaderByNumber(number - defaultEpochLength/2) + blockHeader := chain.GetHeaderByNumber(number) + if blockHeader != nil { + blockHash = blockHeader.Hash() + } + } + if checkpoint != nil && blockHash != (common.Hash{}) { // get validators from headers validators, voteAddrs, err := parseValidators(checkpoint, p.chainConfig, p.config) if err != nil { @@ -753,11 +768,11 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash } // new snapshot - snap = newSnapshot(p.config, p.signatures, number, hash, validators, voteAddrs, p.ethAPI) + snap = newSnapshot(p.config, p.signatures, number, blockHash, validators, voteAddrs, p.ethAPI) if err := snap.store(p.db); err != nil { return nil, err } - log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash) + log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", blockHash) break } } From b261714c86b2b3c6f1d03bb9a8f3ea019b18e364 Mon Sep 17 00:00:00 2001 From: buddh0 Date: Wed, 17 Jul 2024 10:51:16 +0800 Subject: [PATCH 2/8] consensus/parlia: recover history cap --- consensus/parlia/parlia.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 8bc3a3673f..4524662c88 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -743,7 +743,7 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash // piled up more headers than allowed to be reorged (chain reinit from a freezer), // consider the checkpoint trusted and snapshot it. // even BEP-341 enabled, an offset `defaultEpochLength/2` can ensure getting the right validators. - if number == 0 || (number%p.config.Epoch == defaultEpochLength/2 && (len(headers) > int(params.FullImmutabilityThreshold)/10)) { + if number == 0 || (number%p.config.Epoch == defaultEpochLength/2 && (len(headers) > int(params.FullImmutabilityThreshold))) { var ( checkpoint *types.Header blockHash common.Hash From 628213c684f572c7c7d530ead936ff8c07e16356 Mon Sep 17 00:00:00 2001 From: buddh0 Date: Wed, 17 Jul 2024 15:12:39 +0800 Subject: [PATCH 3/8] consensus/parlia: fix var assign --- consensus/parlia/parlia.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 4524662c88..53280d0bf1 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -749,7 +749,7 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash blockHash common.Hash ) if number == 0 { - checkpoint := chain.GetHeaderByNumber(0) + checkpoint = chain.GetHeaderByNumber(0) if checkpoint != nil { blockHash = checkpoint.Hash() } From 49abffd336b0d0b43420db389844ce990468e977 Mon Sep 17 00:00:00 2001 From: buddh0 Date: Thu, 18 Jul 2024 11:19:19 +0800 Subject: [PATCH 4/8] consensus/parlia: defaultEpochLength to p.config.Epoch --- consensus/parlia/parlia.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 53280d0bf1..8c3db64f87 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -742,8 +742,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. - // even BEP-341 enabled, an offset `defaultEpochLength/2` can ensure getting the right validators. - if number == 0 || (number%p.config.Epoch == defaultEpochLength/2 && (len(headers) > int(params.FullImmutabilityThreshold))) { + // even BEP-341 enabled, an offset `p.config.Epoch/2` can ensure getting the right validators. + if number == 0 || (number%p.config.Epoch == p.config.Epoch/2 && (len(headers) > int(params.FullImmutabilityThreshold))) { var ( checkpoint *types.Header blockHash common.Hash @@ -754,7 +754,7 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash blockHash = checkpoint.Hash() } } else { - checkpoint = chain.GetHeaderByNumber(number - defaultEpochLength/2) + checkpoint = chain.GetHeaderByNumber(number - p.config.Epoch/2) blockHeader := chain.GetHeaderByNumber(number) if blockHeader != nil { blockHash = blockHeader.Hash() From 16f28bdca209fed73a0acfb4ea61dd398fc55bbf Mon Sep 17 00:00:00 2001 From: buddh0 Date: Thu, 18 Jul 2024 13:55:32 +0800 Subject: [PATCH 5/8] consensus/parlia: modify the offset --- consensus/parlia/parlia.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 8c3db64f87..e33d44965e 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -742,8 +742,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. - // even BEP-341 enabled, an offset `p.config.Epoch/2` can ensure getting the right validators. - if number == 0 || (number%p.config.Epoch == p.config.Epoch/2 && (len(headers) > int(params.FullImmutabilityThreshold))) { + // An offset `p.config.Epoch - 1` can ensure getting the right validators. + if number == 0 || ((number+1)%p.config.Epoch == 0 && (len(headers) > int(params.FullImmutabilityThreshold))) { var ( checkpoint *types.Header blockHash common.Hash @@ -754,7 +754,7 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash blockHash = checkpoint.Hash() } } else { - checkpoint = chain.GetHeaderByNumber(number - p.config.Epoch/2) + checkpoint = chain.GetHeaderByNumber(number + 1 - p.config.Epoch) blockHeader := chain.GetHeaderByNumber(number) if blockHeader != nil { blockHash = blockHeader.Hash() From 83693170f636e03ac4c6f09ea8511554065983da Mon Sep 17 00:00:00 2001 From: buddh0 Date: Wed, 7 Aug 2024 18:18:48 +0800 Subject: [PATCH 6/8] consensus/parlia: add more comments --- consensus/parlia/parlia.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index e33d44965e..45fa30fb5f 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -769,6 +769,9 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash // new snapshot snap = newSnapshot(p.config, p.signatures, number, blockHash, validators, voteAddrs, p.ethAPI) + // snap.Recents is currently empty, which affects the following: + // a. The function SignRecently - This is acceptable since an empty snap.Recents results in a more lenient check. + // b. The function blockTimeVerifyForRamanujanFork - This is also acceptable as it won't be invoked during `snap.apply`. if err := snap.store(p.db); err != nil { return nil, err } From f3a47cd2fc6b2b34cb2da4534dd89ff4f8a6abe5 Mon Sep 17 00:00:00 2001 From: buddh0 Date: Wed, 7 Aug 2024 19:29:17 +0800 Subject: [PATCH 7/8] consensus/parlia: add parseTurnLength --- consensus/parlia/parlia.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 45fa30fb5f..896777f8a2 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -769,9 +769,21 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash // new snapshot snap = newSnapshot(p.config, p.signatures, number, blockHash, validators, voteAddrs, p.ethAPI) + + // get turnLength from headers and use that for new turnLength + turnLength, err := parseTurnLength(checkpoint, p.chainConfig, p.config) + if err != nil { + return nil, err + } + if turnLength != nil { + snap.TurnLength = *turnLength + } + // snap.Recents is currently empty, which affects the following: // a. The function SignRecently - This is acceptable since an empty snap.Recents results in a more lenient check. // b. The function blockTimeVerifyForRamanujanFork - This is also acceptable as it won't be invoked during `snap.apply`. + + // snap.Attestation is nil, but Snapshot.updateAttestation will handle it correctly. if err := snap.store(p.db); err != nil { return nil, err } From f82bfcf6eb7c418bc63ef92958d8075dce383137 Mon Sep 17 00:00:00 2001 From: buddh0 Date: Thu, 8 Aug 2024 09:18:49 +0800 Subject: [PATCH 8/8] consensus/parlia: add more comments --- consensus/parlia/parlia.go | 1 + 1 file changed, 1 insertion(+) diff --git a/consensus/parlia/parlia.go b/consensus/parlia/parlia.go index 896777f8a2..4a79ec344b 100644 --- a/consensus/parlia/parlia.go +++ b/consensus/parlia/parlia.go @@ -782,6 +782,7 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash // snap.Recents is currently empty, which affects the following: // a. The function SignRecently - This is acceptable since an empty snap.Recents results in a more lenient check. // b. The function blockTimeVerifyForRamanujanFork - This is also acceptable as it won't be invoked during `snap.apply`. + // c. This may cause a mismatch in the slash systemtx, but the transaction list is not verified during `snap.apply`. // snap.Attestation is nil, but Snapshot.updateAttestation will handle it correctly. if err := snap.store(p.db); err != nil {