Skip to content

Commit f64c120

Browse files
committed
fork blockchain
1 parent ba5f15e commit f64c120

File tree

19 files changed

+602
-95
lines changed

19 files changed

+602
-95
lines changed

action/protocol/execution/protocol_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ func (sct *SmartContractTest) prepareBlockchain(
478478
cfg.Chain,
479479
cfg.Genesis,
480480
dao,
481-
factory.NewMinter(sf, ap),
481+
factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())),
482482
blockchain.BlockValidatorOption(block.NewValidator(
483483
sf,
484484
protocol.NewGenericValidator(sf, accountutil.AccountState),
@@ -730,7 +730,7 @@ func TestProtocol_Handle(t *testing.T) {
730730
cfg.Chain,
731731
cfg.Genesis,
732732
dao,
733-
factory.NewMinter(sf, ap),
733+
factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())),
734734
blockchain.BlockValidatorOption(block.NewValidator(
735735
sf,
736736
protocol.NewGenericValidator(sf, accountutil.AccountState),

api/serverV2_integrity_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ func setupChain(cfg testConfig) (blockchain.Blockchain, blockdao.BlockDAO, block
325325
cfg.chain,
326326
cfg.genesis,
327327
dao,
328-
factory.NewMinter(sf, ap),
328+
factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.chain.ProducerPrivateKey())),
329329
blockchain.BlockValidatorOption(block.NewValidator(
330330
sf,
331331
protocol.NewGenericValidator(sf, accountutil.AccountState),

blockchain/blockchain.go

Lines changed: 137 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -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"
@@ -98,6 +98,8 @@ type (
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

251271
func (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

255288
func (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
260293
func (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
273301
func (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

361392
func (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

410444
func (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
448474
func (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
506579
func (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+
}

blockchain/integrity/benchmark_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ func newChainInDB() (blockchain.Blockchain, actpool.ActPool, error) {
273273
cfg.Chain,
274274
cfg.Genesis,
275275
dao,
276-
factory.NewMinter(sf, ap),
276+
factory.NewMinter(sf, ap, factory.WithPrivateKeyOption(cfg.Chain.ProducerPrivateKey())),
277277
blockchain.BlockValidatorOption(block.NewValidator(
278278
sf,
279279
protocol.NewGenericValidator(sf, accountutil.AccountState),

0 commit comments

Comments
 (0)