@@ -20,12 +20,12 @@ import (
2020 "github.com/prometheus/client_golang/prometheus"
2121 "go.uber.org/zap"
2222
23- "github.com/iotexproject/iotex-core/v2/action"
2423 "github.com/iotexproject/iotex-core/v2/action/protocol"
2524 "github.com/iotexproject/iotex-core/v2/blockchain/block"
2625 "github.com/iotexproject/iotex-core/v2/blockchain/blockdao"
2726 "github.com/iotexproject/iotex-core/v2/blockchain/filedao"
2827 "github.com/iotexproject/iotex-core/v2/blockchain/genesis"
28+ "github.com/iotexproject/iotex-core/v2/db"
2929 "github.com/iotexproject/iotex-core/v2/pkg/lifecycle"
3030 "github.com/iotexproject/iotex-core/v2/pkg/log"
3131 "github.com/iotexproject/iotex-core/v2/pkg/prometheustimer"
9898 CommitBlock (blk * block.Block ) error
9999 // ValidateBlock validates a new block before adding it to the blockchain
100100 ValidateBlock (* block.Block , ... BlockValidationOption ) error
101+ // Fork returns a new blockchain instance with the given tip hash
102+ Fork (hash.Hash256 ) (Blockchain , error )
101103
102104 // AddSubscriber make you listen to every single produced block
103105 AddSubscriber (BlockCreationSubscriber ) error
@@ -108,8 +110,12 @@ type (
108110
109111 // BlockBuilderFactory is the factory interface of block builder
110112 BlockBuilderFactory interface {
111- // NewBlockBuilder creates block builder
112- NewBlockBuilder (context.Context , func (action.Envelope ) (* action.SealedEnvelope , error )) (* block.Builder , error )
113+ Mint (ctx context.Context ) (* block.Block , error )
114+ ReceiveBlock (* block.Block ) error
115+ Init (hash.Hash256 )
116+ AddProposal (* block.Block ) error
117+ Block (hash.Hash256 ) * block.Block
118+ BlockByHeight (uint64 ) * block.Block
113119 }
114120
115121 // blockchain implements the Blockchain interface
@@ -123,6 +129,7 @@ type (
123129 clk clock.Clock
124130 pubSubManager PubSubManager
125131 timerFactory * prometheustimer.TimerFactory
132+ head * block.Header
126133
127134 // used by account-based model
128135 bbf BlockBuilderFactory
@@ -208,7 +215,7 @@ func NewBlockchain(cfg Config, g genesis.Genesis, dao blockdao.BlockDAO, bbf Blo
208215 }
209216 chain .lifecycle .Add (chain .dao )
210217 chain .lifecycle .Add (chain .pubSubManager )
211-
218+ chain . pubSubManager . AddBlockListener ( chain . bbf )
212219 return chain
213220}
214221
@@ -238,7 +245,20 @@ func (bc *blockchain) Start(ctx context.Context) error {
238245 EvmNetworkID : bc .EvmNetworkID (),
239246 },
240247 ), bc .genesis ))
241- return bc .lifecycle .OnStart (ctx )
248+ if err := bc .lifecycle .OnStart (ctx ); err != nil {
249+ return err
250+ }
251+ tip , err := bc .dao .Height ()
252+ if err != nil {
253+ return err
254+ }
255+ header , err := bc .dao .HeaderByHeight (tip )
256+ if err != nil {
257+ return err
258+ }
259+ bc .head = header
260+ bc .bbf .Init (header .HashBlock ())
261+ return nil
242262}
243263
244264// Stop stops the blockchain.
@@ -249,7 +269,20 @@ func (bc *blockchain) Stop(ctx context.Context) error {
249269}
250270
251271func (bc * blockchain ) BlockHeaderByHeight (height uint64 ) (* block.Header , error ) {
252- return bc .dao .HeaderByHeight (height )
272+ header , err := bc .dao .HeaderByHeight (height )
273+ switch errors .Cause (err ) {
274+ case nil :
275+ return header , nil
276+ case db .ErrNotExist :
277+ bc .mu .RLock ()
278+ defer bc .mu .RUnlock ()
279+ if blk := bc .draftBlockByHeight (height ); blk != nil {
280+ return & blk .Header , nil
281+ }
282+ return nil , err
283+ default :
284+ return nil , err
285+ }
253286}
254287
255288func (bc * blockchain ) BlockFooterByHeight (height uint64 ) (* block.Footer , error ) {
@@ -258,24 +291,18 @@ func (bc *blockchain) BlockFooterByHeight(height uint64) (*block.Footer, error)
258291
259292// TipHash returns tip block's hash
260293func (bc * blockchain ) TipHash () hash.Hash256 {
261- tipHeight , err := bc .dao .Height ()
262- if err != nil {
263- return hash .ZeroHash256
264- }
265- tipHash , err := bc .dao .GetBlockHash (tipHeight )
266- if err != nil {
267- return hash .ZeroHash256
268- }
269- return tipHash
294+ bc .mu .RLock ()
295+ defer bc .mu .RUnlock ()
296+
297+ return bc .head .HashBlock ()
270298}
271299
272300// TipHeight returns tip block's height
273301func (bc * blockchain ) TipHeight () uint64 {
274- tipHeight , err := bc .dao .Height ()
275- if err != nil {
276- log .L ().Panic ("failed to get tip height" , zap .Error (err ))
277- }
278- return tipHeight
302+ bc .mu .RLock ()
303+ defer bc .mu .RUnlock ()
304+
305+ return bc .head .Height ()
279306}
280307
281308// ValidateBlock validates a new block before adding it to the blockchain
@@ -287,10 +314,7 @@ func (bc *blockchain) ValidateBlock(blk *block.Block, opts ...BlockValidationOpt
287314 if blk == nil {
288315 return ErrInvalidBlock
289316 }
290- tipHeight , err := bc .dao .Height ()
291- if err != nil {
292- return err
293- }
317+ tipHeight := bc .head .Height ()
294318 tip , err := bc .tipInfo (tipHeight )
295319 if err != nil {
296320 return err
@@ -355,7 +379,14 @@ func (bc *blockchain) ValidateBlock(blk *block.Block, opts ...BlockValidationOpt
355379 if bc .blockValidator == nil {
356380 return nil
357381 }
358- return bc .blockValidator .Validate (ctx , blk )
382+ err = bc .blockValidator .Validate (ctx , blk )
383+ if err != nil {
384+ return err
385+ }
386+ if err = bc .bbf .AddProposal (blk ); err != nil {
387+ log .L ().Warn ("failed to add block to proposal pool" , zap .Error (err ), zap .Uint64 ("height" , blk .Height ()), zap .Time ("timestamp" , blk .Timestamp ()))
388+ }
389+ return nil
359390}
360391
361392func (bc * blockchain ) Context (ctx context.Context ) (context.Context , error ) {
@@ -392,7 +423,10 @@ func (bc *blockchain) context(ctx context.Context, height uint64) (context.Conte
392423 if err != nil {
393424 return nil , err
394425 }
426+ return bc .contextWithTipInfo (ctx , tip ), nil
427+ }
395428
429+ func (bc * blockchain ) contextWithTipInfo (ctx context.Context , tip * protocol.TipInfo ) context.Context {
396430 ctx = genesis .WithGenesisContext (
397431 protocol .WithBlockchainCtx (
398432 ctx ,
@@ -404,7 +438,7 @@ func (bc *blockchain) context(ctx context.Context, height uint64) (context.Conte
404438 ),
405439 bc .genesis ,
406440 )
407- return protocol .WithFeatureWithHeightCtx (ctx ), nil
441+ return protocol .WithFeatureWithHeightCtx (ctx )
408442}
409443
410444func (bc * blockchain ) MintNewBlock (timestamp time.Time ) (* block.Block , error ) {
@@ -422,30 +456,23 @@ func (bc *blockchain) MintNewBlock(timestamp time.Time) (*block.Block, error) {
422456 return nil , err
423457 }
424458 tip := protocol .MustGetBlockchainCtx (ctx ).Tip
459+ // create a new block
460+ log .L ().Debug ("Produce a new block." , zap .Uint64 ("height" , newblockHeight ), zap .Time ("timestamp" , timestamp ))
425461 ctx = bc .contextWithBlock (ctx , bc .config .ProducerAddress (), newblockHeight , timestamp , protocol .CalcBaseFee (genesis .MustExtractGenesisContext (ctx ).Blockchain , & tip ), protocol .CalcExcessBlobGas (tip .ExcessBlobGas , tip .BlobGasUsed ))
426462 ctx = protocol .WithFeatureCtx (ctx )
427463 // run execution and update state trie root hash
428- minterPrivateKey := bc .config .ProducerPrivateKey ()
429- blockBuilder , err := bc .bbf .NewBlockBuilder (
430- ctx ,
431- func (elp action.Envelope ) (* action.SealedEnvelope , error ) {
432- return action .Sign (elp , minterPrivateKey )
433- },
434- )
435- if err != nil {
436- return nil , errors .Wrapf (err , "failed to create block builder at new block height %d" , newblockHeight )
437- }
438- blk , err := blockBuilder .SignAndBuild (minterPrivateKey )
464+ blk , err := bc .bbf .Mint (ctx )
439465 if err != nil {
440466 return nil , errors .Wrapf (err , "failed to create block" )
441467 }
442468 _blockMtc .WithLabelValues ("MintGas" ).Set (float64 (blk .GasUsed ()))
443469 _blockMtc .WithLabelValues ("MintActions" ).Set (float64 (len (blk .Actions )))
444- return & blk , nil
470+ return blk , nil
445471}
446472
447473// CommitBlock validates and appends a block to the chain
448474func (bc * blockchain ) CommitBlock (blk * block.Block ) error {
475+ log .L ().Debug ("Commit a block." , zap .Uint64 ("height" , blk .Height ()), zap .String ("ioAddr" , bc .config .ProducerAddress ().String ()))
449476 bc .mu .Lock ()
450477 defer bc .mu .Unlock ()
451478 timer := bc .timerFactory .NewTimer ("CommitBlock" )
@@ -466,6 +493,21 @@ func (bc *blockchain) RemoveSubscriber(s BlockCreationSubscriber) error {
466493 return bc .pubSubManager .RemoveBlockListener (s )
467494}
468495
496+ func (bc * blockchain ) Fork (hash hash.Hash256 ) (Blockchain , error ) {
497+ bc .mu .RLock ()
498+ defer bc .mu .RUnlock ()
499+ if bc .head .HashBlock () == hash {
500+ return bc , nil
501+ }
502+ headBlk := bc .bbf .Block (hash )
503+ if headBlk == nil {
504+ return nil , errors .Errorf ("block %x is not in the proposal pool, tip height %d" , hash , bc .head .Height ())
505+ }
506+ fork := * bc
507+ fork .head = & headBlk .Header
508+ return & fork , nil
509+ }
510+
469511//======================================
470512// internal functions
471513//=====================================
@@ -486,11 +528,10 @@ func (bc *blockchain) tipInfo(tipHeight uint64) (*protocol.TipInfo, error) {
486528 Timestamp : time .Unix (bc .genesis .Timestamp , 0 ),
487529 }, nil
488530 }
489- header , err := bc .dao . HeaderByHeight (tipHeight )
531+ header , err := bc .blockByHeight (tipHeight )
490532 if err != nil {
491533 return nil , err
492534 }
493-
494535 return & protocol.TipInfo {
495536 Height : tipHeight ,
496537 GasUsed : header .GasUsed (),
@@ -502,6 +543,38 @@ func (bc *blockchain) tipInfo(tipHeight uint64) (*protocol.TipInfo, error) {
502543 }, nil
503544}
504545
546+ func (bc * blockchain ) tipInfoByHash (tipHash []byte ) (* protocol.TipInfo , error ) {
547+ if hash .Hash256 (tipHash ) == bc .genesis .Hash () {
548+ return & protocol.TipInfo {
549+ Height : 0 ,
550+ Hash : bc .genesis .Hash (),
551+ Timestamp : time .Unix (bc .genesis .Timestamp , 0 ),
552+ }, nil
553+ }
554+
555+ header , err := bc .dao .Header (hash .Hash256 (tipHash ))
556+ switch errors .Cause (err ) {
557+ case nil :
558+ case db .ErrNotExist :
559+ if blk := bc .bbf .Block (hash .Hash256 (tipHash )); blk == nil {
560+ return nil , err
561+ } else {
562+ header = & blk .Header
563+ }
564+ default :
565+ return nil , err
566+ }
567+ return & protocol.TipInfo {
568+ Height : header .Height (),
569+ GasUsed : header .GasUsed (),
570+ Hash : header .HashBlock (),
571+ Timestamp : header .Timestamp (),
572+ BaseFee : header .BaseFee (),
573+ BlobGasUsed : header .BlobGasUsed (),
574+ ExcessBlobGas : header .ExcessBlobGas (),
575+ }, nil
576+ }
577+
505578// commitBlock commits a block to the chain
506579func (bc * blockchain ) commitBlock (blk * block.Block ) error {
507580 tipHeight , err := bc .dao .Height ()
@@ -526,6 +599,7 @@ func (bc *blockchain) commitBlock(blk *block.Block) error {
526599 default :
527600 return err
528601 }
602+ bc .head = & blk .Header
529603 blkHash := blk .HashBlock ()
530604 if blk .Height ()% 100 == 0 {
531605 blk .HeaderLogger (log .L ()).Info ("Committed a block." , log .Hex ("tipHash" , blkHash [:]))
@@ -548,3 +622,26 @@ func (bc *blockchain) emitToSubscribers(blk *block.Block) {
548622 }
549623 bc .pubSubManager .SendBlockToSubscribers (blk )
550624}
625+
626+ func (bc * blockchain ) draftBlockByHeight (height uint64 ) * block.Block {
627+ for blk := bc .bbf .Block (bc .head .HashBlock ()); blk != nil && blk .Height () > height ; blk = bc .bbf .Block (blk .PrevHash ()) {
628+ if blk .Height () == height {
629+ return blk
630+ }
631+ }
632+ return nil
633+ }
634+
635+ func (bc * blockchain ) blockByHeight (height uint64 ) (* block.Block , error ) {
636+ daoHeight , err := bc .dao .Height ()
637+ if err != nil {
638+ return nil , err
639+ }
640+ if height > daoHeight {
641+ if blk := bc .draftBlockByHeight (height ); blk != nil {
642+ return blk , nil
643+ }
644+ return nil , errors .Wrapf (db .ErrNotExist , "block %d not found" , height )
645+ }
646+ return bc .dao .GetBlockByHeight (height )
647+ }
0 commit comments