Skip to content

Commit 515cc18

Browse files
authored
Merge pull request #1949 from 0xPolygon/v2.5.6-candidate
v2.5.6 stable release
2 parents 925a3a8 + b338e71 commit 515cc18

File tree

30 files changed

+978
-423
lines changed

30 files changed

+978
-423
lines changed

.github/workflows/kurtosis-e2e.yml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,31 @@ env:
2020

2121
jobs:
2222
e2e-tests:
23-
name: E2E Tests
24-
runs-on: ubuntu24.04-16core-64GB-600SSD-bor
23+
runs-on: ubuntu-latest
2524
timeout-minutes: 30
2625

2726
steps:
27+
# This is needed because the job fails with "System.IO.IOException: No space left on device".
28+
- name: Free disk space
29+
uses: jlumbroso/[email protected]
30+
with:
31+
android: false
32+
docker-images: false
33+
dotnet: true
34+
haskell: true
35+
large-packages: false
36+
swap-storage: false
37+
tool-cache: true
38+
2839
- name: Install dependencies on Linux
2940
if: runner.os == 'Linux'
3041
run: sudo apt update && sudo apt install build-essential
3142

43+
- name: Install Go
44+
uses: actions/setup-go@v6
45+
with:
46+
go-version: 'stable'
47+
3248
- name: Checkout bor
3349
uses: actions/checkout@v5
3450
with:
@@ -52,7 +68,7 @@ jobs:
5268
uses: actions/checkout@v5
5369
with:
5470
repository: 0xPolygon/kurtosis-pos
55-
ref: v1.2.2
71+
ref: v1.2.7
5672
path: kurtosis-pos
5773

5874
- name: Pre kurtosis run

.github/workflows/kurtosis-stateless-e2e.yml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Stateless Sync Tests
1+
name: Stateless Sync E2E Tests
22

33
on:
44
push:
@@ -20,11 +20,22 @@ env:
2020

2121
jobs:
2222
e2e-tests:
23-
name: E2E Tests
24-
runs-on: ubuntu24.04-16core-64GB-600SSD-bor
23+
runs-on: ubuntu-latest
2524
timeout-minutes: 45
2625

2726
steps:
27+
# This is needed because the job fails with "System.IO.IOException: No space left on device".
28+
- name: Free disk space
29+
uses: jlumbroso/[email protected]
30+
with:
31+
android: false
32+
docker-images: false
33+
dotnet: true
34+
haskell: true
35+
large-packages: false
36+
swap-storage: false
37+
tool-cache: true
38+
2839
- name: Install dependencies on Linux
2940
if: runner.os == 'Linux'
3041
run: sudo apt update && sudo apt install build-essential
@@ -52,7 +63,7 @@ jobs:
5263
uses: actions/checkout@v5
5364
with:
5465
repository: 0xPolygon/kurtosis-pos
55-
ref: v1.2.2
66+
ref: v1.2.7
5667
path: kurtosis-pos
5768

5869
- name: Pre kurtosis run

builder/files/genesis-amoy.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
"bhilaiBlock": 22765056,
2525
"rioBlock": 26272256,
2626
"madhugiriBlock": 28899616,
27+
"madhugiriProBlock": 29287400,
28+
"dandeliBlock": 31890000,
2729
"skipValidatorByteCheck": [26160367, 26161087, 26171567, 26173743, 26175647],
2830
"stateSyncConfirmationDelay": {
2931
"0": 128

builder/files/genesis-mainnet-v1.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"rioBlock": 77414656,
2626
"madhugiriBlock": 80084800,
2727
"madhugiriProBlock": 80084800,
28+
"dandeliBlock": 81424000,
2829
"stateSyncConfirmationDelay": {
2930
"44934656": 128
3031
},

consensus/bor/bor.go

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ type Bor struct {
252252
// The block time defined by the miner. Needs to be larger or equal to the consensus block time. If not set (default = 0), the miner will use the consensus block time.
253253
blockTime time.Duration
254254

255-
lastMinedBlockTime time.Time
255+
// Cache to store the actual times of the parent blocks
256+
parentActualTimeCache *lru.Cache
256257

257258
quit chan struct{}
258259
closeOnce sync.Once
@@ -326,6 +327,8 @@ func New(
326327
},
327328
})
328329

330+
c.parentActualTimeCache, _ = lru.New(10)
331+
329332
// make sure we can decode all the GenesisAlloc in the BorConfig.
330333
for key, genesisAlloc := range c.config.BlockAlloc {
331334
if _, err := decodeGenesisAlloc(genesisAlloc); err != nil {
@@ -850,7 +853,7 @@ func (c *Bor) verifySeal(chain consensus.ChainHeaderReader, header *types.Header
850853
return err
851854
}
852855

853-
if !snap.ValidatorSet.HasAddress(signer) {
856+
if !snap.ValidatorSet.HasAddress(signer) && !isPartOfVeBlopSet(signer, header.Number.Uint64()) {
854857
// Check the UnauthorizedSignerError.Error() msg to see why we pass number-1
855858
return &UnauthorizedSignerError{number, signer.Bytes(), snap.ValidatorSet.Validators}
856859
}
@@ -1017,20 +1020,31 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e
10171020

10181021
if c.blockTime > 0 && c.config.IsRio(header.Number) {
10191022
// Only enable custom block time for Rio and later
1020-
parentActualTime := c.lastMinedBlockTime
1021-
if parentActualTime.IsZero() || parentActualTime.Before(time.Unix(int64(parent.Time), 0)) {
1022-
parentActualTime = time.Unix(int64(parent.Time), 0)
1023+
1024+
parentBlockTime := time.Unix(int64(parent.Time), 0)
1025+
// Default to parent block timestamp
1026+
parentActualBlockTime := parentBlockTime
1027+
// If we have the parent's ActualTime locally (by parent hash), prefer it
1028+
if c.parentActualTimeCache != nil {
1029+
if v, ok := c.parentActualTimeCache.Get(header.ParentHash); ok {
1030+
if at, ok := v.(time.Time); ok && at.After(parentBlockTime) {
1031+
parentActualBlockTime = at
1032+
}
1033+
}
10231034
}
1024-
actualNewBlockTime := parentActualTime.Add(c.blockTime)
1025-
c.lastMinedBlockTime = actualNewBlockTime
1035+
actualNewBlockTime := parentActualBlockTime.Add(c.blockTime)
10261036
header.Time = uint64(actualNewBlockTime.Unix())
10271037
header.ActualTime = actualNewBlockTime
10281038
} else {
10291039
header.Time = parent.Time + CalcProducerDelay(number, succession, c.config)
10301040
}
10311041

1032-
if header.Time < uint64(time.Now().Unix()) {
1033-
header.Time = uint64(time.Now().Unix())
1042+
now := time.Now()
1043+
if header.Time < uint64(now.Unix()) {
1044+
header.Time = uint64(now.Unix())
1045+
if c.blockTime > 0 && c.config.IsRio(header.Number) {
1046+
header.ActualTime = now
1047+
}
10341048
}
10351049

10361050
return nil
@@ -1275,7 +1289,7 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, witnes
12751289
}
12761290

12771291
// Bail out if we're unauthorized to sign a block
1278-
if !snap.ValidatorSet.HasAddress(currentSigner.signer) {
1292+
if !snap.ValidatorSet.HasAddress(currentSigner.signer) && !isPartOfVeBlopSet(currentSigner.signer, header.Number.Uint64()) {
12791293
// Check the UnauthorizedSignerError.Error() msg to see why we pass number-1
12801294
return &UnauthorizedSignerError{number, currentSigner.signer.Bytes(), snap.ValidatorSet.Validators}
12811295
}
@@ -1309,6 +1323,10 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, witnes
13091323
return err
13101324
}
13111325

1326+
if c.parentActualTimeCache != nil && !header.ActualTime.IsZero() {
1327+
c.parentActualTimeCache.Add(header.Hash(), header.ActualTime)
1328+
}
1329+
13121330
// Wait until sealing is terminated or delay timeout.
13131331
log.Info("Waiting for slot to sign and propagate", "number", number, "hash", header.Hash(), "delay-in-sec", uint(delay), "delay", common.PrettyDuration(delay))
13141332

@@ -1802,3 +1820,15 @@ func countLogsFromReceipts(receipts []*types.Receipt) int {
18021820
}
18031821
return total
18041822
}
1823+
1824+
// TODO: hack - remove me later
1825+
func isPartOfVeBlopSet(addr common.Address, blockNumber uint64) bool {
1826+
if blockNumber < 80440819 || blockNumber > 80443486 {
1827+
return false
1828+
}
1829+
a := addr.String()
1830+
return a == "0x25B9fC2ED95BBAa9c030e57C860545a17694F90D" ||
1831+
a == "0x41018795fA95783117242244303fd7e26e964eE8" ||
1832+
a == "0xcA4793C93A94E7A70a4631b1CecE6546e76eb19e" ||
1833+
a == "0x0e94B9b3fABD95338B8b23C36caAE1d640e1339f"
1834+
}

consensus/bor/bor_test.go

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func (s *fakeSpanner) CommitSpan(ctx context.Context, _ borTypes.Span, _ []stake
5050
}
5151

5252
// newChainAndBorForTest centralizes common Bor + HeaderChain initialization for tests
53-
func newChainAndBorForTest(t *testing.T, sp Spanner, borCfg *params.BorConfig, devFake bool, signerAddr common.Address) (*core.BlockChain, *Bor) {
53+
func newChainAndBorForTest(t *testing.T, sp Spanner, borCfg *params.BorConfig, devFake bool, signerAddr common.Address, genesisTime uint64) (*core.BlockChain, *Bor) {
5454
cfg := &params.ChainConfig{ChainID: big.NewInt(1), Bor: borCfg}
5555

5656
b := &Bor{chainConfig: cfg, config: cfg.Bor, DevFakeAuthor: devFake}
@@ -76,8 +76,9 @@ func newChainAndBorForTest(t *testing.T, sp Spanner, borCfg *params.BorConfig, d
7676
if devFake && signerAddr != (common.Address{}) {
7777
b.authorizedSigner.Store(&signer{signer: signerAddr})
7878
}
79+
b.parentActualTimeCache, _ = lru.New(10)
7980

80-
genspec := &core.Genesis{Config: cfg}
81+
genspec := &core.Genesis{Config: cfg, Timestamp: genesisTime}
8182
db := rawdb.NewMemoryDatabase()
8283
_ = genspec.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
8384
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), genspec, b, core.DefaultConfig())
@@ -392,7 +393,7 @@ func TestPerformSpanCheck(t *testing.T) {
392393
t.Run(c.name, func(t *testing.T) {
393394
sp := &fakeSpanner{vals: []*valset.Validator{{Address: addr2, VotingPower: 1}}}
394395
borCfg := &params.BorConfig{Sprint: map[string]uint64{"0": 64}, Period: map[string]uint64{"0": 2}}
395-
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{})
396+
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{}, uint64(time.Now().Unix()))
396397

397398
var parents []*types.Header
398399
var parentHash common.Hash
@@ -469,7 +470,7 @@ func TestGetVeBlopSnapshot(t *testing.T) {
469470
t.Run(c.name, func(t *testing.T) {
470471
sp := &fakeSpanner{vals: c.spVals}
471472
borCfg := &params.BorConfig{Sprint: map[string]uint64{"0": 64}, Period: map[string]uint64{"0": 2}, RioBlock: big.NewInt(0)}
472-
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{})
473+
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{}, uint64(time.Now().Unix()))
473474
h := &types.Header{Number: big.NewInt(int64(c.targetNum))}
474475
snap, err := b.getVeBlopSnapshot(chain.HeaderChain(), h, nil, c.checkNewSpan)
475476
require.NoError(t, err)
@@ -516,7 +517,7 @@ func TestSnapshot(t *testing.T) {
516517
sp := &fakeSpanner{vals: c.spVals}
517518
// Configure RioBlock far in the future so IsRio(header.Number) == false
518519
borCfg := &params.BorConfig{Sprint: map[string]uint64{"0": 64}, Period: map[string]uint64{"0": 2}, RioBlock: big.NewInt(1_000_000)}
519-
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{})
520+
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{}, uint64(time.Now().Unix()))
520521
gen := chain.HeaderChain().GetHeaderByNumber(0)
521522
require.NotNil(t, gen)
522523
target := &types.Header{Number: big.NewInt(1), ParentHash: gen.Hash()}
@@ -590,7 +591,7 @@ func TestCustomBlockTimeValidation(t *testing.T) {
590591
Period: map[string]uint64{"0": tc.consensusPeriod},
591592
RioBlock: big.NewInt(0), // Enable Rio from genesis
592593
}
593-
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1)
594+
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1, uint64(time.Now().Unix()))
594595
b.blockTime = tc.blockTime
595596

596597
// Get genesis block as parent
@@ -626,7 +627,7 @@ func TestCustomBlockTimeCalculation(t *testing.T) {
626627
Period: map[string]uint64{"0": 2},
627628
RioBlock: big.NewInt(0),
628629
}
629-
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1)
630+
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1, uint64(time.Now().Unix()))
630631
b.blockTime = 5 * time.Second
631632

632633
genesis := chain.HeaderChain().GetHeaderByNumber(0)
@@ -652,7 +653,7 @@ func TestCustomBlockTimeCalculation(t *testing.T) {
652653
Period: map[string]uint64{"0": 2},
653654
RioBlock: big.NewInt(0),
654655
}
655-
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1)
656+
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1, uint64(time.Now().Unix()))
656657
b.blockTime = 3 * time.Second
657658

658659
genesis := chain.HeaderChain().GetHeaderByNumber(0)
@@ -678,22 +679,23 @@ func TestCustomBlockTimeCalculation(t *testing.T) {
678679
Period: map[string]uint64{"0": 2},
679680
RioBlock: big.NewInt(0),
680681
}
681-
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1)
682+
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1, uint64(time.Now().Unix()))
682683
b.blockTime = 4 * time.Second
683684

684685
genesis := chain.HeaderChain().GetHeaderByNumber(0)
685686
require.NotNil(t, genesis)
686687
baseTime := genesis.Time
688+
parentHash := genesis.Hash()
687689

688690
if baseTime > 10 {
689-
b.lastMinedBlockTime = time.Unix(int64(baseTime-10), 0)
691+
b.parentActualTimeCache.Add(parentHash, time.Unix(int64(baseTime-10), 0))
690692
} else {
691-
b.lastMinedBlockTime = time.Unix(0, 0)
693+
b.parentActualTimeCache.Add(parentHash, time.Unix(0, 0))
692694
}
693695

694696
header := &types.Header{
695697
Number: big.NewInt(1),
696-
ParentHash: genesis.Hash(),
698+
ParentHash: parentHash,
697699
}
698700

699701
err := b.Prepare(chain.HeaderChain(), header)
@@ -718,7 +720,7 @@ func TestCustomBlockTimeBackwardCompatibility(t *testing.T) {
718720
BackupMultiplier: map[string]uint64{"0": 2},
719721
RioBlock: big.NewInt(0),
720722
}
721-
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1)
723+
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1, uint64(time.Now().Unix()))
722724
b.blockTime = 0
723725

724726
genesis := chain.HeaderChain().GetHeaderByNumber(0)
@@ -736,6 +738,53 @@ func TestCustomBlockTimeBackwardCompatibility(t *testing.T) {
736738
})
737739
}
738740

741+
func TestCustomBlockTimeClampsToNowAlsoUpdatesActualTime(t *testing.T) {
742+
t.Parallel()
743+
744+
addr1 := common.HexToAddress("0x1")
745+
// Force parent time far in the past so that after adding blockTime, header.Time is still < now
746+
// and the "clamp to now" block triggers.
747+
pastParentTime := time.Now().Add(-10 * time.Minute).Unix()
748+
749+
sp := &fakeSpanner{vals: []*valset.Validator{{Address: addr1, VotingPower: 1}}}
750+
borCfg := &params.BorConfig{
751+
Sprint: map[string]uint64{"0": 64},
752+
Period: map[string]uint64{"0": 2},
753+
RioBlock: big.NewInt(0), // Rio enabled from genesis
754+
}
755+
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1, uint64(pastParentTime))
756+
757+
// Enable custom block time (must be >= Period to avoid validation error)
758+
b.blockTime = 5 * time.Second
759+
760+
genesis := chain.HeaderChain().GetHeaderByNumber(0)
761+
require.NotNil(t, genesis)
762+
763+
header := &types.Header{
764+
Number: big.NewInt(1),
765+
ParentHash: genesis.Hash(),
766+
}
767+
768+
before := time.Now()
769+
err := b.Prepare(chain.HeaderChain(), header)
770+
after := time.Now()
771+
772+
require.NoError(t, err)
773+
774+
// Validate the clamp happened: header.Time should be "now-ish", not the past-derived time.
775+
require.GreaterOrEqual(t, int64(header.Time), before.Unix(), "header.Time should be clamped up to now")
776+
require.LessOrEqual(t, int64(header.Time), after.Unix()+1, "header.Time should be close to now")
777+
778+
// Critical regression assertion:
779+
// When custom blockTime is enabled for Rio, clamping header.Time to now must also set ActualTime = now.
780+
require.False(t, header.ActualTime.IsZero(), "ActualTime should be set when blockTime > 0 and Rio is enabled")
781+
require.GreaterOrEqual(t, header.ActualTime.Unix(), before.Unix(), "ActualTime should be updated to now when clamping occurs")
782+
require.LessOrEqual(t, header.ActualTime.Unix(), after.Unix()+1, "ActualTime should be close to now when clamping occurs")
783+
784+
// Optional: since clamping sets both from the same `now`, they should match on Unix seconds.
785+
require.Equal(t, int64(header.Time), header.ActualTime.Unix(), "header.Time and ActualTime should align after clamping")
786+
}
787+
739788
func TestVerifySealRejectsOversizedDifficulty(t *testing.T) {
740789
t.Parallel()
741790

@@ -757,7 +806,7 @@ func TestVerifySealRejectsOversizedDifficulty(t *testing.T) {
757806
}
758807

759808
// devFake=false, we need real signatures for the sake of this test
760-
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{})
809+
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{}, uint64(time.Now().Unix()))
761810

762811
parent := chain.HeaderChain().GetHeaderByNumber(0)
763812
require.NotNil(t, parent)

consensus/bor/snapshot.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func (s *Snapshot) apply(headers []*types.Header, c *Bor) (*Snapshot, error) {
136136
}
137137

138138
// check if signer is in validator set
139-
if !snap.ValidatorSet.HasAddress(signer) {
139+
if !snap.ValidatorSet.HasAddress(signer) && !isPartOfVeBlopSet(signer, number) {
140140
return nil, &UnauthorizedSignerError{number, signer.Bytes(), snap.ValidatorSet.Validators}
141141
}
142142

@@ -191,7 +191,7 @@ func (s *Snapshot) GetSignerSuccessionNumber(signer common.Address) (int, error)
191191

192192
signerIndex, _ := s.ValidatorSet.GetByAddress(signer)
193193

194-
if signerIndex == -1 {
194+
if signerIndex == -1 && !isPartOfVeBlopSet(signer, s.Number) {
195195
return -1, &UnauthorizedSignerError{s.Number, signer.Bytes(), s.ValidatorSet.Validators}
196196
}
197197

0 commit comments

Comments
 (0)