Skip to content

Commit 381a819

Browse files
committed
Merge remote-tracking branch 'origin/develop' into v2.3.4-candidate
2 parents 9f13af9 + bb3874c commit 381a819

37 files changed

+1217
-490
lines changed

consensus/bor/bor.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,11 @@ type Bor struct {
249249
fakeDiff bool // Skip difficulty verifications
250250
DevFakeAuthor bool
251251

252+
// 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.
253+
blockTime time.Duration
254+
255+
lastMinedBlockTime time.Time
256+
252257
quit chan struct{}
253258
closeOnce sync.Once
254259
}
@@ -268,6 +273,7 @@ func New(
268273
heimdallWSClient IHeimdallWSClient,
269274
genesisContracts GenesisContract,
270275
devFakeAuthor bool,
276+
blockTime time.Duration,
271277
) *Bor {
272278
// get bor config
273279
borConfig := chainConfig.Bor
@@ -308,6 +314,7 @@ func New(
308314
HeimdallWSClient: heimdallWSClient,
309315
spanStore: spanStore,
310316
DevFakeAuthor: devFakeAuthor,
317+
blockTime: blockTime,
311318
quit: make(chan struct{}),
312319
}
313320

@@ -986,17 +993,34 @@ func (c *Bor) Prepare(chain consensus.ChainHeaderReader, header *types.Header) e
986993
}
987994
}
988995

989-
header.Time = parent.Time + CalcProducerDelay(number, succession, c.config)
996+
if c.blockTime > 0 && uint64(c.blockTime.Seconds()) < c.config.CalculatePeriod(number) {
997+
return fmt.Errorf("the floor of custom mining block time (%v) is less than the consensus block time: %v < %v", c.blockTime, c.blockTime.Seconds(), c.config.CalculatePeriod(number))
998+
}
999+
1000+
if c.blockTime > 0 && c.config.IsRio(header.Number) {
1001+
// Only enable custom block time for Rio and later
1002+
parentActualTime := c.lastMinedBlockTime
1003+
if parentActualTime.IsZero() || parentActualTime.Before(time.Unix(int64(parent.Time), 0)) {
1004+
parentActualTime = time.Unix(int64(parent.Time), 0)
1005+
}
1006+
actualNewBlockTime := parentActualTime.Add(c.blockTime)
1007+
c.lastMinedBlockTime = actualNewBlockTime
1008+
header.Time = uint64(actualNewBlockTime.Unix())
1009+
header.ActualTime = actualNewBlockTime
1010+
} else {
1011+
header.Time = parent.Time + CalcProducerDelay(number, succession, c.config)
1012+
}
1013+
9901014
if header.Time < uint64(time.Now().Unix()) {
991-
header.Time = uint64(time.Now().Unix()) + CalcProducerDelay(number, succession, c.config)
1015+
header.Time = uint64(time.Now().Unix())
9921016
} else {
9931017
// For primary validators, wait until the current block production window
9941018
// starts. This prevents bor from starting to build next block before time
9951019
// as we'd like to wait for new transactions. Although this change doesn't
9961020
// need a check for hard fork as it doesn't change any consensus rules, we
9971021
// still keep it for safety and testing.
9981022
if c.config.IsBhilai(big.NewInt(int64(number))) && succession == 0 {
999-
startTime := time.Unix(int64(header.Time-c.config.CalculatePeriod(number)), 0)
1023+
startTime := header.GetActualTime().Add(-time.Duration(c.config.CalculatePeriod(number)) * time.Second)
10001024
time.Sleep(time.Until(startTime))
10011025
}
10021026
}
@@ -1202,15 +1226,15 @@ func (c *Bor) Seal(chain consensus.ChainHeaderReader, block *types.Block, witnes
12021226

12031227
// Sweet, the protocol permits us to sign the block, wait for our time
12041228
if c.config.IsBhilai(header.Number) {
1205-
delay = time.Until(time.Unix(int64(header.Time), 0)) // Wait until we reach header time for non-primary validators
1229+
delay = time.Until(header.GetActualTime()) // Wait until we reach header time for non-primary validators
12061230
// Disable early block announcement
12071231
// if successionNumber == 0 {
12081232
// // For primary producers, set the delay to `header.Time - block time` instead of `header.Time`
12091233
// // for early block announcement instead of waiting for full block time.
12101234
// delay = time.Until(time.Unix(int64(header.Time-c.config.CalculatePeriod(number)), 0))
12111235
// }
12121236
} else {
1213-
delay = time.Until(time.Unix(int64(header.Time), 0)) // Wait until we reach header time
1237+
delay = time.Until(header.GetActualTime()) // Wait until we reach header time
12141238
}
12151239

12161240
// wiggle was already accounted for in header.Time, this is just for logging

consensus/bor/bor_test.go

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"math/big"
66
"testing"
7+
"time"
78

89
"github.com/holiman/uint256"
910
"github.com/stretchr/testify/require"
@@ -530,3 +531,204 @@ func TestSnapshot(t *testing.T) {
530531
})
531532
}
532533
}
534+
535+
func TestCustomBlockTimeValidation(t *testing.T) {
536+
t.Parallel()
537+
538+
addr1 := common.HexToAddress("0x1")
539+
540+
testCases := []struct {
541+
name string
542+
blockTime time.Duration
543+
consensusPeriod uint64
544+
blockNumber uint64
545+
expectError bool
546+
description string
547+
}{
548+
{
549+
name: "blockTime is zero (default) - should succeed",
550+
blockTime: 0,
551+
consensusPeriod: 2,
552+
blockNumber: 1,
553+
expectError: false,
554+
description: "Default blockTime of 0 should use standard consensus delay",
555+
},
556+
{
557+
name: "blockTime equals consensus period - should succeed",
558+
blockTime: 2 * time.Second,
559+
consensusPeriod: 2,
560+
blockNumber: 1,
561+
expectError: false,
562+
description: "Custom blockTime equal to consensus period should be valid",
563+
},
564+
{
565+
name: "blockTime greater than consensus period - should succeed",
566+
blockTime: 5 * time.Second,
567+
consensusPeriod: 2,
568+
blockNumber: 1,
569+
expectError: false,
570+
description: "Custom blockTime greater than consensus period should be valid",
571+
},
572+
{
573+
name: "blockTime less than consensus period - should fail",
574+
blockTime: 1 * time.Second,
575+
consensusPeriod: 2,
576+
blockNumber: 1,
577+
expectError: true,
578+
description: "Custom blockTime less than consensus period should be invalid",
579+
},
580+
}
581+
582+
for _, tc := range testCases {
583+
t.Run(tc.name, func(t *testing.T) {
584+
sp := &fakeSpanner{vals: []*valset.Validator{{Address: addr1, VotingPower: 1}}}
585+
borCfg := &params.BorConfig{
586+
Sprint: map[string]uint64{"0": 64},
587+
Period: map[string]uint64{"0": tc.consensusPeriod},
588+
RioBlock: big.NewInt(0), // Enable Rio from genesis
589+
}
590+
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1)
591+
b.blockTime = tc.blockTime
592+
593+
// Get genesis block as parent
594+
genesis := chain.HeaderChain().GetHeaderByNumber(0)
595+
require.NotNil(t, genesis)
596+
597+
header := &types.Header{
598+
Number: big.NewInt(int64(tc.blockNumber)),
599+
ParentHash: genesis.Hash(),
600+
}
601+
602+
err := b.Prepare(chain.HeaderChain(), header)
603+
604+
if tc.expectError {
605+
require.Error(t, err, tc.description)
606+
require.Contains(t, err.Error(), "less than the consensus block time", tc.description)
607+
} else {
608+
require.NoError(t, err, tc.description)
609+
}
610+
})
611+
}
612+
}
613+
614+
func TestCustomBlockTimeCalculation(t *testing.T) {
615+
t.Parallel()
616+
617+
addr1 := common.HexToAddress("0x1")
618+
619+
t.Run("sequential blocks with custom blockTime", func(t *testing.T) {
620+
sp := &fakeSpanner{vals: []*valset.Validator{{Address: addr1, VotingPower: 1}}}
621+
borCfg := &params.BorConfig{
622+
Sprint: map[string]uint64{"0": 64},
623+
Period: map[string]uint64{"0": 2},
624+
RioBlock: big.NewInt(0),
625+
}
626+
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1)
627+
b.blockTime = 5 * time.Second
628+
629+
genesis := chain.HeaderChain().GetHeaderByNumber(0)
630+
require.NotNil(t, genesis)
631+
baseTime := genesis.Time
632+
633+
header1 := &types.Header{
634+
Number: big.NewInt(1),
635+
ParentHash: genesis.Hash(),
636+
}
637+
err := b.Prepare(chain.HeaderChain(), header1)
638+
require.NoError(t, err)
639+
640+
require.False(t, header1.ActualTime.IsZero(), "ActualTime should be set")
641+
expectedTime := time.Unix(int64(baseTime), 0).Add(5 * time.Second)
642+
require.Equal(t, expectedTime.Unix(), header1.ActualTime.Unix())
643+
})
644+
645+
t.Run("lastMinedBlockTime is zero (first block)", func(t *testing.T) {
646+
sp := &fakeSpanner{vals: []*valset.Validator{{Address: addr1, VotingPower: 1}}}
647+
borCfg := &params.BorConfig{
648+
Sprint: map[string]uint64{"0": 64},
649+
Period: map[string]uint64{"0": 2},
650+
RioBlock: big.NewInt(0),
651+
}
652+
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1)
653+
b.blockTime = 3 * time.Second
654+
655+
genesis := chain.HeaderChain().GetHeaderByNumber(0)
656+
require.NotNil(t, genesis)
657+
baseTime := genesis.Time
658+
659+
header := &types.Header{
660+
Number: big.NewInt(1),
661+
ParentHash: genesis.Hash(),
662+
}
663+
664+
err := b.Prepare(chain.HeaderChain(), header)
665+
require.NoError(t, err)
666+
667+
expectedTime := time.Unix(int64(baseTime), 0).Add(3 * time.Second)
668+
require.Equal(t, expectedTime.Unix(), header.ActualTime.Unix())
669+
})
670+
671+
t.Run("lastMinedBlockTime before parent time (fallback)", func(t *testing.T) {
672+
sp := &fakeSpanner{vals: []*valset.Validator{{Address: addr1, VotingPower: 1}}}
673+
borCfg := &params.BorConfig{
674+
Sprint: map[string]uint64{"0": 64},
675+
Period: map[string]uint64{"0": 2},
676+
RioBlock: big.NewInt(0),
677+
}
678+
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1)
679+
b.blockTime = 4 * time.Second
680+
681+
genesis := chain.HeaderChain().GetHeaderByNumber(0)
682+
require.NotNil(t, genesis)
683+
baseTime := genesis.Time
684+
685+
if baseTime > 10 {
686+
b.lastMinedBlockTime = time.Unix(int64(baseTime-10), 0)
687+
} else {
688+
b.lastMinedBlockTime = time.Unix(0, 0)
689+
}
690+
691+
header := &types.Header{
692+
Number: big.NewInt(1),
693+
ParentHash: genesis.Hash(),
694+
}
695+
696+
err := b.Prepare(chain.HeaderChain(), header)
697+
require.NoError(t, err)
698+
699+
expectedTime := time.Unix(int64(baseTime), 0).Add(4 * time.Second)
700+
require.Equal(t, expectedTime.Unix(), header.ActualTime.Unix())
701+
})
702+
}
703+
704+
func TestCustomBlockTimeBackwardCompatibility(t *testing.T) {
705+
t.Parallel()
706+
707+
addr1 := common.HexToAddress("0x1")
708+
709+
t.Run("blockTime is zero uses standard CalcProducerDelay", func(t *testing.T) {
710+
sp := &fakeSpanner{vals: []*valset.Validator{{Address: addr1, VotingPower: 1}}}
711+
borCfg := &params.BorConfig{
712+
Sprint: map[string]uint64{"0": 64},
713+
Period: map[string]uint64{"0": 2},
714+
ProducerDelay: map[string]uint64{"0": 3},
715+
BackupMultiplier: map[string]uint64{"0": 2},
716+
RioBlock: big.NewInt(0),
717+
}
718+
chain, b := newChainAndBorForTest(t, sp, borCfg, true, addr1)
719+
b.blockTime = 0
720+
721+
genesis := chain.HeaderChain().GetHeaderByNumber(0)
722+
require.NotNil(t, genesis)
723+
724+
header := &types.Header{
725+
Number: big.NewInt(1),
726+
ParentHash: genesis.Hash(),
727+
}
728+
729+
err := b.Prepare(chain.HeaderChain(), header)
730+
require.NoError(t, err)
731+
732+
require.True(t, header.ActualTime.IsZero(), "ActualTime should not be set when blockTime is 0")
733+
})
734+
}

core/blockchain.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2573,7 +2573,7 @@ func (bc *BlockChain) InsertChainStateless(chain types.Blocks, witnesses []*stat
25732573
statedb.SetWitness(witness)
25742574

25752575
// Write the block to the chain without committing state
2576-
if _, err := bc.writeBlockAndSetHead(block, nil, nil, statedb, false, true); err != nil {
2576+
if _, err := bc.writeBlockAndSetHead(block, res.Receipts, res.Logs, statedb, false, true); err != nil {
25772577
return processed, err
25782578
}
25792579

core/blockchain_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2145,7 +2145,7 @@ func testInsertReceiptChainRollback(t *testing.T, scheme string) {
21452145
}
21462146

21472147
// Set up a BlockChain that uses the ancient store.
2148-
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
2148+
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false, false)
21492149
if err != nil {
21502150
t.Fatalf("failed to create temp freezer db: %v", err)
21512151
}

core/rawdb/accessors_chain.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,3 +1117,21 @@ func ReadHeadBlock(db ethdb.Reader) *types.Block {
11171117

11181118
return ReadBlock(db, headBlockHash, *headBlockNumber)
11191119
}
1120+
1121+
func ReadBlockPruneCursor(db ethdb.KeyValueReader) *uint64 {
1122+
log.Debug("ReadBlockCursor")
1123+
data, err := db.Get(blockPruneCursorKey())
1124+
if err != nil || len(data) == 0 {
1125+
return nil
1126+
}
1127+
1128+
number := binary.BigEndian.Uint64(data)
1129+
return &number
1130+
}
1131+
1132+
func WriteBlockPruneCursor(db ethdb.KeyValueWriter, cursor uint64) {
1133+
log.Debug("WriteBlockPruneCursor", "cursor", cursor)
1134+
if err := db.Put(blockPruneCursorKey(), encodeBlockNumber(cursor)); err != nil {
1135+
log.Crit("Failed to store block cursor", "err", err)
1136+
}
1137+
}

core/rawdb/accessors_state.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,6 @@ func ReadWitnessPruneCursor(db ethdb.KeyValueReader) *uint64 {
373373
func WriteWitnessPruneCursor(db ethdb.KeyValueWriter, cursor uint64) {
374374
log.Debug("WriteWitnessPruneCursor", "cursor", cursor)
375375
if err := db.Put(witnessPruneCursorKey(), encodeBlockNumber(cursor)); err != nil {
376-
log.Crit("Failed to store witness", "err", err)
376+
log.Crit("Failed to store witness cursor", "err", err)
377377
}
378378
}

0 commit comments

Comments
 (0)