mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2025-01-03 01:27:38 +00:00
polygon/sync: span updates (#9229)
It is possible that a span update happens during a milestone. A headers slice might cross to the new span. Also if 2 forks evolve simulaneously, a shorter fork can still be in the previous span. In these cases we need access to the previous span to calculate difficulty and validate header times. SpansCache will keep recent spans. The cache will be updated on new span events from the heimdall. The cache is pruned on new milestone events and in practice no more than 2 spans are kept. The header difficulty calculation and time validation depends on having a span for that header in the cache.
This commit is contained in:
parent
8d4d4d802c
commit
c5b75d00ca
@ -38,19 +38,20 @@ type canonicalChainBuilderImpl struct {
|
|||||||
tip *forkTreeNode
|
tip *forkTreeNode
|
||||||
|
|
||||||
difficultyCalc DifficultyCalculator
|
difficultyCalc DifficultyCalculator
|
||||||
|
|
||||||
headerValidator HeaderValidator
|
headerValidator HeaderValidator
|
||||||
|
spansCache *SpansCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCanonicalChainBuilder(
|
func NewCanonicalChainBuilder(
|
||||||
root *types.Header,
|
root *types.Header,
|
||||||
difficultyCalc DifficultyCalculator,
|
difficultyCalc DifficultyCalculator,
|
||||||
headerValidator HeaderValidator,
|
headerValidator HeaderValidator,
|
||||||
|
spansCache *SpansCache,
|
||||||
) CanonicalChainBuilder {
|
) CanonicalChainBuilder {
|
||||||
impl := &canonicalChainBuilderImpl{
|
impl := &canonicalChainBuilderImpl{
|
||||||
difficultyCalc: difficultyCalc,
|
difficultyCalc: difficultyCalc,
|
||||||
|
|
||||||
headerValidator: headerValidator,
|
headerValidator: headerValidator,
|
||||||
|
spansCache: spansCache,
|
||||||
}
|
}
|
||||||
impl.Reset(root)
|
impl.Reset(root)
|
||||||
return impl
|
return impl
|
||||||
@ -63,6 +64,9 @@ func (impl *canonicalChainBuilderImpl) Reset(root *types.Header) {
|
|||||||
headerHash: root.Hash(),
|
headerHash: root.Hash(),
|
||||||
}
|
}
|
||||||
impl.tip = impl.root
|
impl.tip = impl.root
|
||||||
|
if impl.spansCache != nil {
|
||||||
|
impl.spansCache.Prune(root.Number.Uint64())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// depth-first search
|
// depth-first search
|
||||||
@ -138,8 +142,11 @@ func (impl *canonicalChainBuilderImpl) Prune(newRootNum uint64) error {
|
|||||||
for newRoot.header.Number.Uint64() > newRootNum {
|
for newRoot.header.Number.Uint64() > newRootNum {
|
||||||
newRoot = newRoot.parent
|
newRoot = newRoot.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
impl.root = newRoot
|
impl.root = newRoot
|
||||||
|
|
||||||
|
if impl.spansCache != nil {
|
||||||
|
impl.spansCache.Prune(newRootNum)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ func makeRoot() *types.Header {
|
|||||||
|
|
||||||
func makeCCB(root *types.Header) CanonicalChainBuilder {
|
func makeCCB(root *types.Header) CanonicalChainBuilder {
|
||||||
difficultyCalc := testDifficultyCalculator{}
|
difficultyCalc := testDifficultyCalculator{}
|
||||||
builder := NewCanonicalChainBuilder(root, &difficultyCalc, nil)
|
builder := NewCanonicalChainBuilder(root, &difficultyCalc, nil, nil)
|
||||||
return builder
|
return builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package sync
|
package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
lru "github.com/hashicorp/golang-lru/arc/v2"
|
lru "github.com/hashicorp/golang-lru/arc/v2"
|
||||||
|
|
||||||
"github.com/ledgerwatch/erigon/eth/stagedsync"
|
"github.com/ledgerwatch/erigon/eth/stagedsync"
|
||||||
|
|
||||||
heimdallspan "github.com/ledgerwatch/erigon/polygon/heimdall/span"
|
|
||||||
|
|
||||||
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
||||||
"github.com/ledgerwatch/erigon/core/types"
|
"github.com/ledgerwatch/erigon/core/types"
|
||||||
"github.com/ledgerwatch/erigon/polygon/bor"
|
"github.com/ledgerwatch/erigon/polygon/bor"
|
||||||
@ -16,20 +16,19 @@ import (
|
|||||||
|
|
||||||
type DifficultyCalculator interface {
|
type DifficultyCalculator interface {
|
||||||
HeaderDifficulty(header *types.Header) (uint64, error)
|
HeaderDifficulty(header *types.Header) (uint64, error)
|
||||||
SetSpan(span *heimdallspan.HeimdallSpan)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type difficultyCalculatorImpl struct {
|
type difficultyCalculatorImpl struct {
|
||||||
borConfig *borcfg.BorConfig
|
borConfig *borcfg.BorConfig
|
||||||
span *heimdallspan.HeimdallSpan
|
spans *SpansCache
|
||||||
validatorSetFactory func() validatorSetInterface
|
validatorSetFactory func(headerNum uint64) validatorSetInterface
|
||||||
signaturesCache *lru.ARCCache[libcommon.Hash, libcommon.Address]
|
signaturesCache *lru.ARCCache[libcommon.Hash, libcommon.Address]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDifficultyCalculator(
|
func NewDifficultyCalculator(
|
||||||
borConfig *borcfg.BorConfig,
|
borConfig *borcfg.BorConfig,
|
||||||
span *heimdallspan.HeimdallSpan,
|
spans *SpansCache,
|
||||||
validatorSetFactory func() validatorSetInterface,
|
validatorSetFactory func(headerNum uint64) validatorSetInterface,
|
||||||
signaturesCache *lru.ARCCache[libcommon.Hash, libcommon.Address],
|
signaturesCache *lru.ARCCache[libcommon.Hash, libcommon.Address],
|
||||||
) DifficultyCalculator {
|
) DifficultyCalculator {
|
||||||
if signaturesCache == nil {
|
if signaturesCache == nil {
|
||||||
@ -42,7 +41,7 @@ func NewDifficultyCalculator(
|
|||||||
|
|
||||||
impl := difficultyCalculatorImpl{
|
impl := difficultyCalculatorImpl{
|
||||||
borConfig: borConfig,
|
borConfig: borConfig,
|
||||||
span: span,
|
spans: spans,
|
||||||
validatorSetFactory: validatorSetFactory,
|
validatorSetFactory: validatorSetFactory,
|
||||||
signaturesCache: signaturesCache,
|
signaturesCache: signaturesCache,
|
||||||
}
|
}
|
||||||
@ -54,12 +53,12 @@ func NewDifficultyCalculator(
|
|||||||
return &impl
|
return &impl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (impl *difficultyCalculatorImpl) makeValidatorSet() validatorSetInterface {
|
func (impl *difficultyCalculatorImpl) makeValidatorSet(headerNum uint64) validatorSetInterface {
|
||||||
return valset.NewValidatorSet(impl.span.ValidatorSet.Validators)
|
span := impl.spans.SpanAt(headerNum)
|
||||||
}
|
if span == nil {
|
||||||
|
return nil
|
||||||
func (impl *difficultyCalculatorImpl) SetSpan(span *heimdallspan.HeimdallSpan) {
|
}
|
||||||
impl.span = span
|
return valset.NewValidatorSet(span.ValidatorSet.Validators)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (impl *difficultyCalculatorImpl) HeaderDifficulty(header *types.Header) (uint64, error) {
|
func (impl *difficultyCalculatorImpl) HeaderDifficulty(header *types.Header) (uint64, error) {
|
||||||
@ -71,7 +70,10 @@ func (impl *difficultyCalculatorImpl) HeaderDifficulty(header *types.Header) (ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (impl *difficultyCalculatorImpl) signerDifficulty(signer libcommon.Address, headerNum uint64) (uint64, error) {
|
func (impl *difficultyCalculatorImpl) signerDifficulty(signer libcommon.Address, headerNum uint64) (uint64, error) {
|
||||||
validatorSet := impl.validatorSetFactory()
|
validatorSet := impl.validatorSetFactory(headerNum)
|
||||||
|
if validatorSet == nil {
|
||||||
|
return 0, fmt.Errorf("difficultyCalculatorImpl.signerDifficulty: no span at %d", headerNum)
|
||||||
|
}
|
||||||
|
|
||||||
sprintNum := impl.borConfig.CalculateSprintNumber(headerNum)
|
sprintNum := impl.borConfig.CalculateSprintNumber(headerNum)
|
||||||
if sprintNum > 0 {
|
if sprintNum > 0 {
|
||||||
|
@ -7,8 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
heimdallspan "github.com/ledgerwatch/erigon/polygon/heimdall/span"
|
|
||||||
|
|
||||||
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
||||||
"github.com/ledgerwatch/erigon/core/types"
|
"github.com/ledgerwatch/erigon/core/types"
|
||||||
"github.com/ledgerwatch/erigon/polygon/bor/borcfg"
|
"github.com/ledgerwatch/erigon/polygon/bor/borcfg"
|
||||||
@ -57,7 +55,7 @@ func TestSignerDifficulty(t *testing.T) {
|
|||||||
libcommon.HexToAddress("01"),
|
libcommon.HexToAddress("01"),
|
||||||
libcommon.HexToAddress("02"),
|
libcommon.HexToAddress("02"),
|
||||||
}
|
}
|
||||||
validatorSetFactory := func() validatorSetInterface { return &testValidatorSetInterface{signers: signers} }
|
validatorSetFactory := func(uint64) validatorSetInterface { return &testValidatorSetInterface{signers: signers} }
|
||||||
calc := NewDifficultyCalculator(&borConfig, nil, validatorSetFactory, nil).(*difficultyCalculatorImpl)
|
calc := NewDifficultyCalculator(&borConfig, nil, validatorSetFactory, nil).(*difficultyCalculatorImpl)
|
||||||
|
|
||||||
var d uint64
|
var d uint64
|
||||||
@ -123,9 +121,18 @@ func TestSignerDifficulty(t *testing.T) {
|
|||||||
|
|
||||||
func TestHeaderDifficultyNoSignature(t *testing.T) {
|
func TestHeaderDifficultyNoSignature(t *testing.T) {
|
||||||
borConfig := borcfg.BorConfig{}
|
borConfig := borcfg.BorConfig{}
|
||||||
span := heimdallspan.HeimdallSpan{}
|
spans := NewSpansCache()
|
||||||
calc := NewDifficultyCalculator(&borConfig, &span, nil, nil)
|
calc := NewDifficultyCalculator(&borConfig, spans, nil, nil)
|
||||||
|
|
||||||
_, err := calc.HeaderDifficulty(new(types.Header))
|
_, err := calc.HeaderDifficulty(new(types.Header))
|
||||||
require.ErrorContains(t, err, "signature suffix missing")
|
require.ErrorContains(t, err, "signature suffix missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSignerDifficultyNoSpan(t *testing.T) {
|
||||||
|
borConfig := borcfg.BorConfig{}
|
||||||
|
spans := NewSpansCache()
|
||||||
|
calc := NewDifficultyCalculator(&borConfig, spans, nil, nil).(*difficultyCalculatorImpl)
|
||||||
|
|
||||||
|
_, err := calc.signerDifficulty(libcommon.HexToAddress("00"), 0)
|
||||||
|
require.ErrorContains(t, err, "no span")
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package sync
|
package sync
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
lru "github.com/hashicorp/golang-lru/arc/v2"
|
lru "github.com/hashicorp/golang-lru/arc/v2"
|
||||||
@ -11,25 +12,23 @@ import (
|
|||||||
"github.com/ledgerwatch/erigon/polygon/bor"
|
"github.com/ledgerwatch/erigon/polygon/bor"
|
||||||
"github.com/ledgerwatch/erigon/polygon/bor/borcfg"
|
"github.com/ledgerwatch/erigon/polygon/bor/borcfg"
|
||||||
"github.com/ledgerwatch/erigon/polygon/bor/valset"
|
"github.com/ledgerwatch/erigon/polygon/bor/valset"
|
||||||
heimdallspan "github.com/ledgerwatch/erigon/polygon/heimdall/span"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type HeaderTimeValidator interface {
|
type HeaderTimeValidator interface {
|
||||||
ValidateHeaderTime(header *types.Header, now time.Time, parent *types.Header) error
|
ValidateHeaderTime(header *types.Header, now time.Time, parent *types.Header) error
|
||||||
SetSpan(span *heimdallspan.HeimdallSpan)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type headerTimeValidatorImpl struct {
|
type headerTimeValidatorImpl struct {
|
||||||
borConfig *borcfg.BorConfig
|
borConfig *borcfg.BorConfig
|
||||||
span *heimdallspan.HeimdallSpan
|
spans *SpansCache
|
||||||
validatorSetFactory func() validatorSetInterface
|
validatorSetFactory func(headerNum uint64) validatorSetInterface
|
||||||
signaturesCache *lru.ARCCache[libcommon.Hash, libcommon.Address]
|
signaturesCache *lru.ARCCache[libcommon.Hash, libcommon.Address]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHeaderTimeValidator(
|
func NewHeaderTimeValidator(
|
||||||
borConfig *borcfg.BorConfig,
|
borConfig *borcfg.BorConfig,
|
||||||
span *heimdallspan.HeimdallSpan,
|
spans *SpansCache,
|
||||||
validatorSetFactory func() validatorSetInterface,
|
validatorSetFactory func(headerNum uint64) validatorSetInterface,
|
||||||
signaturesCache *lru.ARCCache[libcommon.Hash, libcommon.Address],
|
signaturesCache *lru.ARCCache[libcommon.Hash, libcommon.Address],
|
||||||
) HeaderTimeValidator {
|
) HeaderTimeValidator {
|
||||||
if signaturesCache == nil {
|
if signaturesCache == nil {
|
||||||
@ -42,7 +41,7 @@ func NewHeaderTimeValidator(
|
|||||||
|
|
||||||
impl := headerTimeValidatorImpl{
|
impl := headerTimeValidatorImpl{
|
||||||
borConfig: borConfig,
|
borConfig: borConfig,
|
||||||
span: span,
|
spans: spans,
|
||||||
validatorSetFactory: validatorSetFactory,
|
validatorSetFactory: validatorSetFactory,
|
||||||
signaturesCache: signaturesCache,
|
signaturesCache: signaturesCache,
|
||||||
}
|
}
|
||||||
@ -54,18 +53,22 @@ func NewHeaderTimeValidator(
|
|||||||
return &impl
|
return &impl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (impl *headerTimeValidatorImpl) makeValidatorSet() validatorSetInterface {
|
func (impl *headerTimeValidatorImpl) makeValidatorSet(headerNum uint64) validatorSetInterface {
|
||||||
return valset.NewValidatorSet(impl.span.ValidatorSet.Validators)
|
span := impl.spans.SpanAt(headerNum)
|
||||||
}
|
if span == nil {
|
||||||
|
return nil
|
||||||
func (impl *headerTimeValidatorImpl) SetSpan(span *heimdallspan.HeimdallSpan) {
|
}
|
||||||
impl.span = span
|
return valset.NewValidatorSet(span.ValidatorSet.Validators)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (impl *headerTimeValidatorImpl) ValidateHeaderTime(header *types.Header, now time.Time, parent *types.Header) error {
|
func (impl *headerTimeValidatorImpl) ValidateHeaderTime(header *types.Header, now time.Time, parent *types.Header) error {
|
||||||
validatorSet := impl.validatorSetFactory()
|
headerNum := header.Number.Uint64()
|
||||||
|
validatorSet := impl.validatorSetFactory(headerNum)
|
||||||
|
if validatorSet == nil {
|
||||||
|
return fmt.Errorf("headerTimeValidatorImpl.ValidateHeaderTime: no span at %d", headerNum)
|
||||||
|
}
|
||||||
|
|
||||||
sprintNum := impl.borConfig.CalculateSprintNumber(header.Number.Uint64())
|
sprintNum := impl.borConfig.CalculateSprintNumber(headerNum)
|
||||||
if sprintNum > 0 {
|
if sprintNum > 0 {
|
||||||
validatorSet.IncrementProposerPriority(int(sprintNum))
|
validatorSet.IncrementProposerPriority(int(sprintNum))
|
||||||
}
|
}
|
||||||
|
36
polygon/sync/spans_cache.go
Normal file
36
polygon/sync/spans_cache.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package sync
|
||||||
|
|
||||||
|
import heimdallspan "github.com/ledgerwatch/erigon/polygon/heimdall/span"
|
||||||
|
|
||||||
|
type SpansCache struct {
|
||||||
|
spans map[uint64]*heimdallspan.HeimdallSpan
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSpansCache() *SpansCache {
|
||||||
|
return &SpansCache{
|
||||||
|
spans: make(map[uint64]*heimdallspan.HeimdallSpan),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cache *SpansCache) Add(span *heimdallspan.HeimdallSpan) {
|
||||||
|
cache.spans[span.StartBlock] = span
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpanAt finds a span that contains blockNum.
|
||||||
|
func (cache *SpansCache) SpanAt(blockNum uint64) *heimdallspan.HeimdallSpan {
|
||||||
|
for _, span := range cache.spans {
|
||||||
|
if (span.StartBlock <= blockNum) && (blockNum <= span.EndBlock) {
|
||||||
|
return span
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune removes spans that ended before blockNum.
|
||||||
|
func (cache *SpansCache) Prune(blockNum uint64) {
|
||||||
|
for key, span := range cache.spans {
|
||||||
|
if span.EndBlock < blockNum {
|
||||||
|
delete(cache.spans, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user