Skip to content

709 Smithing All Blocksmith Skipped #725

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 15 commits into from
Apr 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions cmd/block/blockGenerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ var (
Use: "fake-blocks",
Short: "fake-blocks command used to create fake blocks",
Run: func(cmd *cobra.Command, args []string) {
generateBlocks(numberOfBlocks, blocksmithSecretPhrase, outputPath)
generateBlocks(numberOfBlocks, blocksmithSecretPhrase, outputPath, chainType)
},
}
)
Expand Down Expand Up @@ -200,7 +200,9 @@ func initialize(
migration = database.Migration{Query: queryExecutor}
}

func generateBlocks(numberOfBlocks int, blocksmithSecretPhrase, outputPath string) {
// generateBlocks used to generate dummy block for testing
// note: now only support mainchain, will implement spinechain implementation details later.
func generateBlocks(numberOfBlocks int, blocksmithSecretPhrase, outputPath string, ct chaintype.ChainType) {
fmt.Println("initializing dependency and database...")
initialize(blocksmithSecretPhrase, outputPath)
fmt.Println("done initializing database")
Expand Down Expand Up @@ -235,13 +237,13 @@ func generateBlocks(numberOfBlocks int, blocksmithSecretPhrase, outputPath strin
}

fmt.Println("begin generating blocks")
if err := blockProcessor.FakeSmithing(numberOfBlocks, true); err != nil {
if err := blockProcessor.FakeSmithing(numberOfBlocks, true, ct); err != nil {
panic(err)
}
} else {
// start from last block's timestamp
fmt.Println("continuing from last database...")
if err := blockProcessor.FakeSmithing(numberOfBlocks, false); err != nil {
if err := blockProcessor.FakeSmithing(numberOfBlocks, false, ct); err != nil {
panic("error in appending block to existing database")
}
}
Expand Down
1 change: 1 addition & 0 deletions common/blocker/blocker.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ var (
ZeroParticipationScoreErr TypeBlocker = "ZeroParticipationScoreErr"
ChainValidationErr TypeBlocker = "ChainValidationErr"
P2PNetworkConnectionErr TypeBlocker = "P2PNetworkConnectionErr"
SmithingPending TypeBlocker = "SmithingPending"
TimeoutExceeded TypeBlocker = "TimeoutExceeded"
)

Expand Down
8 changes: 7 additions & 1 deletion common/chaintype/chainType.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ type (
GetTypeInt() int32
// GetTablePrefix return the value of current chain table prefix in the database
GetTablePrefix() string
// GetSmithingPeriod return the value of smithing period we want
// GetSmithingPeriod the time since last block that blocksmith can start to smith
GetSmithingPeriod() int64
// GetBlocksmithTimeGap return the time gap one to the next blocksmith
GetBlocksmithTimeGap() int64
// GetBlocksmithBlockCreationTime return the maximum allowed time to create block
GetBlocksmithBlockCreationTime() int64
// GetBlocksmithNetworkTolerance return the maximum allowed time to broadcast block
GetBlocksmithNetworkTolerance() int64
// GetName return the name of the chain : used in parsing chaintype across node
GetName() string
// GetGenesisBlockID return the block ID of genesis block in the chain
Expand Down
15 changes: 14 additions & 1 deletion common/chaintype/mainchain.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package chaintype

import (
"github.com/zoobc/zoobc-core/common/constant"
"time"

"github.com/zoobc/zoobc-core/common/constant"
)

// MainChain is struct should has methods in below
Expand All @@ -22,6 +23,18 @@ func (*MainChain) GetSmithingPeriod() int64 {
return constant.MainChainSmithingPeriod
}

func (*MainChain) GetBlocksmithTimeGap() int64 {
return constant.MainSmithingBlocksmithTimeGap
}

func (*MainChain) GetBlocksmithBlockCreationTime() int64 {
return constant.MainSmithingBlockCreationTime
}

func (*MainChain) GetBlocksmithNetworkTolerance() int64 {
return constant.MainSmithingNetworkTolerance
}

// GetName return the name of the chain : used in parsing chaintype across node
func (*MainChain) GetName() string {
return "Mainchain"
Expand Down
15 changes: 14 additions & 1 deletion common/chaintype/spinechain.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package chaintype

import (
"github.com/zoobc/zoobc-core/common/constant"
"time"

"github.com/zoobc/zoobc-core/common/constant"
)

// SpineChain is struct should has methods in below
Expand All @@ -22,6 +23,18 @@ func (*SpineChain) GetSmithingPeriod() int64 {
return constant.SpineChainSmithingPeriod
}

func (*SpineChain) GetBlocksmithTimeGap() int64 {
return constant.SpineSmithingBlocksmithTimeGap
}

func (*SpineChain) GetBlocksmithBlockCreationTime() int64 {
return constant.SpineSmithingBlockCreationTime
}

func (*SpineChain) GetBlocksmithNetworkTolerance() int64 {
return constant.SpineSmithingNetworkTolerance
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this can be unified in one constant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried that we might need a longer network tolerance time for spine block since it is of larger size. is that a possibility?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok then :)

}

// GetName return the name of the chain : used in parsing chaintype across node
func (*SpineChain) GetName() string {
return "Spinechain"
Expand Down
11 changes: 8 additions & 3 deletions common/constant/smith.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ import (
var (
MaxNumBlocksmithRewards = 5
GenerateBlockTimeoutSec = int64(15)
SmithingBlockCreationTime = int64(30)
SmithingNetworkTolerance = int64(15)
SmithingBlocksmithTimeGap = int64(10)
CumulativeDifficultyDivisor = int64(1000000)
// BlockPoolScanPeriod define the periodic time to scan the whole block pool for legal block to persist to the chain
BlockPoolScanPeriod = 5 * time.Second
Expand All @@ -27,4 +24,12 @@ var (
// 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)
// Mainchain smithing
MainSmithingBlockCreationTime = int64(30)
MainSmithingNetworkTolerance = int64(15)
MainSmithingBlocksmithTimeGap = int64(10)
// Spinechain smithing
SpineSmithingBlockCreationTime = int64(30)
SpineSmithingNetworkTolerance = int64(15)
SpineSmithingBlocksmithTimeGap = int64(10)
)
1 change: 0 additions & 1 deletion common/model/blocksmith.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ type Blocksmith struct {
NodeOrder *big.Int
SmithOrder uint32
Score *big.Int
SmithTime int64
BlockSeed int64
SecretPhrase string
Deadline uint32
Expand Down
10 changes: 5 additions & 5 deletions common/monitoring/metricsMonitoring.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ var (
blockerCounterVector *prometheus.CounterVec
statusLockGaugeVector *prometheus.GaugeVec
blockchainStatusGaugeVector *prometheus.GaugeVec
blockchainSmithTimeGaugeVector *prometheus.GaugeVec
blockchainSmithIndexGaugeVector *prometheus.GaugeVec
blockchainIDMsbGaugeVector *prometheus.GaugeVec
blockchainIDLsbGaugeVector *prometheus.GaugeVec
blockchainHeightGaugeVector *prometheus.GaugeVec
Expand Down Expand Up @@ -121,11 +121,11 @@ func SetMonitoringActive(isActive bool) {
}, []string{"chaintype"})
prometheus.MustRegister(blockchainStatusGaugeVector)

blockchainSmithTimeGaugeVector = prometheus.NewGaugeVec(prometheus.GaugeOpts{
blockchainSmithIndexGaugeVector = prometheus.NewGaugeVec(prometheus.GaugeOpts{
Name: "zoobc_blockchain_smith_time",
Help: "Smith time of each nodes to smith for each chain",
}, []string{"chaintype"})
prometheus.MustRegister(blockchainSmithTimeGaugeVector)
prometheus.MustRegister(blockchainSmithIndexGaugeVector)

nodeScore = prometheus.NewGauge(prometheus.GaugeOpts{
Name: "zoobc_node_score",
Expand Down Expand Up @@ -277,12 +277,12 @@ func SetBlockchainStatus(chainType chaintype.ChainType, newStatus int) {
blockchainStatusGaugeVector.WithLabelValues(chainType.GetName()).Set(float64(newStatus))
}

func SetBlockchainSmithTime(chainType chaintype.ChainType, newTime int64) {
func SetBlockchainSmithIndex(chainType chaintype.ChainType, index int64) {
if !isMonitoringActive {
return
}

blockchainSmithTimeGaugeVector.WithLabelValues(chainType.GetName()).Set(float64(newTime))
blockchainSmithIndexGaugeVector.WithLabelValues(chainType.GetName()).Set(float64(index))
}

func SetNodeScore(activeBlocksmiths []*model.Blocksmith) {
Expand Down
88 changes: 20 additions & 68 deletions core/service/blockMainService.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,8 @@ func (bs *BlockService) PreValidateBlock(block, previousLastBlock *model.Block)
return blocker.NewBlocker(blocker.BlockErr, "InvalidBlocksmith")
}
// check smithtime
blocksmithTime := bs.BlocksmithStrategy.GetSmithTime(*blocksmithIndex, previousLastBlock)
if blocksmithTime > block.GetTimestamp() {
err := bs.BlocksmithStrategy.IsValidSmithTime(*blocksmithIndex, int64(len(blocksmithsMap)), previousLastBlock)
if err != nil {
return blocker.NewBlocker(blocker.BlockErr, "InvalidSmithTime")
}
return nil
Expand Down Expand Up @@ -597,7 +597,8 @@ func (bs *BlockService) PushBlock(previousBlock, block *model.Block, broadcast,
// handle if is first index
if *blocksmithIndex > 0 {
// check if current block is in pushable window
if !bs.canPersistBlock(*blocksmithIndex, previousBlock) {
err = bs.BlocksmithStrategy.CanPersistBlock(*blocksmithIndex, int64(len(blocksmithsMap)), previousBlock)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

if err != nil {
// insert into block pool
bs.BlockPoolService.InsertBlock(block, *blocksmithIndex)
if rollbackErr := bs.QueryExecutor.RollbackTx(); rollbackErr != nil {
Expand Down Expand Up @@ -643,8 +644,10 @@ func (bs *BlockService) ScanBlockPool() error {
return err
}
blocks := bs.BlockPoolService.GetBlocks()
blocksmithsMap := bs.BlocksmithStrategy.GetSortedBlocksmiths(previousBlock)
for index, block := range blocks {
if bs.canPersistBlock(index, previousBlock) {
err = bs.BlocksmithStrategy.CanPersistBlock(index, int64(len(blocksmithsMap)), previousBlock)
if err == nil {
err := func(block *model.Block) error {
bs.ChainWriteLock(constant.BlockchainStatusReceivingBlock)
defer bs.ChainWriteUnlock(constant.BlockchainStatusReceivingBlock)
Expand All @@ -659,64 +662,10 @@ func (bs *BlockService) ScanBlockPool() error {
break
}
}
// check if we have passed the last blocksmith expiry time
numberOfBlocksmiths := len(bs.BlocksmithStrategy.GetSortedBlocksmithsMap(previousBlock))
lastBlocksmithExpiry := bs.BlocksmithStrategy.GetSmithTime(int64(numberOfBlocksmiths-1), previousBlock) +
constant.SmithingBlockCreationTime + constant.SmithingNetworkTolerance
if len(blocks) > 0 &&
time.Now().Unix() > lastBlocksmithExpiry {
// choose block to persist since all blocksmith time has expired
var (
bestIndex int64
bestCumDiff = new(big.Int).SetInt64(0)
)
for index, block := range blocks {
currBlockCumDiff, _ := new(big.Int).SetString(block.GetCumulativeDifficulty(), 10)
if currBlockCumDiff.Cmp(bestCumDiff) > 0 {
bestCumDiff = currBlockCumDiff
bestIndex = index
}
}
err := func() error {
bs.ChainWriteLock(constant.BlockchainStatusReceivingBlock)
defer bs.ChainWriteUnlock(constant.BlockchainStatusReceivingBlock)
err := bs.PushBlock(previousBlock, blocks[bestIndex], true, true)
return err
}()
if err != nil {
return blocker.NewBlocker(
blocker.BlockErr, "ScanBlockPool:PushBlockFail",
)
}
}
return nil
}

// canPersistBlock check if the blocksmith can push the block based on previous block's blocksmiths order
// this function must only run when receiving / generating block, not on download block since it uses the current machine
// time as comparison
// todo: will move this to block pool service + write the test when refactoring the block service
func (bs *BlockService) canPersistBlock(blocksmithIndex int64, previousBlock *model.Block) bool {
if blocksmithIndex < 1 {
return true
}
var (
currentTime = time.Now().Unix()
)
blocksmithAllowedBeginTime := bs.BlocksmithStrategy.GetSmithTime(blocksmithIndex, previousBlock)
blocksmithExpiredPersistTime := blocksmithAllowedBeginTime +
constant.SmithingBlockCreationTime + constant.SmithingNetworkTolerance
previousBlocksmithAllowedBeginTime := blocksmithAllowedBeginTime - constant.SmithingBlocksmithTimeGap
blocksmithAllowedPersistTime := previousBlocksmithAllowedBeginTime +
constant.SmithingBlockCreationTime + constant.SmithingNetworkTolerance
// allowed time window = lastBlocksmithExpiredTime < current_time <= currentBlocksmithExpiredTime
if previousBlock.GetHeight() == 0 {
return currentTime > blocksmithAllowedPersistTime
}
return currentTime >= blocksmithAllowedPersistTime && currentTime <= blocksmithExpiredPersistTime
}

// adminNodes seelct and admit nodes from node registry
// adminNodes select and admit nodes from node registry
func (bs *BlockService) admitNodes(block *model.Block) error {
// select n (= MaxNodeAdmittancePerCycle) queued nodes with the highest locked balance from node registry
nodeRegistrations, err := bs.NodeRegistrationService.SelectNodesToBeAdmitted(constant.MaxNodeAdmittancePerCycle)
Expand Down Expand Up @@ -1463,7 +1412,8 @@ func (bs *BlockService) WillSmith(
bs.BlocksmithStrategy.SortBlocksmiths(lastBlock, true)
// check if eligible to create block in this round
blocksmithsMap := bs.BlocksmithStrategy.GetSortedBlocksmithsMap(lastBlock)
if blocksmithsMap[string(blocksmith.NodePublicKey)] == nil {
blocksmithIdx := blocksmithsMap[string(blocksmith.NodePublicKey)]
if blocksmithIdx == nil {
return blockchainProcessorLastBlockID, blocksmithIndex,
blocker.NewBlocker(blocker.SmithingErr, "BlocksmithNotInBlocksmithList")
}
Expand All @@ -1482,16 +1432,11 @@ func (bs *BlockService) WillSmith(
bs.Logger.Errorf("Participation score calculation: %s", err)
return 0, 0, blocker.NewBlocker(blocker.ZeroParticipationScoreErr, "participation score = 0")
}
err = bs.BlocksmithStrategy.CalculateSmith(
lastBlock,
*(blocksmithsMap[string(blocksmith.NodePublicKey)]),
blocksmith,
blocksmithScore,
)
err = bs.BlocksmithStrategy.CalculateScore(blocksmith, blocksmithScore)
if err != nil {
return blockchainProcessorLastBlockID, blocksmithIndex, err
}
monitoring.SetBlockchainSmithTime(bs.GetChainType(), blocksmith.SmithTime-lastBlock.Timestamp)
monitoring.SetBlockchainSmithIndex(bs.GetChainType(), *blocksmithIdx)
}
// check for block pool duplicate
blocksmithsMap := bs.BlocksmithStrategy.GetSortedBlocksmithsMap(lastBlock)
Expand All @@ -1508,7 +1453,14 @@ func (bs *BlockService) WillSmith(
blocker.BlockErr, "DuplicateBlockPool",
)
}
return blockchainProcessorLastBlockID, blocksmithIndex, nil
// check if it's legal to create block for current blocksmith now
err = bs.BlocksmithStrategy.IsValidSmithTime(blocksmithIndex, int64(len(blocksmithsMap)), lastBlock)
if err == nil {
return blockchainProcessorLastBlockID, blocksmithIndex, nil
}
return blockchainProcessorLastBlockID, blocksmithIndex, blocker.NewBlocker(
blocker.SmithingErr, "NotTimeToSmithYet",
)
}

// ProcessCompletedBlock to process block that already having all needed transactions
Expand Down
Loading