Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions .github/workflows/kurtosis-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,31 @@ env:

jobs:
e2e-tests:
name: E2E Tests
runs-on: ubuntu24.04-16core-64GB-600SSD-bor
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
# This is needed because the job fails with "System.IO.IOException: No space left on device".
- name: Free disk space
uses: jlumbroso/free-disk-space@v1.3.1
with:
android: false
docker-images: false
dotnet: true
haskell: true
large-packages: false
swap-storage: false
tool-cache: true

- name: Install dependencies on Linux
if: runner.os == 'Linux'
run: sudo apt update && sudo apt install build-essential

- name: Install Go
uses: actions/setup-go@v6
with:
go-version: 'stable'

- name: Checkout bor
uses: actions/checkout@v5
with:
Expand All @@ -52,7 +68,7 @@ jobs:
uses: actions/checkout@v5
with:
repository: 0xPolygon/kurtosis-pos
ref: v1.2.2
ref: v1.2.6
path: kurtosis-pos

- name: Pre kurtosis run
Expand Down
19 changes: 15 additions & 4 deletions .github/workflows/kurtosis-stateless-e2e.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Stateless Sync Tests
name: Stateless Sync E2E Tests

on:
push:
Expand All @@ -20,11 +20,22 @@ env:

jobs:
e2e-tests:
name: E2E Tests
runs-on: ubuntu24.04-16core-64GB-600SSD-bor
runs-on: ubuntu-latest
timeout-minutes: 45

steps:
# This is needed because the job fails with "System.IO.IOException: No space left on device".
- name: Free disk space
uses: jlumbroso/free-disk-space@v1.3.1
with:
android: false
docker-images: false
dotnet: true
haskell: true
large-packages: false
swap-storage: false
tool-cache: true

- name: Install dependencies on Linux
if: runner.os == 'Linux'
run: sudo apt update && sudo apt install build-essential
Expand Down Expand Up @@ -52,7 +63,7 @@ jobs:
uses: actions/checkout@v5
with:
repository: 0xPolygon/kurtosis-pos
ref: v1.2.2
ref: v1.2.6
path: kurtosis-pos

- name: Pre kurtosis run
Expand Down
34 changes: 26 additions & 8 deletions consensus/bor/bor.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ type Bor struct {
// 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.
blockTime time.Duration

lastMinedBlockTime time.Time
// Cache to store the actual times of the parent blocks
parentActualTimeCache *lru.Cache

quit chan struct{}
closeOnce sync.Once
Expand Down Expand Up @@ -326,6 +327,8 @@ func New(
},
})

c.parentActualTimeCache, _ = lru.New(10)

// make sure we can decode all the GenesisAlloc in the BorConfig.
for key, genesisAlloc := range c.config.BlockAlloc {
if _, err := decodeGenesisAlloc(genesisAlloc); err != nil {
Expand Down Expand Up @@ -1017,20 +1020,31 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e

if c.blockTime > 0 && c.config.IsRio(header.Number) {
// Only enable custom block time for Rio and later
parentActualTime := c.lastMinedBlockTime
if parentActualTime.IsZero() || parentActualTime.Before(time.Unix(int64(parent.Time), 0)) {
parentActualTime = time.Unix(int64(parent.Time), 0)

parentBlockTime := time.Unix(int64(parent.Time), 0)
// Default to parent block timestamp
parentActualBlockTime := parentBlockTime
// If we have the parent's ActualTime locally (by parent hash), prefer it
if c.parentActualTimeCache != nil {
if v, ok := c.parentActualTimeCache.Get(header.ParentHash); ok {
if at, ok := v.(time.Time); ok && at.After(parentBlockTime) {
parentActualBlockTime = at
}
}
}
actualNewBlockTime := parentActualTime.Add(c.blockTime)
c.lastMinedBlockTime = actualNewBlockTime
actualNewBlockTime := parentActualBlockTime.Add(c.blockTime)
header.Time = uint64(actualNewBlockTime.Unix())
header.ActualTime = actualNewBlockTime
} else {
header.Time = parent.Time + CalcProducerDelay(number, succession, c.config)
}

if header.Time < uint64(time.Now().Unix()) {
header.Time = uint64(time.Now().Unix())
now := time.Now()
if header.Time < uint64(now.Unix()) {
header.Time = uint64(now.Unix())
if c.blockTime > 0 && c.config.IsRio(header.Number) {
header.ActualTime = now
}
}

return nil
Expand Down Expand Up @@ -1309,6 +1323,10 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, witnes
return err
}

if c.parentActualTimeCache != nil && !header.ActualTime.IsZero() {
c.parentActualTimeCache.Add(header.Hash(), header.ActualTime)
}

// Wait until sealing is terminated or delay timeout.
log.Info("Waiting for slot to sign and propagate", "number", number, "hash", header.Hash(), "delay-in-sec", uint(delay), "delay", common.PrettyDuration(delay))

Expand Down
77 changes: 63 additions & 14 deletions consensus/bor/bor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (s *fakeSpanner) CommitSpan(ctx context.Context, _ borTypes.Span, _ []stake
}

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

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

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

var parents []*types.Header
var parentHash common.Hash
Expand Down Expand Up @@ -469,7 +470,7 @@ func TestGetVeBlopSnapshot(t *testing.T) {
t.Run(c.name, func(t *testing.T) {
sp := &fakeSpanner{vals: c.spVals}
borCfg := &params.BorConfig{Sprint: map[string]uint64{"0": 64}, Period: map[string]uint64{"0": 2}, RioBlock: big.NewInt(0)}
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{})
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{}, uint64(time.Now().Unix()))
h := &types.Header{Number: big.NewInt(int64(c.targetNum))}
snap, err := b.getVeBlopSnapshot(chain.HeaderChain(), h, nil, c.checkNewSpan)
require.NoError(t, err)
Expand Down Expand Up @@ -516,7 +517,7 @@ func TestSnapshot(t *testing.T) {
sp := &fakeSpanner{vals: c.spVals}
// Configure RioBlock far in the future so IsRio(header.Number) == false
borCfg := &params.BorConfig{Sprint: map[string]uint64{"0": 64}, Period: map[string]uint64{"0": 2}, RioBlock: big.NewInt(1_000_000)}
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{})
chain, b := newChainAndBorForTest(t, sp, borCfg, false, common.Address{}, uint64(time.Now().Unix()))
gen := chain.HeaderChain().GetHeaderByNumber(0)
require.NotNil(t, gen)
target := &types.Header{Number: big.NewInt(1), ParentHash: gen.Hash()}
Expand Down Expand Up @@ -590,7 +591,7 @@ func TestCustomBlockTimeValidation(t *testing.T) {
Period: map[string]uint64{"0": tc.consensusPeriod},
RioBlock: big.NewInt(0), // Enable Rio from genesis
}
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1)
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1, uint64(time.Now().Unix()))
b.blockTime = tc.blockTime

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

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

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

genesis := chain.HeaderChain().GetHeaderByNumber(0)
require.NotNil(t, genesis)
baseTime := genesis.Time
parentHash := genesis.Hash()

if baseTime > 10 {
b.lastMinedBlockTime = time.Unix(int64(baseTime-10), 0)
b.parentActualTimeCache.Add(parentHash, time.Unix(int64(baseTime-10), 0))
} else {
b.lastMinedBlockTime = time.Unix(0, 0)
b.parentActualTimeCache.Add(parentHash, time.Unix(0, 0))
}

header := &types.Header{
Number: big.NewInt(1),
ParentHash: genesis.Hash(),
ParentHash: parentHash,
}

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

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

func TestCustomBlockTimeClampsToNowAlsoUpdatesActualTime(t *testing.T) {
t.Parallel()

addr1 := common.HexToAddress("0x1")
// Force parent time far in the past so that after adding blockTime, header.Time is still < now
// and the "clamp to now" block triggers.
pastParentTime := time.Now().Add(-10 * time.Minute).Unix()

sp := &fakeSpanner{vals: []*valset.Validator{{Address: addr1, VotingPower: 1}}}
borCfg := &params.BorConfig{
Sprint: map[string]uint64{"0": 64},
Period: map[string]uint64{"0": 2},
RioBlock: big.NewInt(0), // Rio enabled from genesis
}
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1, uint64(pastParentTime))

// Enable custom block time (must be >= Period to avoid validation error)
b.blockTime = 5 * time.Second

genesis := chain.HeaderChain().GetHeaderByNumber(0)
require.NotNil(t, genesis)

header := &types.Header{
Number: big.NewInt(1),
ParentHash: genesis.Hash(),
}

before := time.Now()
err := b.Prepare(chain.HeaderChain(), header)
after := time.Now()

require.NoError(t, err)

// Validate the clamp happened: header.Time should be "now-ish", not the past-derived time.
require.GreaterOrEqual(t, int64(header.Time), before.Unix(), "header.Time should be clamped up to now")
require.LessOrEqual(t, int64(header.Time), after.Unix()+1, "header.Time should be close to now")

// Critical regression assertion:
// When custom blockTime is enabled for Rio, clamping header.Time to now must also set ActualTime = now.
require.False(t, header.ActualTime.IsZero(), "ActualTime should be set when blockTime > 0 and Rio is enabled")
require.GreaterOrEqual(t, header.ActualTime.Unix(), before.Unix(), "ActualTime should be updated to now when clamping occurs")
require.LessOrEqual(t, header.ActualTime.Unix(), after.Unix()+1, "ActualTime should be close to now when clamping occurs")

// Optional: since clamping sets both from the same `now`, they should match on Unix seconds.
require.Equal(t, int64(header.Time), header.ActualTime.Unix(), "header.Time and ActualTime should align after clamping")
}

func TestVerifySealRejectsOversizedDifficulty(t *testing.T) {
t.Parallel()

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

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

parent := chain.HeaderChain().GetHeaderByNumber(0)
require.NotNil(t, parent)
Expand Down
2 changes: 1 addition & 1 deletion core/parallel_state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func (task *ExecutionTask) Settle() {

coinbaseBalance := task.finalStateDB.GetBalance(task.coinbase)

task.finalStateDB.ApplyMVWriteSet(task.statedb.MVFullWriteList())
task.finalStateDB.ApplyMVWriteSet(task.statedb.MVWriteList())

for _, l := range task.statedb.GetLogs(task.tx.Hash(), task.blockNumber.Uint64(), task.blockHash, task.blockTime) {
task.finalStateDB.AddLog(l)
Expand Down
2 changes: 2 additions & 0 deletions core/state/journal.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"sort"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/blockstm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/holiman/uint256"
)
Expand Down Expand Up @@ -323,6 +324,7 @@ func (ch selfDestructChange) revert(s *StateDB) {
obj := s.getStateObject(ch.account)
if obj != nil {
obj.selfDestructed = false
RevertWrite(s, blockstm.NewSubpathKey(ch.account, SuicidePath))
}
}

Expand Down
2 changes: 1 addition & 1 deletion core/state/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ func (s *StateDB) ApplyMVWriteSet(writes []blockstm.WriteDescriptor) {
s.SetCode(addr, sr.GetCode(addr))
case SuicidePath:
stateObject := s.getStateObject(addr)
if stateObject != nil {
if stateObject != nil && sr.HasSelfDestructed(addr) {
s.SelfDestruct(addr)
}
default:
Expand Down
Loading
Loading