}
// SaveChainStatus save the core's newest status && delete old status
-func (s *Store) SaveChainStatus(node, irreversibleNode *state.BlockNode, view *state.UtxoViewpoint, voteMap map[uint64]*state.VoteResult) error {
+func (s *Store) SaveChainStatus(node, irreversibleNode *state.BlockNode, view *state.UtxoViewpoint, voteResults []*state.VoteResult) error {
batch := s.db.NewBatch()
if err := saveUtxoView(batch, view); err != nil {
return err
}
- if err := saveVoteResult(batch, voteMap); err != nil {
+ if err := saveVoteResult(batch, voteResults); err != nil {
return err
}
}
// saveVoteResult update the voting results generated by each irreversible block
-func saveVoteResult(batch dbm.Batch, voteMap map[uint64]*state.VoteResult) error {
- for _, vote := range voteMap {
+func saveVoteResult(batch dbm.Batch, voteResults []*state.VoteResult) error {
+ for _, vote := range voteResults {
bytes, err := json.Marshal(vote)
if err != nil {
return err
},
}
- if err := store.SaveChainStatus(node, node, view, map[uint64]*state.VoteResult{}); err != nil {
+ if err := store.SaveChainStatus(node, node, view, []*state.VoteResult{}); err != nil {
t.Fatal(err)
}
nextBlockTime = minNextBlockTime
}
- if isBlocker, err := b.chain.IsBlocker(&bestBlockHash, xpubStr, nextBlockTime); !isBlocker {
- log.WithFields(log.Fields{"module": logModule, "error": err, "pubKey": xpubStr}).Debug("fail on check is next blocker")
+ isBlocker, err := b.chain.IsBlocker(&bestBlockHash, xpubStr, nextBlockTime)
+ if err != nil {
+ log.WithFields(log.Fields{"module": logModule, "error": err, "pubKey": xpubStr}).Error("fail on check is next blocker")
+ continue
+ }
+
+ if !isBlocker {
continue
}
}
func (c *Chain) isIrreversible(blockNode *state.BlockNode) bool {
- consensusNodes, err := c.consensusNodeManager.getConsensusNodesByVoteResult(&blockNode.Parent.Hash)
+ consensusNodes, err := c.consensusNodeManager.getConsensusNodes(&blockNode.Parent.Hash)
if err != nil {
return false
}
// if some signature is invalid, they will be reset to nil
// if the block has not the signature of blocker, it will return error
func (c *Chain) validateSign(block *types.Block) (uint64, error) {
- consensusNodeMap, err := c.consensusNodeManager.getConsensusNodesByVoteResult(&block.PreviousBlockHash)
+ consensusNodeMap, err := c.consensusNodeManager.getConsensusNodes(&block.PreviousBlockHash)
if err != nil {
return 0, err
}
return err
}
- voteResultMap := make(map[uint64]*state.VoteResult)
- if err := c.consensusNodeManager.applyBlock(voteResultMap, block); err != nil {
+ voteResult, err := c.consensusNodeManager.getBestVoteResult()
+ if err != nil {
+ return err
+ }
+ if err := voteResult.ApplyBlock(block); err != nil {
return err
}
irreversibleNode = node
}
- if err := c.setState(node, irreversibleNode, utxoView, voteResultMap); err != nil {
+ if err := c.setState(node, irreversibleNode, utxoView, []*state.VoteResult{voteResult}); err != nil {
return err
}
func (c *Chain) reorganizeChain(node *state.BlockNode) error {
attachNodes, detachNodes := c.calcReorganizeNodes(node)
utxoView := state.NewUtxoViewpoint()
- voteResultMap := make(map[uint64]*state.VoteResult)
+ voteResults := []*state.VoteResult{}
irreversibleNode := c.bestIrreversibleNode
+ voteResult, err := c.consensusNodeManager.getBestVoteResult()
+ if err != nil {
+ return err
+ }
for _, detachNode := range detachNodes {
b, err := c.store.GetBlock(&detachNode.Hash)
return err
}
- if err := c.consensusNodeManager.detachBlock(voteResultMap, b); err != nil {
+ if err := voteResult.DetachBlock(b); err != nil {
return err
}
return err
}
- if err := c.consensusNodeManager.applyBlock(voteResultMap, b); err != nil {
+ if err := voteResult.ApplyBlock(b); err != nil {
return err
}
+ if voteResult.IsFinalize() {
+ voteResults = append(voteResults, voteResult.Fork())
+ }
+
if c.isIrreversible(attachNode) && b.Height > irreversibleNode.Height {
irreversibleNode = attachNode
}
log.WithFields(log.Fields{"module": logModule, "height": node.Height, "hash": node.Hash.String()}).Debug("attach from mainchain")
}
- return c.setState(node, irreversibleNode, utxoView, voteResultMap)
+ voteResults = append(voteResults, voteResult.Fork())
+ return c.setState(node, irreversibleNode, utxoView, voteResults)
}
// SaveBlock will validate and save block into storage
"github.com/vapor/consensus"
"github.com/vapor/errors"
"github.com/vapor/protocol/bc"
- "github.com/vapor/protocol/bc/types"
"github.com/vapor/protocol/state"
)
}
func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) {
- consensusNodeMap, err := c.getConsensusNodesByVoteResult(prevBlockHash)
+ consensusNodeMap, err := c.getConsensusNodes(prevBlockHash)
if err != nil {
return nil, err
}
}
func (c *consensusNodeManager) isBlocker(prevBlockHash *bc.Hash, pubKey string, timeStamp uint64) (bool, error) {
- consensusNodeMap, err := c.getConsensusNodesByVoteResult(prevBlockHash)
+ consensusNodeMap, err := c.getConsensusNodes(prevBlockHash)
if err != nil {
return false, err
}
return false, nil
}
- prevVoteRoundLastBlock, err := c.getPrevRoundVoteLastBlock(prevBlockHash)
+ prevVoteRoundLastBlock, err := c.getPrevRoundLastBlock(prevBlockHash)
if err != nil {
return false, err
}
startTimestamp := prevVoteRoundLastBlock.Timestamp + consensus.BlockTimeInterval
- begin := getLastBlockTimeInTimeRange(startTimestamp, timeStamp, consensusNode.Order, len(consensusNodeMap))
+ begin := getLastBlockTimeInTimeRange(startTimestamp, timeStamp, consensusNode.Order, uint64(len(consensusNodeMap)))
end := begin + consensus.BlockNumEachNode*consensus.BlockTimeInterval
return timeStamp >= begin && timeStamp < end, nil
}
-func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order uint64, numOfConsensusNode int) uint64 {
+func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order, numOfConsensusNode uint64) uint64 {
// One round of product block time for all consensus nodes
- roundBlockTime := uint64(consensus.BlockNumEachNode * numOfConsensusNode * consensus.BlockTimeInterval)
+ roundBlockTime := consensus.BlockNumEachNode * numOfConsensusNode * consensus.BlockTimeInterval
// The start time of the last round of product block
lastRoundStartTime := startTimestamp + (endTimestamp-startTimestamp)/roundBlockTime*roundBlockTime
// The time of product block of the consensus in last round
return lastRoundStartTime + order*(consensus.BlockNumEachNode*consensus.BlockTimeInterval)
}
-func (c *consensusNodeManager) getPrevRoundVoteLastBlock(prevBlockHash *bc.Hash) (*state.BlockNode, error) {
- prevBlockNode := c.blockIndex.GetNode(prevBlockHash)
- if prevBlockNode == nil {
+func (c *consensusNodeManager) getPrevRoundLastBlock(prevBlockHash *bc.Hash) (*state.BlockNode, error) {
+ node := c.blockIndex.GetNode(prevBlockHash)
+ if node == nil {
return nil, errNotFoundBlockNode
}
- blockHeight := prevBlockNode.Height + 1
-
- prevVoteRoundLastBlockHeight := blockHeight/consensus.RoundVoteBlockNums*consensus.RoundVoteBlockNums - 1
- // first round
- if blockHeight/consensus.RoundVoteBlockNums == 0 {
- prevVoteRoundLastBlockHeight = 0
+ for node.Height%consensus.RoundVoteBlockNums != 0 {
+ node = node.Parent
}
-
- lastBlockNode := prevBlockNode.GetParent(prevVoteRoundLastBlockHeight)
- if lastBlockNode == nil {
- return nil, errNotFoundBlockNode
- }
- return lastBlockNode, nil
+ return node, nil
}
-func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.Hash) (map[string]*state.ConsensusNode, error) {
+func (c *consensusNodeManager) getConsensusNodes(prevBlockHash *bc.Hash) (map[string]*state.ConsensusNode, error) {
prevBlockNode := c.blockIndex.GetNode(prevBlockHash)
if prevBlockNode == nil {
return nil, errNotFoundBlockNode
}
- seq := (prevBlockNode.Height + 1) / consensus.RoundVoteBlockNums
- voteResult, err := c.store.GetVoteResult(seq)
+ seqHeight := prevBlockNode.Height + 1
+ if bestHeight := c.blockIndex.BestNode().Height; bestHeight < seqHeight && bestHeight != 0 {
+ seqHeight = bestHeight
+ }
+
+ preSeq := state.CalcVoteSeq(seqHeight) - 1
+ voteResult, err := c.store.GetVoteResult(preSeq)
if err != nil {
- // TODO find previous round vote
- voteResult = &state.VoteResult{
- Seq: seq,
- NumOfVote: make(map[string]uint64),
- }
+ return nil, err
}
- lastBlockNode, err := c.getPrevRoundVoteLastBlock(prevBlockHash)
+ lastBlockNode, err := c.getPrevRoundLastBlock(prevBlockHash)
if err != nil {
return nil, err
}
}
if len(voteResult.NumOfVote) == 0 {
- return initConsensusNodes(), nil
+ return federationNodes(), nil
}
-
return voteResult.ConsensusNodes()
}
-func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, forkChainNode *state.BlockNode) error {
- genesisBlockHash := config.GenesisBlock().Hash()
- mainChainNode := c.blockIndex.GetNode(&genesisBlockHash)
+func (c *consensusNodeManager) getBestVoteResult() (*state.VoteResult, error) {
+ blockNode := c.blockIndex.BestNode()
+ seq := state.CalcVoteSeq(blockNode.Height)
+ voteResult, err := c.store.GetVoteResult(seq)
+ if err != nil {
+ return nil, err
+ }
- emptyHash := bc.Hash{}
- if voteResult.LastBlockHash != emptyHash {
- mainChainNode = c.blockIndex.GetNode(&voteResult.LastBlockHash)
- if mainChainNode == nil {
- return errNotFoundBlockNode
- }
+ if err := c.reorganizeVoteResult(voteResult, blockNode); err != nil {
+ return nil, err
}
+ return voteResult, nil
+}
+
+func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, node *state.BlockNode) error {
+ mainChainNode := c.blockIndex.GetNode(&voteResult.BlockHash)
var attachNodes []*state.BlockNode
var detachNodes []*state.BlockNode
-
- for forkChainNode != nil && mainChainNode != nil && forkChainNode.Hash != mainChainNode.Hash {
+ for forkChainNode := node; mainChainNode != forkChainNode; node = node.Parent {
if forkChainNode.Height == mainChainNode.Height {
detachNodes = append(detachNodes, mainChainNode)
mainChainNode = mainChainNode.Parent
}
attachNodes = append([]*state.BlockNode{forkChainNode}, attachNodes...)
- forkChainNode = forkChainNode.Parent
}
for _, node := range detachNodes {
return err
}
- if err := c.detachBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil {
+ if err := voteResult.DetachBlock(block); err != nil {
return err
}
}
return err
}
- if err := c.applyBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (c *consensusNodeManager) applyBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) (err error) {
- voteResult, err := c.getVoteResult(voteResultMap, block.Height)
- if err != nil {
- return err
- }
-
- return voteResult.ApplyBlock(block)
-}
-
-func (c *consensusNodeManager) getVoteResult(voteResultMap map[uint64]*state.VoteResult, blockHeight uint64) (*state.VoteResult, error) {
- var err error
- // This round of voting prepares for the next round
- seq := blockHeight/consensus.RoundVoteBlockNums + 1
- voteResult := voteResultMap[seq]
- if blockHeight == 0 {
- voteResult = &state.VoteResult{
- Seq: seq,
- NumOfVote: make(map[string]uint64),
- }
- }
-
- if voteResult == nil {
- prevVoteResult := voteResultMap[seq-1]
- if prevVoteResult != nil {
- voteResult = &state.VoteResult{
- Seq: seq,
- NumOfVote: prevVoteResult.NumOfVote,
- }
- }
- }
-
- if voteResult == nil {
- voteResult, err = c.store.GetVoteResult(seq)
- if err != nil && err != ErrNotFoundVoteResult {
- return nil, err
- }
- }
-
- if voteResult == nil {
- voteResult, err = c.store.GetVoteResult(seq - 1)
- if err != nil && err != ErrNotFoundVoteResult {
- return nil, err
- }
-
- if voteResult != nil {
- voteResult.Seq = seq
- voteResult.LastBlockHash = bc.Hash{}
- }
- }
-
- if voteResult == nil {
- return nil, errors.New("fail to get vote result")
- }
-
- voteResultMap[seq] = voteResult
- return voteResult, nil
-}
-
-func (c *consensusNodeManager) detachBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) error {
- voteSeq := block.Height / consensus.RoundVoteBlockNums
- voteResult := voteResultMap[voteSeq]
-
- if voteResult == nil {
- voteResult, err := c.store.GetVoteResult(voteSeq)
- if err != nil {
+ if err := voteResult.ApplyBlock(block); err != nil {
return err
}
- voteResultMap[voteSeq] = voteResult
}
-
- voteResult.DetachBlock(block)
return nil
}
-func initConsensusNodes() map[string]*state.ConsensusNode {
+func federationNodes() map[string]*state.ConsensusNode {
voteResult := map[string]*state.ConsensusNode{}
for i, xpub := range config.CommonConfig.Federation.Xpubs {
voteResult[xpub.String()] = &state.ConsensusNode{XPub: xpub, VoteNum: 0, Order: uint64(i)}
return err
}
- voteResultMap := make(map[uint64]*state.VoteResult)
- if err := c.consensusNodeManager.applyBlock(voteResultMap, genesisBlock); err != nil {
- return err
- }
-
+ voteResults := []*state.VoteResult{&state.VoteResult{
+ Seq: 0,
+ NumOfVote: map[string]uint64{},
+ BlockHash: genesisBlock.Hash(),
+ BlockHeight: 0,
+ }}
node, err := state.NewBlockNode(&genesisBlock.BlockHeader, nil)
if err != nil {
return err
}
- return c.store.SaveChainStatus(node, node, utxoView, voteResultMap)
+ return c.store.SaveChainStatus(node, node, utxoView, voteResults)
}
// BestBlockHeight returns the current height of the blockchain.
}
// This function must be called with mu lock in above level
-func (c *Chain) setState(node *state.BlockNode, irreversibleNode *state.BlockNode, view *state.UtxoViewpoint, voteMap map[uint64]*state.VoteResult) error {
- if err := c.store.SaveChainStatus(node, irreversibleNode, view, voteMap); err != nil {
+func (c *Chain) setState(node *state.BlockNode, irreversibleNode *state.BlockNode, view *state.UtxoViewpoint, voteResults []*state.VoteResult) error {
+ if err := c.store.SaveChainStatus(node, irreversibleNode, view, voteResults); err != nil {
return err
}
return timestamps[len(timestamps)/2]
}
-// GetParent return the node of specified height
-// And the node satisfies the same chain as current node
-// Height of current node must greater than height parameter
-func (node *BlockNode) GetParent(height uint64) *BlockNode {
- prevBlockNode := node
- for prevBlockNode != nil && prevBlockNode.Height != height {
- if prevBlockNode.Height < height {
- return nil
- }
- prevBlockNode = prevBlockNode.Parent
- }
- return prevBlockNode
-}
-
// BlockIndex is the struct for help chain trace block chain as tree
type BlockIndex struct {
sync.RWMutex
// NewBlockIndex will create a empty BlockIndex
func NewBlockIndex() *BlockIndex {
return &BlockIndex{
- index: make(map[bc.Hash]*BlockNode),
+ index: make(map[bc.Hash]*BlockNode),
heightIndex: make(map[uint64][]*BlockNode),
- mainChain: make([]*BlockNode, 0, approxNodesPerDay),
+ mainChain: make([]*BlockNode, 0, approxNodesPerDay),
}
}
}
func (c byVote) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
+// seq 0 is the genesis block
+// seq 1 is the the block height 1, to block height RoundVoteBlockNums
+// seq 2 is the block height RoundVoteBlockNums + 1 to block height 2 * RoundVoteBlockNums
+// consensus node of the current round is the final result of previous round
+func CalcVoteSeq(blockHeight uint64) uint64 {
+ if blockHeight == 0 {
+ return 0
+ }
+ return (blockHeight-1)/consensus.RoundVoteBlockNums + 1
+}
+
// VoteResult represents a snapshot of each round of DPOS voting
// Seq indicates the sequence of current votes, which start from zero
// NumOfVote indicates the number of votes each consensus node receives, the key of map represent public key
// Finalized indicates whether this vote is finalized
type VoteResult struct {
- Seq uint64
- NumOfVote map[string]uint64
- LastBlockHash bc.Hash
+ Seq uint64
+ NumOfVote map[string]uint64
+ BlockHash bc.Hash
+ BlockHeight uint64
}
func (v *VoteResult) ApplyBlock(block *types.Block) error {
- if v.LastBlockHash != block.PreviousBlockHash {
+ if v.BlockHash != block.PreviousBlockHash {
return errors.New("block parent hash is not equals last block hash of vote result")
}
}
}
- v.LastBlockHash = block.Hash()
+ v.BlockHash = block.Hash()
+ v.BlockHeight = block.Height
+ v.Seq = CalcVoteSeq(block.Height)
return nil
}
}
func (v *VoteResult) DetachBlock(block *types.Block) error {
- if v.LastBlockHash != block.Hash() {
+ if v.BlockHash != block.Hash() {
return errors.New("block hash is not equals last block hash of vote result")
}
}
}
- v.LastBlockHash = block.PreviousBlockHash
+ v.BlockHash = block.PreviousBlockHash
+ v.BlockHeight = block.Height - 1
+ v.Seq = CalcVoteSeq(block.Height - 1)
return nil
}
+
+func (v *VoteResult) Fork() *VoteResult {
+ f := &VoteResult{
+ Seq: v.Seq,
+ NumOfVote: map[string]uint64{},
+ BlockHash: v.BlockHash,
+ BlockHeight: v.BlockHeight,
+ }
+
+ for key, value := range v.NumOfVote {
+ f.NumOfVote[key] = value
+ }
+ return f
+}
+
+func (v *VoteResult) IsFinalize() bool {
+ return v.BlockHeight%consensus.RoundVoteBlockNums == 0
+}
LoadBlockIndex(uint64) (*state.BlockIndex, error)
SaveBlock(*types.Block, *bc.TransactionStatus) error
- SaveChainStatus(*state.BlockNode, *state.BlockNode, *state.UtxoViewpoint, map[uint64]*state.VoteResult) error
+ SaveChainStatus(*state.BlockNode, *state.BlockNode, *state.UtxoViewpoint, []*state.VoteResult) error
SaveChainNodeStatus(*state.BlockNode, *state.BlockNode) error
}
func (s *mockStore) GetVoteResult(uint64) (*state.VoteResult, error) { return nil, nil }
func (s *mockStore) LoadBlockIndex(uint64) (*state.BlockIndex, error) { return nil, nil }
func (s *mockStore) SaveBlock(*types.Block, *bc.TransactionStatus) error { return nil }
-func (s *mockStore) SaveChainStatus(*state.BlockNode, *state.BlockNode, *state.UtxoViewpoint, map[uint64]*state.VoteResult) error {
+func (s *mockStore) SaveChainStatus(*state.BlockNode, *state.BlockNode, *state.UtxoViewpoint, []*state.VoteResult) error {
return nil
}
func (s *mockStore) SaveChainNodeStatus(*state.BlockNode, *state.BlockNode) error { return nil }
func (s *mockStore1) GetVoteResult(uint64) (*state.VoteResult, error) { return nil, nil }
func (s *mockStore1) LoadBlockIndex(uint64) (*state.BlockIndex, error) { return nil, nil }
func (s *mockStore1) SaveBlock(*types.Block, *bc.TransactionStatus) error { return nil }
-func (s *mockStore1) SaveChainStatus(*state.BlockNode, *state.BlockNode, *state.UtxoViewpoint, map[uint64]*state.VoteResult) error {
+func (s *mockStore1) SaveChainStatus(*state.BlockNode, *state.BlockNode, *state.UtxoViewpoint, []*state.VoteResult) error {
return nil
}
func (s *mockStore1) SaveChainNodeStatus(*state.BlockNode, *state.BlockNode) error { return nil }
for k, v := range c.before {
utxoViewpoint.Entries[k] = v
}
- if err := store.SaveChainStatus(node, node, utxoViewpoint, map[uint64]*state.VoteResult{}); err != nil {
+ if err := store.SaveChainStatus(node, node, utxoViewpoint, []*state.VoteResult{}); err != nil {
t.Error(err)
}
t.Error(err)
}
}
- if err := store.SaveChainStatus(node, node, utxoViewpoint, map[uint64]*state.VoteResult{}); err != nil {
+ if err := store.SaveChainStatus(node, node, utxoViewpoint, []*state.VoteResult{}); err != nil {
t.Error(err)
}