Skip to content

Commit 96ab269

Browse files
Merge branch 'develop' of https://github.com/maticnetwork/bor into psp-pos-2955
2 parents fc81044 + e181a2a commit 96ab269

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1999
-231
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
3333
- uses: actions/setup-go@v5
3434
with:
35-
go-version: 1.24.3
35+
go-version: 1.24.6
3636

3737
- name: Install dependencies on Linux
3838
if: runner.os == 'Linux'
@@ -64,7 +64,7 @@ jobs:
6464
6565
- uses: actions/setup-go@v5
6666
with:
67-
go-version: 1.24.3
67+
go-version: 1.24.6
6868

6969
- name: Install dependencies on Linux
7070
if: runner.os == 'Linux'
@@ -92,7 +92,7 @@ jobs:
9292
9393
- uses: actions/setup-go@v5
9494
with:
95-
go-version: 1.24.3
95+
go-version: 1.24.6
9696

9797
- name: Install dependencies on Linux
9898
if: runner.os == 'Linux'
@@ -143,7 +143,7 @@ jobs:
143143
144144
- uses: actions/setup-go@v5
145145
with:
146-
go-version: 1.24.3
146+
go-version: 1.24.6
147147

148148
- name: Install dependencies on Linux
149149
if: runner.os == 'Linux'

.github/workflows/govulncheck.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
steps:
99
- uses: actions/setup-go@v5
1010
with:
11-
go-version: '1.24.3'
11+
go-version: '1.24.6'
1212
check-latest: true
1313
- uses: actions/checkout@v4
1414
- uses: technote-space/get-diff-action@v6

.github/workflows/kurtosis-e2e.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
uses: actions/checkout@v5
5353
with:
5454
repository: 0xPolygon/kurtosis-pos
55-
ref: v1.1.5
55+
ref: v1.1.11
5656
path: kurtosis-pos
5757

5858
- name: Pre kurtosis run
@@ -83,7 +83,7 @@ jobs:
8383
run: kurtosis enclave inspect ${{ env.ENCLAVE_NAME }}
8484

8585
- name: Test state syncs
86-
run: kurtosis service exec ${{ env.ENCLAVE_NAME }} test-runner "bats --filter-tags pos,bridge,l1,l2 --recursive tests/"
86+
run: kurtosis service exec ${{ env.ENCLAVE_NAME }} test-runner "bats --filter 'bridge MATIC/POL, ERC20, and ERC721 from L1 to L2 and confirm L2 balances increased' tests/pos/bridge.bats"
8787

8888
- name: Run RPC tests
8989
run: |

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ jobs:
9494
bash tests/stateless_tests/kurtosis_stateless_test.sh
9595
9696
- name: Test state syncs (post VeBlop HF)
97-
run: kurtosis service exec ${{ env.ENCLAVE_NAME }} test-runner "bats --filter-tags pos,bridge,l1,l2 --recursive tests/"
97+
run: kurtosis service exec ${{ env.ENCLAVE_NAME }} test-runner "bats --filter 'bridge MATIC/POL, ERC20, and ERC721 from L1 to L2 and confirm L2 balances increased' tests/pos/bridge.bats"
9898

9999
- name: Post kurtosis run
100100
if: always()

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
- name: Set up Go
2222
uses: actions/setup-go@master
2323
with:
24-
go-version: 1.24.3
24+
go-version: 1.24.6
2525

2626
- name: Prepare
2727
id: prepare

builder/files/genesis-amoy.json

Lines changed: 98 additions & 98 deletions
Large diffs are not rendered by default.

builder/files/genesis-mainnet-v1.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"indoreBlock": 44934656,
2323
"ahmedabadBlock": 62278656,
2424
"bhilaiBlock": 73440256,
25+
"rioBlock": 77414656,
2526
"stateSyncConfirmationDelay": {
2627
"44934656": 128
2728
},
@@ -39,6 +40,10 @@
3940
"backupMultiplier": {
4041
"0": 2
4142
},
43+
"coinbase": {
44+
"0": "0x0000000000000000000000000000000000000000",
45+
"77414656": "0x7Ee41D8A25641000661B1EF5E6AE8A00400466B0"
46+
},
4247
"validatorContract": "0x0000000000000000000000000000000000001000",
4348
"stateReceiverContract": "0x0000000000000000000000000000000000001001",
4449
"overrideStateSyncRecords": {

consensus/bor/bor_test.go

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,86 @@
11
package bor
22

33
import (
4+
"context"
45
"math/big"
56
"testing"
67

78
"github.com/holiman/uint256"
89
"github.com/stretchr/testify/require"
910

11+
"github.com/ethereum/go-ethereum/accounts"
1012
"github.com/ethereum/go-ethereum/common"
1113
"github.com/ethereum/go-ethereum/common/hexutil" //nolint:typecheck
14+
"github.com/ethereum/go-ethereum/consensus/bor/valset"
1215
"github.com/ethereum/go-ethereum/core"
1316
"github.com/ethereum/go-ethereum/core/rawdb"
1417
"github.com/ethereum/go-ethereum/core/state"
1518
"github.com/ethereum/go-ethereum/core/types"
1619
"github.com/ethereum/go-ethereum/core/vm"
1720
"github.com/ethereum/go-ethereum/params"
21+
"github.com/ethereum/go-ethereum/rpc"
1822
"github.com/ethereum/go-ethereum/triedb"
23+
lru "github.com/hashicorp/golang-lru"
24+
ttlcache "github.com/jellydator/ttlcache/v3"
25+
26+
borTypes "github.com/0xPolygon/heimdall-v2/x/bor/types"
27+
stakeTypes "github.com/0xPolygon/heimdall-v2/x/stake/types"
1928
)
2029

30+
// fakeSpanner implements Spanner for tests
31+
type fakeSpanner struct {
32+
vals []*valset.Validator
33+
}
34+
35+
func (s *fakeSpanner) GetCurrentSpan(ctx context.Context, headerHash common.Hash, st *state.StateDB) (*borTypes.Span, error) {
36+
return &borTypes.Span{Id: 0, StartBlock: 0, EndBlock: 255}, nil
37+
}
38+
func (s *fakeSpanner) GetCurrentValidatorsByHash(ctx context.Context, headerHash common.Hash, blockNumber uint64) ([]*valset.Validator, error) {
39+
return s.vals, nil
40+
}
41+
func (s *fakeSpanner) GetCurrentValidatorsByBlockNrOrHash(ctx context.Context, _ rpc.BlockNumberOrHash, _ uint64) ([]*valset.Validator, error) {
42+
return s.vals, nil
43+
}
44+
func (s *fakeSpanner) CommitSpan(ctx context.Context, _ borTypes.Span, _ []stakeTypes.MinimalVal, _ []stakeTypes.MinimalVal, _ vm.StateDB, _ *types.Header, _ core.ChainContext) error {
45+
return nil
46+
}
47+
48+
// newChainAndBorForTest centralizes common Bor + HeaderChain initialization for tests
49+
func newChainAndBorForTest(t *testing.T, sp Spanner, borCfg *params.BorConfig, devFake bool, signerAddr common.Address) (*core.BlockChain, *Bor) {
50+
cfg := &params.ChainConfig{ChainID: big.NewInt(1), Bor: borCfg}
51+
52+
b := &Bor{chainConfig: cfg, config: cfg.Bor, DevFakeAuthor: devFake}
53+
b.db = rawdb.NewMemoryDatabase()
54+
b.recents = ttlcache.New(
55+
ttlcache.WithTTL[common.Hash, *Snapshot](veblopBlockTimeout),
56+
ttlcache.WithCapacity[common.Hash, *Snapshot](inmemorySnapshots),
57+
ttlcache.WithDisableTouchOnHit[common.Hash, *Snapshot](),
58+
)
59+
sig, _ := lru.NewARC(inmemorySignatures)
60+
b.signatures = sig
61+
b.recentVerifiedHeaders = ttlcache.New[common.Hash, *types.Header](
62+
ttlcache.WithTTL[common.Hash, *types.Header](veblopBlockTimeout),
63+
ttlcache.WithCapacity[common.Hash, *types.Header](inmemorySignatures),
64+
ttlcache.WithDisableTouchOnHit[common.Hash, *types.Header](),
65+
)
66+
b.spanStore = NewSpanStore(nil, sp, cfg.ChainID.String())
67+
// set a default authorized signer to prevent nil deref in snapshot
68+
b.authorizedSigner.Store(&signer{signer: common.Address{}, signFn: func(_ accounts.Account, _ string, _ []byte) ([]byte, error) {
69+
return nil, &UnauthorizedSignerError{0, common.Address{}.Bytes(), []*valset.Validator{}}
70+
}})
71+
72+
if devFake && signerAddr != (common.Address{}) {
73+
b.authorizedSigner.Store(&signer{signer: signerAddr})
74+
}
75+
76+
genspec := &core.Genesis{Config: cfg}
77+
db := rawdb.NewMemoryDatabase()
78+
_ = genspec.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
79+
chain, err := core.NewBlockChain(rawdb.NewMemoryDatabase(), nil, genspec, nil, b, vm.Config{}, nil, nil, nil)
80+
require.NoError(t, err)
81+
return chain, b
82+
}
83+
2184
func TestGenesisContractChange(t *testing.T) {
2285
t.Parallel()
2386

@@ -294,3 +357,176 @@ func TestCalcProducerDelayRio(t *testing.T) {
294357
})
295358
}
296359
}
360+
361+
func TestPerformSpanCheck(t *testing.T) {
362+
t.Parallel()
363+
364+
addr1 := common.HexToAddress("0x1")
365+
addr2 := common.HexToAddress("0x2")
366+
367+
type testCase struct {
368+
name string
369+
targetNum uint64
370+
setMilestone *uint64
371+
provideParent bool
372+
parentHasSignature bool
373+
sameAuthorAsParent bool
374+
recentVerified bool
375+
expectErr error
376+
}
377+
378+
cases := []testCase{
379+
{name: "early return for block number 1", targetNum: 1},
380+
{name: "early return when milestone reached", targetNum: 50, setMilestone: uint64Ptr(100)},
381+
{name: "parent header nil triggers span wait", targetNum: 100, provideParent: false, parentHasSignature: false},
382+
{name: "missing parent signature returns error", targetNum: 10, provideParent: true, parentHasSignature: false, expectErr: errMissingSignature},
383+
{name: "same author without recent verification triggers span wait", targetNum: 43, provideParent: true, parentHasSignature: true, sameAuthorAsParent: true},
384+
{name: "different author no wait", targetNum: 20, provideParent: true, parentHasSignature: true, sameAuthorAsParent: false},
385+
}
386+
387+
for _, c := range cases {
388+
t.Run(c.name, func(t *testing.T) {
389+
sp := &fakeSpanner{vals: []*valset.Validator{{Address: addr2, VotingPower: 1}}}
390+
borCfg := &params.BorConfig{Sprint: map[string]uint64{"0": 64}, Period: map[string]uint64{"0": 2}}
391+
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{})
392+
393+
var parents []*types.Header
394+
var parentHash common.Hash
395+
if c.provideParent {
396+
parent := &types.Header{Number: big.NewInt(int64(c.targetNum - 1))}
397+
parentHash = parent.Hash()
398+
if c.parentHasSignature {
399+
if c.sameAuthorAsParent {
400+
b.signatures.Add(parent.Hash(), addr1)
401+
} else {
402+
b.signatures.Add(parent.Hash(), addr2)
403+
}
404+
}
405+
parents = []*types.Header{parent}
406+
} else {
407+
parentHash = common.HexToHash("0xdead")
408+
}
409+
410+
target := &types.Header{Number: big.NewInt(int64(c.targetNum)), ParentHash: parentHash}
411+
b.signatures.Add(target.Hash(), addr1)
412+
413+
if c.recentVerified {
414+
b.recentVerifiedHeaders.Set(parentHash, target, ttlcache.DefaultTTL)
415+
}
416+
if c.setMilestone != nil {
417+
b.latestMilestoneBlock.Store(*c.setMilestone)
418+
}
419+
420+
err := b.performSpanCheck(chain.HeaderChain(), target, parents)
421+
if c.expectErr != nil {
422+
require.Error(t, err)
423+
require.Equal(t, c.expectErr, err)
424+
} else {
425+
require.NoError(t, err)
426+
}
427+
})
428+
}
429+
}
430+
431+
func uint64Ptr(v uint64) *uint64 { return &v }
432+
433+
func TestGetVeBlopSnapshot(t *testing.T) {
434+
t.Parallel()
435+
436+
addr1 := common.HexToAddress("0x1")
437+
addr2 := common.HexToAddress("0x2")
438+
439+
type testCase struct {
440+
name string
441+
spVals []*valset.Validator
442+
targetNum uint64
443+
expectAddrs []common.Address
444+
checkNewSpan bool
445+
}
446+
447+
cases := []testCase{
448+
{
449+
name: "veblop snapshot with checkNewSpan=true",
450+
spVals: []*valset.Validator{{Address: addr1, VotingPower: 1}, {Address: addr2, VotingPower: 2}},
451+
targetNum: 42,
452+
expectAddrs: []common.Address{addr1, addr2},
453+
checkNewSpan: true,
454+
},
455+
{
456+
name: "veblop snapshot with checkNewSpan=false",
457+
spVals: []*valset.Validator{{Address: addr1, VotingPower: 1}, {Address: addr2, VotingPower: 2}},
458+
targetNum: 43,
459+
expectAddrs: []common.Address{addr1, addr2},
460+
checkNewSpan: false,
461+
},
462+
}
463+
464+
for _, c := range cases {
465+
t.Run(c.name, func(t *testing.T) {
466+
sp := &fakeSpanner{vals: c.spVals}
467+
borCfg := &params.BorConfig{Sprint: map[string]uint64{"0": 64}, Period: map[string]uint64{"0": 2}, RioBlock: big.NewInt(0)}
468+
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{})
469+
h := &types.Header{Number: big.NewInt(int64(c.targetNum))}
470+
snap, err := b.getVeBlopSnapshot(chain.HeaderChain(), h, nil, c.checkNewSpan)
471+
require.NoError(t, err)
472+
require.NotNil(t, snap)
473+
require.Equal(t, h.Number.Uint64(), snap.Number)
474+
require.Equal(t, h.Hash(), snap.Hash)
475+
476+
seen := map[common.Address]bool{}
477+
for _, v := range snap.ValidatorSet.Validators {
478+
seen[v.Address] = true
479+
}
480+
for _, exp := range c.expectAddrs {
481+
require.True(t, seen[exp])
482+
}
483+
})
484+
}
485+
}
486+
487+
func TestSnapshot(t *testing.T) {
488+
// Only consider case when c.config.IsRio(targetHeader.Number) != true
489+
t.Parallel()
490+
491+
addr1 := common.HexToAddress("0x1111111111111111111111111111111111111111")
492+
addr2 := common.HexToAddress("0x2222222222222222222222222222222222222222")
493+
494+
type testCase struct {
495+
name string
496+
spVals []*valset.Validator
497+
targetNum uint64
498+
expectAddrs []common.Address
499+
}
500+
501+
cases := []testCase{
502+
{
503+
name: "snapshot uses non-VeBlop path and includes validators",
504+
spVals: []*valset.Validator{{Address: addr1, VotingPower: 1}, {Address: addr2, VotingPower: 2}},
505+
targetNum: 2,
506+
expectAddrs: []common.Address{addr1, addr2},
507+
},
508+
}
509+
510+
for _, c := range cases {
511+
t.Run(c.name, func(t *testing.T) {
512+
sp := &fakeSpanner{vals: c.spVals}
513+
// Configure RioBlock far in the future so IsRio(header.Number) == false
514+
borCfg := &params.BorConfig{Sprint: map[string]uint64{"0": 64}, Period: map[string]uint64{"0": 2}, RioBlock: big.NewInt(1_000_000)}
515+
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{})
516+
gen := chain.HeaderChain().GetHeaderByNumber(0)
517+
require.NotNil(t, gen)
518+
target := &types.Header{Number: big.NewInt(1), ParentHash: gen.Hash()}
519+
snap, err := b.snapshot(chain.HeaderChain(), target, []*types.Header{gen}, true)
520+
require.NoError(t, err)
521+
require.NotNil(t, snap)
522+
523+
seen := map[common.Address]bool{}
524+
for _, v := range snap.ValidatorSet.Validators {
525+
seen[v.Address] = true
526+
}
527+
for _, exp := range c.expectAddrs {
528+
require.True(t, seen[exp])
529+
}
530+
})
531+
}
532+
}

consensus/bor/heimdall/client.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ func (h *HeimdallClient) GetLatestSpan(ctx context.Context) (*types.Span, error)
159159

160160
ctx = WithRequestType(ctx, SpanRequest)
161161

162-
response, err := FetchWithRetry[types.QueryLatestSpanResponse](ctx, h.client, url, h.closeCh)
162+
response, err := FetchOnce[types.QueryLatestSpanResponse](ctx, h.client, url, h.closeCh)
163163
if err != nil {
164164
return nil, err
165165
}
@@ -242,14 +242,19 @@ func (h *HeimdallClient) FetchStatus(ctx context.Context) (*ctypes.SyncInfo, err
242242
return nil, err
243243
}
244244

245-
response, err := FetchWithRetry[ctypes.SyncInfo](ctx, h.client, url, h.closeCh)
245+
response, err := FetchOnce[ctypes.SyncInfo](ctx, h.client, url, h.closeCh)
246246
if err != nil {
247247
return nil, err
248248
}
249249

250250
return response, nil
251251
}
252252

253+
func FetchOnce[T any](ctx context.Context, client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) {
254+
request := &Request{client: client, url: url, start: time.Now()}
255+
return Fetch[T](ctx, request)
256+
}
257+
253258
// FetchWithRetry returns data from heimdall with retry
254259
func FetchWithRetry[T any](ctx context.Context, client http.Client, url *url.URL, closeCh chan struct{}) (*T, error) {
255260
// request data once

0 commit comments

Comments
 (0)