Skip to content

Commit b98016a

Browse files
committed
early mint
1 parent 4da59ab commit b98016a

File tree

9 files changed

+169
-5
lines changed

9 files changed

+169
-5
lines changed

blockchain/blockchain.go

Lines changed: 54 additions & 2 deletions
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,8 @@ type (
125129
timerFactory *prometheustimer.TimerFactory
126130

127131
// used by account-based model
128-
bbf BlockBuilderFactory
132+
bbf BlockBuilderFactory
133+
prepare *Prepare
129134
}
130135
)
131136

@@ -187,6 +192,7 @@ func NewBlockchain(cfg Config, g genesis.Genesis, dao blockdao.BlockDAO, bbf Blo
187192
bbf: bbf,
188193
clk: clock.New(),
189194
pubSubManager: NewPubSub(cfg.StreamingBlockBufferSize),
195+
prepare: newPrepare(),
190196
}
191197
for _, opt := range opts {
192198
if err := opt(chain); err != nil {
@@ -208,7 +214,7 @@ func NewBlockchain(cfg Config, g genesis.Genesis, dao blockdao.BlockDAO, bbf Blo
208214
}
209215
chain.lifecycle.Add(chain.dao)
210216
chain.lifecycle.Add(chain.pubSubManager)
211-
217+
chain.pubSubManager.AddBlockListener(chain.prepare)
212218
return chain
213219
}
214220

@@ -407,6 +413,41 @@ func (bc *blockchain) context(ctx context.Context, height uint64) (context.Conte
407413
return protocol.WithFeatureWithHeightCtx(ctx), nil
408414
}
409415

416+
func (bc *blockchain) PrepareBlock(height uint64, prevHash []byte, timestamp time.Time) error {
417+
ctx, err := bc.context(context.Background(), height-1)
418+
if err != nil {
419+
return err
420+
}
421+
tip := protocol.MustGetBlockchainCtx(ctx).Tip
422+
ctx = bc.contextWithBlock(ctx, bc.config.ProducerAddress(), height, timestamp, protocol.CalcBaseFee(genesis.MustExtractGenesisContext(ctx).Blockchain, &tip), protocol.CalcExcessBlobGas(tip.ExcessBlobGas, tip.BlobGasUsed))
423+
ctx = protocol.WithFeatureCtx(ctx)
424+
// run execution and update state trie root hash
425+
minterPrivateKey := bc.config.ProducerPrivateKey()
426+
427+
go bc.prepare.PrepareBlock(prevHash, func() (*block.Block, error) {
428+
blockBuilder, err := bc.bbf.NewBlockBuilderAt(
429+
ctx,
430+
func(elp action.Envelope) (*action.SealedEnvelope, error) {
431+
return action.Sign(elp, minterPrivateKey)
432+
},
433+
prevHash,
434+
)
435+
if err != nil {
436+
return nil, errors.Wrapf(err, "failed to create block builder at height %d", height)
437+
}
438+
blk, err := blockBuilder.SignAndBuild(minterPrivateKey)
439+
if err != nil {
440+
return nil, errors.Wrapf(err, "failed to create block at height %d", height)
441+
}
442+
443+
_blockMtc.WithLabelValues("MintGas").Set(float64(blk.GasUsed()))
444+
_blockMtc.WithLabelValues("MintActions").Set(float64(len(blk.Actions)))
445+
return &blk, nil
446+
})
447+
448+
return nil
449+
}
450+
410451
func (bc *blockchain) MintNewBlock(timestamp time.Time) (*block.Block, error) {
411452
bc.mu.RLock()
412453
defer bc.mu.RUnlock()
@@ -422,6 +463,17 @@ func (bc *blockchain) MintNewBlock(timestamp time.Time) (*block.Block, error) {
422463
return nil, err
423464
}
424465
tip := protocol.MustGetBlockchainCtx(ctx).Tip
466+
467+
// retrieve the draft block if it's prepared
468+
pblk, err := bc.prepare.WaitBlock(tip.Hash[:])
469+
if pblk != nil {
470+
return pblk, nil
471+
}
472+
if err != nil {
473+
log.L().Error("Failed to prepare new block", zap.Error(err))
474+
}
475+
476+
// create a new block
425477
ctx = bc.contextWithBlock(ctx, bc.config.ProducerAddress(), newblockHeight, timestamp, protocol.CalcBaseFee(genesis.MustExtractGenesisContext(ctx).Blockchain, &tip), protocol.CalcExcessBlobGas(tip.ExcessBlobGas, tip.BlobGasUsed))
426478
ctx = protocol.WithFeatureCtx(ctx)
427479
// run execution and update state trie root hash

blockchain/prepare.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package blockchain
2+
3+
import (
4+
"sync"
5+
6+
"github.com/iotexproject/go-pkgs/hash"
7+
8+
"github.com/iotexproject/iotex-core/v2/blockchain/block"
9+
)
10+
11+
type (
12+
Prepare struct {
13+
draftBlocks map[hash.Hash256]chan *mintResult
14+
mu sync.Mutex
15+
}
16+
mintResult struct {
17+
blk *block.Block
18+
err error
19+
}
20+
)
21+
22+
func newPrepare() *Prepare {
23+
return &Prepare{
24+
draftBlocks: make(map[hash.Hash256]chan *mintResult),
25+
}
26+
}
27+
28+
func (d *Prepare) PrepareBlock(prevHash []byte, mintFn func() (*block.Block, error)) {
29+
d.mu.Lock()
30+
res := make(chan *mintResult, 1)
31+
d.draftBlocks[hash.BytesToHash256(prevHash)] = res
32+
d.mu.Unlock()
33+
34+
blk, err := mintFn()
35+
res <- &mintResult{blk: blk, err: err}
36+
}
37+
38+
func (d *Prepare) WaitBlock(prevHash []byte) (*block.Block, error) {
39+
d.mu.Lock()
40+
defer d.mu.Unlock()
41+
hash := hash.Hash256(prevHash)
42+
if ch, ok := d.draftBlocks[hash]; ok {
43+
// wait for the draft block
44+
res := <-ch
45+
delete(d.draftBlocks, hash)
46+
if res.err == nil {
47+
return res.blk, nil
48+
}
49+
return nil, res.err
50+
}
51+
return nil, nil
52+
}
53+
54+
func (d *Prepare) ReceiveBlock(blk *block.Block) error {
55+
d.mu.Lock()
56+
keys := make([]hash.Hash256, 0, len(d.draftBlocks))
57+
for hash, ch := range d.draftBlocks {
58+
select {
59+
case res := <-ch:
60+
if res.err != nil {
61+
keys = append(keys, hash)
62+
} else if res.blk.Height() <= blk.Height() {
63+
keys = append(keys, hash)
64+
}
65+
default:
66+
}
67+
}
68+
for _, key := range keys {
69+
delete(d.draftBlocks, key)
70+
}
71+
d.mu.Unlock()
72+
return nil
73+
}

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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ type (
5454
// MintNewBlock creates a new block with given actions
5555
// Note: the coinbase transfer will be added to the given transfers when minting a new block
5656
MintNewBlock(timestamp time.Time) (*block.Block, error)
57+
// PrepareBlock prepares a new block with given parent hash
58+
PrepareBlock(height uint64, prevHash []byte, timestamp time.Time) error
5759
// CommitBlock validates and appends a block to the chain
5860
CommitBlock(blk *block.Block) error
5961
// ValidateBlock validates a new block before adding it to the blockchain
@@ -124,6 +126,11 @@ func (cm *chainManager) MintNewBlock(timestamp time.Time) (*block.Block, error)
124126
return cm.bc.MintNewBlock(timestamp)
125127
}
126128

129+
// PrepareBlock prepares a new block with given parent hash
130+
func (cm *chainManager) PrepareBlock(height uint64, prevHash []byte, timestamp time.Time) error {
131+
return cm.bc.PrepareBlock(height, prevHash, timestamp)
132+
}
133+
127134
// CommitBlock validates and appends a block to the chain
128135
func (cm *chainManager) CommitBlock(blk *block.Block) error {
129136
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)