Skip to content

690 empty block smith #703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 30, 2020
3 changes: 3 additions & 0 deletions common/constant/smith.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
3 changes: 2 additions & 1 deletion core/service/blockCoreService.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -54,6 +55,6 @@ type (
WillSmith(
blocksmith *model.Blocksmith,
blockchainProcessorLastBlockID int64,
) (int64, error)
) (lastBlockID, blocksmithIndex int64, err error)
}
)
49 changes: 27 additions & 22 deletions core/service/blockMainService.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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")
}

Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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
Expand Down
4 changes: 4 additions & 0 deletions core/service/blockMainService_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1705,6 +1705,7 @@ func TestBlockService_GenerateBlock(t *testing.T) {
secretPhrase string
timestamp int64
blockSmithAccountAddress string
empty bool
}
tests := []struct {
name string
Expand Down Expand Up @@ -1740,6 +1741,7 @@ func TestBlockService_GenerateBlock(t *testing.T) {
secretPhrase: "phasepress",
timestamp: 12344587645,
blockSmithAccountAddress: "BCZ",
empty: false,
},
wantErr: true,
},
Expand Down Expand Up @@ -1778,6 +1780,7 @@ func TestBlockService_GenerateBlock(t *testing.T) {
},
secretPhrase: "",
timestamp: 12345678,
empty: false,
},
wantErr: false,
},
Expand All @@ -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)
Expand Down
11 changes: 6 additions & 5 deletions core/service/blockSpineService.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ func (bs *BlockSpineService) GenerateBlock(
previousBlock *model.Block,
secretPhrase string,
timestamp int64,
_ bool,
) (*model.Block, error) {
var (
spinePublicKeys []*model.SpinePublicKey
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions core/service/blockSpineService_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,7 @@ func TestBlockSpineService_GenerateBlock(t *testing.T) {
previousBlock *model.Block
secretPhrase string
timestamp int64
empty bool
}
tests := []struct {
name string
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion core/smith/blockchainProcessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func (bp *BlockchainProcessor) FakeSmithing(numberOfBlocks int, fromGenesis bool
previousBlock,
bp.Generator.SecretPhrase,
timeNow,
true,
)
if err != nil {
return err
Expand All @@ -129,14 +130,15 @@ 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(
blocker.SmithingErr, "genesis block has not been applied")
}
// 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 {
Expand All @@ -150,6 +152,7 @@ func (bp *BlockchainProcessor) StartSmithing() error {
lastBlock,
bp.Generator.SecretPhrase,
timestamp,
blocksmithIndex >= constant.EmptyBlockSkippedBlocksmithLimit,
)
if err != nil {
return err
Expand Down
1 change: 1 addition & 0 deletions core/smith/strategy/blocksmithStrategyMain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down