diff --git a/common/constant/smith.go b/common/constant/smith.go index 263c15a63..9f2a44406 100644 --- a/common/constant/smith.go +++ b/common/constant/smith.go @@ -24,4 +24,7 @@ var ( MainChainSmithIdlePeriod = 500 * time.Millisecond // MainChainSmithingPeriod one main block every 15 seconds + block pool delay (max +30 seconds) MainChainSmithingPeriod = int64(15) + // EmptyBlockSkippedBlocksmithLimit state the number of allowed skipped blocksmith until only empty block can be generated + // 0 will set node to always create empty block + EmptyBlockSkippedBlocksmithLimit = int64(2) ) diff --git a/core/service/blockCoreService.go b/core/service/blockCoreService.go index 71b719e2a..2ad0ef304 100644 --- a/core/service/blockCoreService.go +++ b/core/service/blockCoreService.go @@ -20,6 +20,7 @@ type ( previousBlock *model.Block, secretPhrase string, timestamp int64, + empty bool, ) (*model.Block, error) ValidateBlock(block, previousLastBlock *model.Block, curTime int64) error ValidatePayloadHash(block *model.Block) error @@ -54,6 +55,6 @@ type ( WillSmith( blocksmith *model.Blocksmith, blockchainProcessorLastBlockID int64, - ) (int64, error) + ) (lastBlockID, blocksmithIndex int64, err error) } ) diff --git a/core/service/blockMainService.go b/core/service/blockMainService.go index e388bb95a..c64819284 100644 --- a/core/service/blockMainService.go +++ b/core/service/blockMainService.go @@ -985,11 +985,12 @@ func (bs *BlockService) GetPayloadHashAndLength(block *model.Block) (payloadHash return } -// GenerateBlock generate block from transactions in mempool +// GenerateBlock generate block from transactions in mempool, pass empty flag to generate an empty block func (bs *BlockService) GenerateBlock( previousBlock *model.Block, secretPhrase string, timestamp int64, + empty bool, ) (*model.Block, error) { var ( totalAmount, totalFee, totalCoinbase int64 @@ -1003,19 +1004,22 @@ func (bs *BlockService) GenerateBlock( newBlockHeight := previousBlock.Height + 1 // calculate total coinbase to be added to the block totalCoinbase = bs.CoinbaseService.GetCoinbase() - sortedTransactions, err = bs.MempoolService.SelectTransactionsFromMempool(timestamp) - if err != nil { - return nil, errors.New("MempoolReadError") - } - // select transactions from mempool to be added to the block - for _, tx := range sortedTransactions { - txType, errType := bs.ActionTypeSwitcher.GetTransactionType(tx) - if errType != nil { - return nil, err + if !empty { + sortedTransactions, err = bs.MempoolService.SelectTransactionsFromMempool(timestamp) + if err != nil { + return nil, errors.New("MempoolReadError") + } + // select transactions from mempool to be added to the block + for _, tx := range sortedTransactions { + txType, errType := bs.ActionTypeSwitcher.GetTransactionType(tx) + if errType != nil { + return nil, err + } + totalAmount += txType.GetAmount() + totalFee += tx.Fee } - totalAmount += txType.GetAmount() - totalFee += tx.Fee } + // select published receipts to be added to the block publishedReceipts, err = bs.ReceiptService.SelectReceipts( timestamp, bs.ReceiptUtil.GetNumberOfMaxReceipts( @@ -1445,11 +1449,11 @@ func (bs *BlockService) PopOffToBlock(commonBlock *model.Block) ([]*model.Block, func (bs *BlockService) WillSmith( blocksmith *model.Blocksmith, blockchainProcessorLastBlockID int64, -) (int64, error) { +) (lastBlockID, blocksmithIndex int64, err error) { var blocksmithScore int64 lastBlock, err := bs.GetLastBlock() if err != nil { - return blockchainProcessorLastBlockID, blocker.NewBlocker( + return blockchainProcessorLastBlockID, blocksmithIndex, blocker.NewBlocker( blocker.SmithingErr, "genesis block has not been applied") } @@ -1460,7 +1464,7 @@ func (bs *BlockService) WillSmith( // check if eligible to create block in this round blocksmithsMap := bs.BlocksmithStrategy.GetSortedBlocksmithsMap(lastBlock) if blocksmithsMap[string(blocksmith.NodePublicKey)] == nil { - return blockchainProcessorLastBlockID, + return blockchainProcessorLastBlockID, blocksmithIndex, blocker.NewBlocker(blocker.SmithingErr, "BlocksmithNotInBlocksmithList") } // calculate blocksmith score for the block type @@ -1476,7 +1480,7 @@ func (bs *BlockService) WillSmith( // no negative scores allowed blocksmithScore = 0 bs.Logger.Errorf("Participation score calculation: %s", err) - return 0, blocker.NewBlocker(blocker.ZeroParticipationScoreErr, "participation score = 0") + return 0, 0, blocker.NewBlocker(blocker.ZeroParticipationScoreErr, "participation score = 0") } err = bs.BlocksmithStrategy.CalculateSmith( lastBlock, @@ -1485,25 +1489,26 @@ func (bs *BlockService) WillSmith( blocksmithScore, ) if err != nil { - return blockchainProcessorLastBlockID, err + return blockchainProcessorLastBlockID, blocksmithIndex, err } monitoring.SetBlockchainSmithTime(bs.GetChainType(), blocksmith.SmithTime-lastBlock.Timestamp) } // check for block pool duplicate blocksmithsMap := bs.BlocksmithStrategy.GetSortedBlocksmithsMap(lastBlock) - blocksmithIndex, ok := blocksmithsMap[string(blocksmith.NodePublicKey)] + blocksmithIdxPtr, ok := blocksmithsMap[string(blocksmith.NodePublicKey)] if !ok { - return blockchainProcessorLastBlockID, blocker.NewBlocker( + return blockchainProcessorLastBlockID, blocksmithIndex, blocker.NewBlocker( blocker.BlockErr, "BlocksmithNotInSmithingList", ) } - blockPool := bs.BlockPoolService.GetBlock(*blocksmithIndex) + blocksmithIndex = *blocksmithIdxPtr + blockPool := bs.BlockPoolService.GetBlock(blocksmithIndex) if blockPool != nil { - return blockchainProcessorLastBlockID, blocker.NewBlocker( + return blockchainProcessorLastBlockID, blocksmithIndex, blocker.NewBlocker( blocker.BlockErr, "DuplicateBlockPool", ) } - return blockchainProcessorLastBlockID, nil + return blockchainProcessorLastBlockID, blocksmithIndex, nil } // ProcessCompletedBlock to process block that already having all needed transactions diff --git a/core/service/blockMainService_test.go b/core/service/blockMainService_test.go index 973bb1cd0..21740249a 100644 --- a/core/service/blockMainService_test.go +++ b/core/service/blockMainService_test.go @@ -1705,6 +1705,7 @@ func TestBlockService_GenerateBlock(t *testing.T) { secretPhrase string timestamp int64 blockSmithAccountAddress string + empty bool } tests := []struct { name string @@ -1740,6 +1741,7 @@ func TestBlockService_GenerateBlock(t *testing.T) { secretPhrase: "phasepress", timestamp: 12344587645, blockSmithAccountAddress: "BCZ", + empty: false, }, wantErr: true, }, @@ -1778,6 +1780,7 @@ func TestBlockService_GenerateBlock(t *testing.T) { }, secretPhrase: "", timestamp: 12345678, + empty: false, }, wantErr: false, }, @@ -1802,6 +1805,7 @@ func TestBlockService_GenerateBlock(t *testing.T) { tt.args.previousBlock, tt.args.secretPhrase, tt.args.timestamp, + tt.args.empty, ) if (err != nil) != tt.wantErr { t.Errorf("BlockService.GenerateBlock() error = %v, wantErr %v", err, tt.wantErr) diff --git a/core/service/blockSpineService.go b/core/service/blockSpineService.go index 7a60d2c3f..216ce24e3 100644 --- a/core/service/blockSpineService.go +++ b/core/service/blockSpineService.go @@ -530,6 +530,7 @@ func (bs *BlockSpineService) GenerateBlock( previousBlock *model.Block, secretPhrase string, timestamp int64, + _ bool, ) (*model.Block, error) { var ( spinePublicKeys []*model.SpinePublicKey @@ -877,10 +878,10 @@ func (bs *BlockSpineService) BlockTransactionsRequestedListener() observer.Liste func (bs *BlockSpineService) WillSmith( blocksmith *model.Blocksmith, blockchainProcessorLastBlockID int64, -) (int64, error) { +) (lastBlockId, blocksmithIndex int64, err error) { lastBlock, err := bs.GetLastBlock() if err != nil { - return blockchainProcessorLastBlockID, blocker.NewBlocker( + return blockchainProcessorLastBlockID, blocksmithIndex, blocker.NewBlocker( blocker.SmithingErr, "genesis block has not been applied") } // caching: only calculate smith time once per new block @@ -891,7 +892,7 @@ func (bs *BlockSpineService) WillSmith( // check if eligible to create block in this round blocksmithsMap := blockSmithStrategy.GetSortedBlocksmithsMap(lastBlock) if blocksmithsMap[string(blocksmith.NodePublicKey)] == nil { - return blockchainProcessorLastBlockID, + return blockchainProcessorLastBlockID, blocksmithIndex, blocker.NewBlocker(blocker.SmithingErr, "BlocksmithNotInBlocksmithList") } // calculate blocksmith score for the block type @@ -904,11 +905,11 @@ func (bs *BlockSpineService) WillSmith( blocksmithScore, ) if err != nil { - return blockchainProcessorLastBlockID, err + return blockchainProcessorLastBlockID, blocksmithIndex, err } monitoring.SetBlockchainSmithTime(bs.GetChainType(), blocksmith.SmithTime-lastBlock.Timestamp) } - return blockchainProcessorLastBlockID, nil + return blockchainProcessorLastBlockID, blocksmithIndex, nil } func (bs *BlockSpineService) ValidateSpineBlockManifest(spineBlockManifest *model.SpineBlockManifest) error { diff --git a/core/service/blockSpineService_test.go b/core/service/blockSpineService_test.go index 79cf743b0..e762b1666 100644 --- a/core/service/blockSpineService_test.go +++ b/core/service/blockSpineService_test.go @@ -1410,6 +1410,7 @@ func TestBlockSpineService_GenerateBlock(t *testing.T) { previousBlock *model.Block secretPhrase string timestamp int64 + empty bool } tests := []struct { name string @@ -1492,6 +1493,7 @@ func TestBlockSpineService_GenerateBlock(t *testing.T) { tt.args.previousBlock, tt.args.secretPhrase, tt.args.timestamp, + tt.args.empty, ) if (err != nil) != tt.wantErr { t.Errorf("BlockSpineService.GenerateBlock() error = %v, wantErr %v", err, tt.wantErr) diff --git a/core/smith/blockchainProcessor.go b/core/smith/blockchainProcessor.go index ccd6a583a..950f68687 100644 --- a/core/smith/blockchainProcessor.go +++ b/core/smith/blockchainProcessor.go @@ -104,6 +104,7 @@ func (bp *BlockchainProcessor) FakeSmithing(numberOfBlocks int, fromGenesis bool previousBlock, bp.Generator.SecretPhrase, timeNow, + true, ) if err != nil { return err @@ -129,6 +130,7 @@ func (bp *BlockchainProcessor) StartSmithing() error { bp.BlockService.ChainWriteLock(constant.BlockchainStatusGeneratingBlock) defer bp.BlockService.ChainWriteUnlock(constant.BlockchainStatusGeneratingBlock) + var blocksmithIndex int64 lastBlock, err := bp.BlockService.GetLastBlock() if err != nil { return blocker.NewBlocker( @@ -136,7 +138,7 @@ func (bp *BlockchainProcessor) StartSmithing() error { } // todo: move this piece of code to service layer // caching: only calculate smith time once per new block - bp.LastBlockID, err = bp.BlockService.WillSmith( + bp.LastBlockID, blocksmithIndex, err = bp.BlockService.WillSmith( bp.Generator, bp.LastBlockID, ) if err != nil { @@ -150,6 +152,7 @@ func (bp *BlockchainProcessor) StartSmithing() error { lastBlock, bp.Generator.SecretPhrase, timestamp, + blocksmithIndex >= constant.EmptyBlockSkippedBlocksmithLimit, ) if err != nil { return err diff --git a/core/smith/strategy/blocksmithStrategyMain.go b/core/smith/strategy/blocksmithStrategyMain.go index 3fb5f0f2c..eb2a7a37b 100644 --- a/core/smith/strategy/blocksmithStrategyMain.go +++ b/core/smith/strategy/blocksmithStrategyMain.go @@ -104,6 +104,7 @@ func (bss *BlocksmithStrategyMain) GetSortedBlocksmithsMap(block *model.Block) m return result } +// SortBlocksmiths sort the list of active node of current block height, index start from 0. func (bss *BlocksmithStrategyMain) SortBlocksmiths(block *model.Block, withLock bool) { if block.ID == bss.LastSortedBlockID && block.ID != constant.MainchainGenesisBlockID { return