type BlockSignatureEvent struct {
BlockHash bc.Hash
Signature []byte
+ XPub [64]byte
}
//NewBlockProposeEvent block propose event which needs to broadcast.
func NewManager(sw Switch, chain Chain, dispatcher *event.Dispatcher, peers *peers.PeerSet) *Manager {
manager := &Manager{
sw: sw,
+ chain: chain,
peers: peers,
blockFetcher: newBlockFetcher(chain, peers),
eventDispatcher: dispatcher,
count := 0
for now = timeStart; now < timeEnd && count < protocol.BlockNumEachNode; now = uint64(time.Now().UnixNano() / 1e6) {
- block, err := proposal.NewBlockTemplate(b.chain, b.txPool, b.accountManager)
+ block, err := proposal.NewBlockTemplate(b.chain, b.txPool, b.accountManager, now)
if err != nil {
log.Errorf("failed on create NewBlockTemplate: %v", err)
} else {
}
// NewBlockTemplate returns a new block template that is ready to be solved
-func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager *account.Manager) (b *types.Block, err error) {
+func NewBlockTemplate(c *protocol.Chain, txPool *protocol.TxPool, accountManager *account.Manager, timestamp uint64) (b *types.Block, err error) {
view := state.NewUtxoViewpoint()
txStatus := bc.NewTransactionStatus()
if err := txStatus.SetStatus(0, false); err != nil {
Version: 1,
Height: nextBlockHeight,
PreviousBlockHash: preBlockHash,
- Timestamp: uint64(time.Now().UnixNano() / int64(time.Millisecond)),
+ Timestamp: timestamp,
BlockCommitment: types.BlockCommitment{},
+ BlockWitness: types.BlockWitness{Witness: make([][]byte, protocol.NumOfConsensusNode)},
},
}
bcBlock := &bc.Block{BlockHeader: &bc.BlockHeader{Height: nextBlockHeight}}
"github.com/vapor/protocol/bc"
"github.com/vapor/protocol/bc/types"
"github.com/vapor/protocol/state"
+ "github.com/vapor/crypto/ed25519/chainkd"
)
const (
}
}
-// IsConsensusPubkey determine whether a public key is a consensus node at a specified height
-func (b *bbft) IsConsensusPubkey(blockHash *bc.Hash, pubkey []byte) (bool, error) {
- node, err := b.consensusNodeManager.getConsensusNode(blockHash, hex.EncodeToString(pubkey))
- if err != nil && err != errNotFoundConsensusNode {
- return false, err
- }
- return node != nil, nil
-}
-
func (b *bbft) isIrreversible(block *types.Block) bool {
signNum, err := b.validateSign(block)
if err != nil {
return false
}
- return signNum > (numOfConsensusNode * 2 / 3)
+ return signNum > (NumOfConsensusNode * 2 / 3)
}
// NextLeaderTime returns the start time of the specified public key as the next leader node
}
// ProcessBlockSignature process the received block signature messages
-// return once a block become irreversible, whether it's height greater than best block height
-// if so, the chain module must update status
-func (b *bbft) ProcessBlockSignature(signature, pubkey []byte, blockHeight uint64, blockHash *bc.Hash) (bool, error) {
- consensusNode, err := b.consensusNodeManager.getConsensusNode(blockHash, hex.EncodeToString(pubkey))
+// return whether a block become irreversible, if so, the chain module must update status
+func (b *bbft) ProcessBlockSignature(signature []byte, xPub [64]byte, blockHeight uint64, blockHash *bc.Hash) (bool, error) {
+ block, err := b.consensusNodeManager.store.GetBlock(blockHash)
+ if err != nil {
+ // block is not exist, save the signature
+ key := fmt.Sprintf("%s:%s", blockHash.String(), hex.EncodeToString(xPub[:]))
+ b.signatureCache.Add(key, signature)
+ return false, err
+ }
+
+ consensusNode, err := b.consensusNodeManager.getConsensusNode(&block.PreviousBlockHash, hex.EncodeToString(xPub[:]))
if err != nil {
return false, err
}
- if !ed25519.Verify(ed25519.PublicKey(pubkey), blockHash.Bytes(), signature) {
+
+ if chainkd.XPub(xPub).Verify(blockHash.Bytes(), signature) {
return false, errInvalidSignature
}
}
if isDoubleSign {
- log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String(), "pubkey": pubkey}).Warn("the consensus node double sign the same height of different block")
+ log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String(), "xPub": hex.EncodeToString(xPub[:])}).Warn("the consensus node double sign the same height of different block")
return false, errDoubleSignBlock
}
return false, nil
}
- block, err := b.consensusNodeManager.store.GetBlock(blockHash)
- if err != nil {
- // block is not exist, save the signature
- key := fmt.Sprintf("%s:%s", blockHash.String(), hex.EncodeToString(pubkey))
- b.signatureCache.Add(key, signature)
- return false, err
- }
-
if err := b.updateBlockSignature(block, consensusNode.order, signature); err != nil {
return false, err
}
- return b.isIrreversible(block) && blockHeight > b.consensusNodeManager.blockIndex.BestNode().Height, nil
+ return b.isIrreversible(block), nil
}
// ValidateBlock verify whether the block is valid
// if the block has not the signature of blocker, it will return error
func (b *bbft) validateSign(block *types.Block) (uint64, error) {
var correctSignNum uint64
- blockHash := block.Hash()
- consensusNodeMap, err := b.consensusNodeManager.getConsensusNodesByVoteResult(&blockHash)
+ consensusNodeMap, err := b.consensusNodeManager.getConsensusNodesByVoteResult(&block.PreviousBlockHash)
if err != nil {
return 0, err
}
hasBlockerSign := false
- for pubkey, node := range consensusNodeMap {
+ for pubKey, node := range consensusNodeMap {
if len(block.Witness) <= int(node.order) {
continue
}
blockHash := block.Hash()
if block.Witness[node.order] == nil {
- key := fmt.Sprintf("%s:%s", blockHash.String(), pubkey)
+ key := fmt.Sprintf("%s:%s", blockHash.String(), pubKey)
signature, ok := b.signatureCache.Get(key)
if ok {
block.Witness[node.order] = signature.([]byte)
}
}
- if ed25519.Verify(ed25519.PublicKey(pubkey), blockHash.Bytes(), block.Witness[node.order]) {
+ pubKeyBytes, err := hex.DecodeString(pubKey)
+ if err != nil {
+ return 0, err
+ }
+
+ if ed25519.Verify(ed25519.PublicKey(pubKeyBytes[:32]), blockHash.Bytes(), block.Witness[node.order]) {
isDoubleSign, err := b.checkDoubleSign(node.order, block.Height, block.Hash())
if err != nil {
return 0, err
}
if isDoubleSign {
- log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String(), "pubkey": pubkey}).Warn("the consensus node double sign the same height of different block")
+ log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String(), "pubKey": pubKey}).Warn("the consensus node double sign the same height of different block")
// Consensus node is signed twice with the same block height, discard the signature
block.Witness[node.order] = nil
} else {
correctSignNum++
- isBlocker, err := b.consensusNodeManager.isBlocker(&blockHash, pubkey)
+ isBlocker, err := b.consensusNodeManager.isBlocker(block, pubKey)
if err != nil {
return 0, err
}
func (b *bbft) SignBlock(block *types.Block) ([]byte, error) {
xprv := config.CommonConfig.PrivateKey()
xpub := [64]byte(xprv.XPub())
- blockHash := block.Hash()
- node, err := b.consensusNodeManager.getConsensusNode(&blockHash, hex.EncodeToString(xpub[:]))
+ node, err := b.consensusNodeManager.getConsensusNode(&block.PreviousBlockHash, hex.EncodeToString(xpub[:]))
if err != nil && err != errNotFoundConsensusNode {
return nil, err
}
return nil, nil
}
- signature := xprv.Sign(block.Hash().Bytes())
- block.Witness[node.order] = signature
+ signature := block.Witness[node.order]
+ if len(signature) == 0 {
+ signature = xprv.Sign(block.Hash().Bytes())
+ block.Witness[node.order] = signature
+ }
return signature, nil
}
cases := []struct {
desc string
startTime uint64
- endTime uint64
now uint64
nodeOrder uint64
wantError error
{
desc: "normal case",
startTime: 1557906284061,
- endTime: 1557906784061,
now: 1557906534061,
nodeOrder: 1,
wantError: nil,
{
desc: "best block height equals to start block height",
startTime: 1557906284061,
- endTime: 1557906784061,
now: 1557906284061,
nodeOrder: 0,
wantError: nil,
{
desc: "best block height equals to start block height",
startTime: 1557906284061,
- endTime: 1557906784061,
now: 1557906284061,
nodeOrder: 1,
wantError: nil,
wantNextLeaderTime: 1557906284061 + BlockNumEachNode*BlockTimeInterval,
},
{
- desc: "has no chance product block in this round of voting",
- startTime: 1557906284061,
- endTime: 1557906784061,
- now: 1557906781561,
- nodeOrder: 1,
- wantError: errHasNoChanceProductBlock,
- wantNextLeaderTime: 0,
- },
- {
desc: "the node is producting block",
startTime: 1557906284061,
- endTime: 1557906784061,
now: 1557906284561,
nodeOrder: 0,
wantError: nil,
- wantNextLeaderTime: 1557906284061,
+ wantNextLeaderTime: 1557906315561,
},
{
desc: "the node is producting block",
startTime: 1557906284061,
- endTime: 1557906784061,
now: 1557906317561,
nodeOrder: 1,
wantError: nil,
- wantNextLeaderTime: 1557906284061 + 66*BlockTimeInterval,
+ wantNextLeaderTime: 1557906348561,
},
{
desc: "first round, must exclude genesis block",
startTime: 1557906284061,
- endTime: 1557906783561,
now: 1557906286561,
nodeOrder: 3,
wantError: nil,
}
for i, c := range cases {
- nextLeaderTimestamp, err := nextLeaderTimeHelper(c.startTime, c.endTime, c.now, c.nodeOrder)
+ nextLeaderTimestamp, err := nextLeaderTimeHelper(c.startTime, c.now, c.nodeOrder)
if err != c.wantError {
t.Fatalf("case #%d (%s) want error:%v, got error:%v", i, c.desc, c.wantError, err)
}
"github.com/vapor/errors"
"github.com/vapor/event"
+ "github.com/vapor/config"
"github.com/vapor/protocol/bc"
"github.com/vapor/protocol/bc/types"
"github.com/vapor/protocol/state"
}
parent := c.index.GetNode(&block.PreviousBlockHash)
- if err := validation.ValidateBlock(types.MapBlock(block), parent); err != nil {
+ bcBlock := types.MapBlock(block)
+ if err := validation.ValidateBlock(bcBlock, parent); err != nil {
return errors.Sub(ErrBadBlock, err)
}
return errors.Sub(ErrBadBlock, err)
}
- if len(signature) != 0 {
- if err := c.bbft.eventDispatcher.Post(event.BlockSignatureEvent{BlockHash: block.Hash(), Signature: signature}); err != nil {
- return err
- }
- }
-
- bcBlock := types.MapBlock(block)
if err := c.store.SaveBlock(block, bcBlock.TransactionStatus); err != nil {
return err
}
}
c.index.AddNode(node)
+
+ if len(signature) != 0 {
+ xPub := config.CommonConfig.PrivateKey().XPub()
+ if err := c.bbft.eventDispatcher.Post(event.BlockSignatureEvent{BlockHash: block.Hash(), Signature: signature, XPub: xPub}); err != nil {
+ return err
+ }
+ }
return nil
}
return false, nil
}
-func (c *Chain) ProcessBlockSignature(signature, pubkey []byte, blockHeight uint64, blockHash *bc.Hash) error {
- isBestIrreversible, err := c.bbft.ProcessBlockSignature(signature, pubkey, blockHeight, blockHash)
+func (c *Chain) ProcessBlockSignature(signature, pubKey []byte, blockHeight uint64, blockHash *bc.Hash) error {
+ var xPub [64]byte
+ for i := 0; i < 64; i++ {
+ xPub[i] = pubKey[i]
+ }
+
+ isIrreversible, err := c.bbft.ProcessBlockSignature(signature, xPub, blockHeight, blockHash)
if err != nil {
return err
}
- if isBestIrreversible {
+ if isIrreversible && blockHeight > c.bestIrreversibleNode.Height {
bestIrreversibleNode := c.index.GetNode(blockHash)
if err := c.store.SaveChainNodeStatus(c.bestNode, bestIrreversibleNode); err != nil {
return err
)
const (
- numOfConsensusNode = 21
+ NumOfConsensusNode = 21
roundVoteBlockNums = 1000
// BlockTimeInterval indicate product one block per 500 milliseconds
)
var (
- errHasNoChanceProductBlock = errors.New("the node has no chance to product a block in this round of voting")
errNotFoundConsensusNode = errors.New("can not found consensus node")
errNotFoundBlockNode = errors.New("can not find block node")
)
}
}
-func (c *consensusNodeManager) getConsensusNode(blockHash *bc.Hash, pubkey string) (*consensusNode, error) {
- consensusNodeMap, err := c.getConsensusNodesByVoteResult(blockHash)
+func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*consensusNode, error) {
+ consensusNodeMap, err := c.getConsensusNodesByVoteResult(prevBlockHash)
if err != nil {
return nil, err
}
return node, nil
}
-func (c *consensusNodeManager) isBlocker(blockHash *bc.Hash, pubkey string) (bool, error) {
- blockNode := c.blockIndex.GetNode(blockHash)
- if blockNode == nil {
- return false, errNotFoundBlockNode
- }
-
- consensusNode, err := c.getConsensusNode(blockHash, pubkey)
+func (c *consensusNodeManager) isBlocker(block *types.Block, pubKey string) (bool, error) {
+ consensusNode, err := c.getConsensusNode(&block.PreviousBlockHash, pubKey)
if err != nil && err != errNotFoundConsensusNode {
return false, err
}
return false, nil
}
- prevVoteRoundLastBlock, err := c.getPrevRoundVoteLastBlock(blockNode)
+ prevVoteRoundLastBlock, err := c.getPrevRoundVoteLastBlock(&block.PreviousBlockHash)
if err != nil {
return false, err
}
startTimestamp := prevVoteRoundLastBlock.Timestamp + BlockTimeInterval
- begin := getLastBlockTimeInTimeRange(startTimestamp, blockNode.Timestamp, consensusNode.order)
+ begin := getLastBlockTimeInTimeRange(startTimestamp, block.Timestamp, consensusNode.order)
end := begin + BlockNumEachNode*BlockTimeInterval
- return blockNode.Timestamp >= begin && blockNode.Timestamp < end, nil
+ return block.Timestamp >= begin && block.Timestamp < end, nil
}
func (c *consensusNodeManager) nextLeaderTimeRange(pubkey []byte, bestBlockHash *bc.Hash) (uint64, uint64, error) {
return 0, 0, errNotFoundBlockNode
}
- consensusNode, err := c.getConsensusNode(bestBlockHash, hex.EncodeToString(pubkey))
+ consensusNode, err := c.getConsensusNode(&bestBlockNode.Parent.Hash, hex.EncodeToString(pubkey))
if err != nil {
return 0, 0, err
}
- prevRoundLastBlock, err := c.getPrevRoundVoteLastBlock(bestBlockNode)
+ prevRoundLastBlock, err := c.getPrevRoundVoteLastBlock(&bestBlockNode.Parent.Hash)
if err != nil {
- return 0, 0, nil
+ return 0, 0, err
}
startTime := prevRoundLastBlock.Timestamp + BlockTimeInterval
- endTime := startTime + roundVoteBlockNums*BlockTimeInterval
- nextLeaderTime, err := nextLeaderTimeHelper(startTime, endTime, uint64(time.Now().UnixNano()/1e6), consensusNode.order)
+ nextLeaderTime, err := nextLeaderTimeHelper(startTime, uint64(time.Now().UnixNano()/1e6), consensusNode.order)
if err != nil {
return 0, 0, err
}
return nextLeaderTime, nextLeaderTime + BlockNumEachNode*BlockTimeInterval, nil
}
-func nextLeaderTimeHelper(startTime, endTime, now, nodeOrder uint64) (uint64, error) {
+func nextLeaderTimeHelper(startTime, now, nodeOrder uint64) (uint64, error) {
nextLeaderTimestamp := getLastBlockTimeInTimeRange(startTime, now, nodeOrder)
- roundBlockTime := uint64(BlockNumEachNode * numOfConsensusNode * BlockTimeInterval)
+ roundBlockTime := uint64(BlockNumEachNode * NumOfConsensusNode * BlockTimeInterval)
- if int64(now-nextLeaderTimestamp) >= BlockNumEachNode*BlockTimeInterval {
+ if now > nextLeaderTimestamp {
nextLeaderTimestamp += roundBlockTime
- if nextLeaderTimestamp >= endTime {
- return 0, errHasNoChanceProductBlock
- }
}
return nextLeaderTimestamp, nil
func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order uint64) uint64 {
// One round of product block time for all consensus nodes
- roundBlockTime := uint64(BlockNumEachNode * numOfConsensusNode * BlockTimeInterval)
+ roundBlockTime := uint64(BlockNumEachNode * NumOfConsensusNode * 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*(BlockNumEachNode*BlockTimeInterval)
}
-func (c *consensusNodeManager) getPrevRoundVoteLastBlock(blockNode *state.BlockNode) (*state.BlockNode, error) {
- prevVoteRoundLastBlockHeight := blockNode.Height/roundVoteBlockNums*roundVoteBlockNums - 1
- lastBlockNode := blockNode.GetParent(prevVoteRoundLastBlockHeight)
+func (c *consensusNodeManager) getPrevRoundVoteLastBlock(prevBlockHash *bc.Hash) (*state.BlockNode, error) {
+ prevBlockNode := c.blockIndex.GetNode(prevBlockHash)
+ if prevBlockNode == nil {
+ return nil, errNotFoundBlockNode
+ }
+
+ blockHeight := prevBlockNode.Height + 1
+
+ prevVoteRoundLastBlockHeight := blockHeight/roundVoteBlockNums*roundVoteBlockNums - 1
+ // first round
+ if blockHeight / roundVoteBlockNums == 0 {
+ prevVoteRoundLastBlockHeight = 0
+ }
+
+ lastBlockNode := prevBlockNode.GetParent(prevVoteRoundLastBlockHeight)
if lastBlockNode == nil {
return nil, errNotFoundBlockNode
}
return lastBlockNode, nil
}
-func (c *consensusNodeManager) getConsensusNodesByVoteResult(blockHash *bc.Hash) (map[string]*consensusNode, error) {
- blockNode := c.blockIndex.GetNode(blockHash)
- if blockNode == nil {
+func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.Hash) (map[string]*consensusNode, error) {
+ prevBlockNode := c.blockIndex.GetNode(prevBlockHash)
+ if prevBlockNode == nil {
return nil, errNotFoundBlockNode
}
- seq := blockNode.Height / roundVoteBlockNums
+ seq := (prevBlockNode.Height + 1) / roundVoteBlockNums
if seq == 0 {
return initVoteResult(), nil
}
}
}
- lastBlockNode, err := c.getPrevRoundVoteLastBlock(blockNode)
+ lastBlockNode, err := c.getPrevRoundVoteLastBlock(prevBlockHash)
if err != nil {
return nil, err
}
sort.Sort(consensusNodeSlice(nodes))
result := make(map[string]*consensusNode)
- for i := 0; i < numOfConsensusNode; i++ {
+ for i := 0; i < NumOfConsensusNode; i++ {
node := nodes[i]
node.order = uint64(i)
result[node.pubkey] = node
voteResult = &state.VoteResult{
Seq: voteSeq,
NumOfVote: make(map[string]uint64),
- LastBlockHash: block.Hash(),
}
}
voteResultMap[voteSeq] = voteResult
- if voteResult.LastBlockHash != block.PreviousBlockHash {
+ emptyHash := bc.Hash{}
+ if voteResult.LastBlockHash != emptyHash && voteResult.LastBlockHash != block.PreviousBlockHash {
return errors.New("bbft append block error, the block parent hash is not equals last block hash of vote result")
}
}
c.bestNode = c.index.GetNode(storeStatus.Hash)
+ c.bestIrreversibleNode = c.index.GetNode(storeStatus.IrreversibleHash)
c.index.SetMainChain(c.bestNode)
c.bbft = newBbft(store, c.index, c.orphanManage, eventDispatcher)
go c.blockProcesser()
}
}
- block, err := proposal.NewBlockTemplate(chain, txPool, nil)
+ block, err := proposal.NewBlockTemplate(chain, txPool, nil, uint64(time.Now().UnixNano() / 1e6))
if err != nil {
return err
}
import (
"os"
"testing"
+ "time"
"github.com/vapor/account"
dbm "github.com/vapor/database/leveldb"
b.ResetTimer()
for i := 0; i < b.N; i++ {
- proposal.NewBlockTemplate(chain, txPool, accountManager)
+ proposal.NewBlockTemplate(chain, txPool, accountManager, uint64(time.Now().UnixNano() / 1e6))
}
}