Skip to content

Commit 1e2fdd3

Browse files
committed
early mint
1 parent 4da59ab commit 1e2fdd3

File tree

8 files changed

+136
-5
lines changed

8 files changed

+136
-5
lines changed

blockchain/blockchain.go

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ type (
9494
// MintNewBlock creates a new block with given actions
9595
// Note: the coinbase transfer will be added to the given transfers when minting a new block
9696
MintNewBlock(timestamp time.Time) (*block.Block, error)
97+
// PrepareBlock start to prepares a new block with given previous hash asynchrously
98+
// it can speed up the MintNewBlock
99+
PrepareBlock(height uint64, prevHash []byte, timestamp time.Time) error
97100
// CommitBlock validates and appends a block to the chain
98101
CommitBlock(blk *block.Block) error
99102
// ValidateBlock validates a new block before adding it to the blockchain
@@ -110,6 +113,7 @@ type (
110113
BlockBuilderFactory interface {
111114
// NewBlockBuilder creates block builder
112115
NewBlockBuilder(context.Context, func(action.Envelope) (*action.SealedEnvelope, error)) (*block.Builder, error)
116+
NewBlockBuilderAt(context.Context, func(action.Envelope) (*action.SealedEnvelope, error), []byte) (*block.Builder, error)
113117
}
114118

115119
// blockchain implements the Blockchain interface
@@ -125,7 +129,14 @@ type (
125129
timerFactory *prometheustimer.TimerFactory
126130

127131
// used by account-based model
128-
bbf BlockBuilderFactory
132+
bbf BlockBuilderFactory
133+
draftBlocks map[hash.Hash256]chan *mintResult
134+
draftMutex sync.Mutex
135+
}
136+
137+
mintResult struct {
138+
blk *block.Block
139+
err error
129140
}
130141
)
131142

@@ -407,6 +418,54 @@ func (bc *blockchain) context(ctx context.Context, height uint64) (context.Conte
407418
return protocol.WithFeatureWithHeightCtx(ctx), nil
408419
}
409420

421+
func (bc *blockchain) PrepareBlock(height uint64, prevHash []byte, timestamp time.Time) error {
422+
ctx, err := bc.context(context.Background(), height-1)
423+
if err != nil {
424+
return err
425+
}
426+
tip := protocol.MustGetBlockchainCtx(ctx).Tip
427+
ctx = bc.contextWithBlock(ctx, bc.config.ProducerAddress(), height, timestamp, protocol.CalcBaseFee(genesis.MustExtractGenesisContext(ctx).Blockchain, &tip), protocol.CalcExcessBlobGas(tip.ExcessBlobGas, tip.BlobGasUsed))
428+
ctx = protocol.WithFeatureCtx(ctx)
429+
// run execution and update state trie root hash
430+
minterPrivateKey := bc.config.ProducerPrivateKey()
431+
432+
bc.draftMutex.Lock()
433+
bc.draftBlocks[hash.BytesToHash256(prevHash)] = make(chan *mintResult, 1)
434+
bc.draftMutex.Unlock()
435+
go func() {
436+
blockBuilder, err := bc.bbf.NewBlockBuilderAt(
437+
ctx,
438+
func(elp action.Envelope) (*action.SealedEnvelope, error) {
439+
return action.Sign(elp, minterPrivateKey)
440+
},
441+
prevHash,
442+
)
443+
res := &mintResult{}
444+
defer func() {
445+
bc.draftMutex.Lock()
446+
if _, ok := bc.draftBlocks[hash.Hash256(prevHash)]; ok {
447+
bc.draftBlocks[hash.Hash256(prevHash)] <- res
448+
}
449+
bc.draftMutex.Unlock()
450+
}()
451+
if err != nil {
452+
res.err = errors.Wrapf(err, "failed to create block builder at height %d", height)
453+
return
454+
}
455+
blk, err := blockBuilder.SignAndBuild(minterPrivateKey)
456+
if err != nil {
457+
res.err = errors.Wrapf(err, "failed to create block at height %d", height)
458+
return
459+
}
460+
res.blk = &blk
461+
462+
_blockMtc.WithLabelValues("MintGas").Set(float64(blk.GasUsed()))
463+
_blockMtc.WithLabelValues("MintActions").Set(float64(len(blk.Actions)))
464+
}()
465+
466+
return nil
467+
}
468+
410469
func (bc *blockchain) MintNewBlock(timestamp time.Time) (*block.Block, error) {
411470
bc.mu.RLock()
412471
defer bc.mu.RUnlock()
@@ -422,6 +481,33 @@ func (bc *blockchain) MintNewBlock(timestamp time.Time) (*block.Block, error) {
422481
return nil, err
423482
}
424483
tip := protocol.MustGetBlockchainCtx(ctx).Tip
484+
485+
// retrieve the draft block if it's prepared
486+
pblk, err := func() (*block.Block, error) {
487+
bc.draftMutex.Lock()
488+
defer bc.draftMutex.Unlock()
489+
if ch, ok := bc.draftBlocks[tip.Hash]; ok {
490+
// wait for the draft block
491+
res := <-ch
492+
delete(bc.draftBlocks, tip.Hash)
493+
if res.err == nil {
494+
if res.blk.Timestamp() == timestamp {
495+
return res.blk, nil
496+
}
497+
return nil, errors.Errorf("block timestamp mismatch, expected %v, actual %v height %d", res.blk.Timestamp(), timestamp, newblockHeight)
498+
}
499+
return nil, res.err
500+
}
501+
return nil, nil
502+
}()
503+
if pblk != nil {
504+
return pblk, nil
505+
}
506+
if err != nil {
507+
log.L().Error("Failed to prepare new block", zap.Error(err))
508+
}
509+
510+
// create a new block
425511
ctx = bc.contextWithBlock(ctx, bc.config.ProducerAddress(), newblockHeight, timestamp, protocol.CalcBaseFee(genesis.MustExtractGenesisContext(ctx).Blockchain, &tip), protocol.CalcExcessBlobGas(tip.ExcessBlobGas, tip.BlobGasUsed))
426512
ctx = protocol.WithFeatureCtx(ctx)
427513
// run execution and update state trie root hash
@@ -539,6 +625,10 @@ func (bc *blockchain) commitBlock(blk *block.Block) error {
539625
_blockMtc.WithLabelValues("blobGasUsed").Set(float64(blk.BlobGasUsed()))
540626
// emit block to all block subscribers
541627
bc.emitToSubscribers(blk)
628+
629+
bc.draftMutex.Lock()
630+
delete(bc.draftBlocks, blk.HashBlock())
631+
bc.draftMutex.Unlock()
542632
return nil
543633
}
544634

consensus/consensusfsm/context.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,5 @@ type Context interface {
4141
NewPreCommitEndorsement(interface{}) (interface{}, error)
4242
Commit(interface{}) (bool, error)
4343
ConsensusConfig
44+
PrepareNextProposal(any) error
4445
}

consensus/consensusfsm/fsm.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ func (m *ConsensusFSM) onReceiveProposalEndorsement(evt fsm.Event, currentState
504504
}
505505
m.ProduceReceiveLockEndorsementEvent(lockEndorsement)
506506
m.ctx.Broadcast(lockEndorsement)
507-
507+
m.ctx.PrepareNextProposal(lockEndorsement)
508508
return sAcceptLockEndorsement, err
509509
}
510510

consensus/scheme/rolldpos/rolldpos.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/facebookgo/clock"
1313
"github.com/iotexproject/go-fsm"
1414
"github.com/iotexproject/go-pkgs/crypto"
15+
"github.com/iotexproject/go-pkgs/hash"
1516
"github.com/iotexproject/iotex-proto/golang/iotextypes"
1617
"github.com/pkg/errors"
1718
"go.uber.org/zap"
@@ -54,6 +55,8 @@ type (
5455
// MintNewBlock creates a new block with given actions
5556
// Note: the coinbase transfer will be added to the given transfers when minting a new block
5657
MintNewBlock(timestamp time.Time) (*block.Block, error)
58+
// PrepareBlock prepares a new block with given parent hash
59+
PrepareBlock(height uint64, prevHash []byte, timestamp time.Time) error
5760
// CommitBlock validates and appends a block to the chain
5861
CommitBlock(blk *block.Block) error
5962
// ValidateBlock validates a new block before adding it to the blockchain
@@ -65,7 +68,8 @@ type (
6568
}
6669

6770
chainManager struct {
68-
bc blockchain.Blockchain
71+
bc blockchain.Blockchain
72+
draftBlocks map[hash.Hash256]*block.Block
6973
}
7074
)
7175

@@ -124,6 +128,11 @@ func (cm *chainManager) MintNewBlock(timestamp time.Time) (*block.Block, error)
124128
return cm.bc.MintNewBlock(timestamp)
125129
}
126130

131+
// PrepareBlock prepares a new block with given parent hash
132+
func (cm *chainManager) PrepareBlock(height uint64, prevHash []byte, timestamp time.Time) error {
133+
return cm.bc.PrepareBlock(height, prevHash, timestamp)
134+
}
135+
127136
// CommitBlock validates and appends a block to the chain
128137
func (cm *chainManager) CommitBlock(blk *block.Block) error {
129138
return cm.bc.CommitBlock(blk)

consensus/scheme/rolldpos/rolldposctx.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,14 @@ func (ctx *rollDPoSCtx) Proposal() (interface{}, error) {
370370
return ctx.mintNewBlock()
371371
}
372372

373+
func (ctx *rollDPoSCtx) PrepareNextProposal(endorse any) error {
374+
if ctx.encodedAddr != ctx.round.nextRoundProposer {
375+
return nil
376+
}
377+
prevHash := endorse.(*EndorsedConsensusMessage).Document().(*ConsensusVote).blkHash
378+
return ctx.chain.PrepareBlock(ctx.round.height+1, prevHash, ctx.round.nextRoundStartTime)
379+
}
380+
373381
func (ctx *rollDPoSCtx) WaitUntilRoundStart() time.Duration {
374382
ctx.mutex.RLock()
375383
defer ctx.mutex.RUnlock()

consensus/scheme/rolldpos/roundcalculator.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ func (c *roundCalculator) UpdateRound(round *roundCtx, height uint64, blockInter
6060
if err != nil {
6161
return nil, err
6262
}
63+
nextProposers, err := c.Proposers(height + 1)
64+
if err != nil {
65+
return nil, err
66+
}
67+
nextProposer, err := c.calculateProposer(height+1, 0, nextProposers)
68+
if err != nil {
69+
return nil, err
70+
}
6371
var status status
6472
var blockInLock []byte
6573
var proofOfLock []*endorsement.Endorsement
@@ -89,6 +97,7 @@ func (c *roundCalculator) UpdateRound(round *roundCtx, height uint64, blockInter
8997
proposer: proposer,
9098
roundStartTime: roundStartTime,
9199
nextRoundStartTime: roundStartTime.Add(blockInterval),
100+
nextRoundProposer: nextProposer,
92101
eManager: round.eManager,
93102
status: status,
94103
blockInLock: blockInLock,
@@ -219,9 +228,9 @@ func (c *roundCalculator) newRound(
219228
) (round *roundCtx, err error) {
220229
epochNum := uint64(0)
221230
epochStartHeight := uint64(0)
222-
var delegates, proposers []string
231+
var delegates, proposers, nextProposers []string
223232
var roundNum uint32
224-
var proposer string
233+
var proposer, nextProposer string
225234
var roundStartTime time.Time
226235
if height != 0 {
227236
epochNum = c.rp.GetEpochNum(height)
@@ -239,6 +248,12 @@ func (c *roundCalculator) newRound(
239248
return
240249
}
241250
}
251+
if nextProposers, err = c.Proposers(height + 1); err != nil {
252+
return
253+
}
254+
if nextProposer, err = c.calculateProposer(height+1, 0, nextProposers); err != nil {
255+
return
256+
}
242257
if eManager == nil {
243258
if eManager, err = newEndorsementManager(nil, nil); err != nil {
244259
return nil, err
@@ -257,6 +272,7 @@ func (c *roundCalculator) newRound(
257272
eManager: eManager,
258273
roundStartTime: roundStartTime,
259274
nextRoundStartTime: roundStartTime.Add(blockInterval),
275+
nextRoundProposer: nextProposer,
260276
status: _open,
261277
}
262278
eManager.SetIsMarjorityFunc(round.EndorsedByMajority)

consensus/scheme/rolldpos/roundctx.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type roundCtx struct {
3838
roundNum uint32
3939
proposer string
4040
roundStartTime time.Time
41+
nextRoundProposer string
4142
nextRoundStartTime time.Time
4243

4344
blockInLock []byte

state/factory/minter.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package factory
77

88
import (
99
"context"
10+
"errors"
1011
"time"
1112

1213
"github.com/iotexproject/iotex-core/v2/action"
@@ -49,3 +50,8 @@ func (m *minter) NewBlockBuilder(ctx context.Context, sign func(action.Envelope)
4950
}
5051
return m.f.NewBlockBuilder(ctx, m.ap, sign)
5152
}
53+
54+
func (m *minter) NewBlockBuilderAt(ctx context.Context, sign func(action.Envelope) (*action.SealedEnvelope, error), prevHash []byte) (*block.Builder, error) {
55+
// TODO: implement this
56+
return nil, errors.New("not implemented")
57+
}

0 commit comments

Comments
 (0)