OSDN Git Service

Dpos process block (#69)
[bytom/vapor.git] / protocol / bbft.go
1 package protocol
2
3 import (
4         "encoding/hex"
5         "time"
6
7         "github.com/vapor/crypto/ed25519"
8         "github.com/vapor/crypto/ed25519/chainkd"
9         "github.com/vapor/errors"
10         "github.com/vapor/math/checked"
11         "github.com/vapor/protocol/bc/types"
12         "github.com/vapor/protocol/state"
13 )
14
15 var (
16         errVotingOperationOverFlow = errors.New("voting operation result overflow")
17 )
18
19 type bbft struct {
20         consensusNodeManager *consensusNodeManager
21 }
22
23 func newBbft(store Store, blockIndex *state.BlockIndex) *bbft {
24         return &bbft{
25                 consensusNodeManager: newConsensusNodeManager(store, blockIndex),
26         }
27 }
28
29 // IsConsensusPubkey determine whether a public key is a consensus node at a specified height
30 func (b *bbft) IsConsensusPubkey(height uint64, pubkey []byte) (bool, error) {
31         node, err := b.consensusNodeManager.getConsensusNode(height, hex.EncodeToString(pubkey))
32         if err != nil && err != errNotFoundConsensusNode {
33                 return false, err
34         }
35         return node != nil, nil
36 }
37
38 func (b *bbft) isIrreversible(block *types.Block) bool {
39         signNum, err := b.validateSign(block)
40         if err != nil {
41                 return false
42         }
43
44         return signNum > (numOfConsensusNode * 2 / 3)
45 }
46
47 // NextLeaderTime returns the start time of the specified public key as the next leader node
48 func (b *bbft) NextLeaderTime(pubkey []byte, bestBlockTimestamp, bestBlockHeight uint64) (*time.Time, error) {
49         return b.consensusNodeManager.nextLeaderTime(pubkey, bestBlockTimestamp, bestBlockHeight)
50 }
51
52 func (b *bbft) ApplyBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) (err error) {
53         voteSeq := block.Height / roundVoteBlockNums
54         voteResult := voteResultMap[voteSeq]
55
56         if voteResult == nil {
57                 store := b.consensusNodeManager.store
58                 voteResult, err = store.GetVoteResult(voteSeq)
59                 if err != nil && err != ErrNotFoundVoteResult {
60                         return err
61                 }
62         }
63
64         if voteResult == nil {
65                 voteResult = &state.VoteResult{
66                         Seq:             voteSeq,
67                         NumOfVote:       make(map[string]uint64),
68                         LastBlockHeight: block.Height,
69                 }
70         }
71
72         voteResultMap[voteSeq] = voteResult
73
74         if voteResult.LastBlockHeight+1 != block.Height {
75                 return errors.New("bbft append block error, the block height is not equals last block height plus 1 of vote result")
76         }
77
78         for _, tx := range block.Transactions {
79                 for _, input := range tx.Inputs {
80                         unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
81                         if !ok {
82                                 continue
83                         }
84                         
85                         pubkey := hex.EncodeToString(unVoteInput.Vote)
86                         voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount)
87                         if !ok {
88                                 return errVotingOperationOverFlow
89                         }
90                 }
91                 for _, output := range tx.Outputs {
92                         voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
93                         if !ok {
94                                 continue
95                         }
96
97                         pubkey := hex.EncodeToString(voteOutput.Vote)
98                         voteResult.NumOfVote[pubkey], ok = checked.AddUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount)
99                         if !ok {
100                                 return errVotingOperationOverFlow
101                         }
102                 }
103         }
104
105         voteResult.LastBlockHeight++
106         voteResult.Finalized = (block.Height+1)%roundVoteBlockNums == 0
107         return nil
108 }
109
110 func (b *bbft) DetachBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) error {
111         voteSeq := block.Height / roundVoteBlockNums
112         voteResult := voteResultMap[voteSeq]
113
114         if voteResult == nil {
115                 store := b.consensusNodeManager.store
116                 voteResult, err := store.GetVoteResult(voteSeq)
117                 if err != nil {
118                         return err
119                 }
120                 voteResultMap[voteSeq] = voteResult
121         }
122
123         if voteResult.LastBlockHeight != block.Height {
124                 return errors.New("bbft detach block error, the block height is not equals last block height of vote result")
125         }
126
127         for _, tx := range block.Transactions {
128                 for _, input := range tx.Inputs {
129                         unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
130                         if !ok {
131                                 continue
132                         }
133                         
134                         pubkey := hex.EncodeToString(unVoteInput.Vote)
135                         voteResult.NumOfVote[pubkey], ok = checked.AddUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount)
136                         if !ok {
137                                 return errVotingOperationOverFlow
138                         }
139                 }
140                 for _, output := range tx.Outputs {
141                         voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
142                         if !ok {
143                                 continue
144                         }
145                         
146                         pubkey := hex.EncodeToString(voteOutput.Vote)
147                         voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount)
148                         if !ok {
149                                 return errVotingOperationOverFlow
150                         }
151                 }
152         }
153
154         voteResult.LastBlockHeight--
155         voteResult.Finalized = false
156         return nil
157 }
158
159 // ValidateBlock verify whether the block is valid
160 func (b *bbft) ValidateBlock(block *types.Block) error {
161         signNum, err := b.validateSign(block)
162         if err != nil {
163                 return err
164         }
165
166         if signNum == 0 {
167                 return errors.New("no valid signature")
168         }
169         return nil
170 }
171
172 // validateSign verify the signatures of block, and return the number of correct signature
173 // if some signature is invalid, they will be reset to nil
174 // if the block has not the signature of blocker, it will return error
175 func (b *bbft) validateSign(block *types.Block) (uint64, error) {
176         var correctSignNum uint64
177         consensusNodeMap, err := b.consensusNodeManager.getConsensusNodesByVoteResult(block.Height)
178         if err != nil {
179                 return 0, err
180         }
181
182         hasBlockerSign := false
183         for pubkey, node := range consensusNodeMap {
184                 if len(block.Witness) <= int(node.order) {
185                         continue
186                 }
187
188                 blocks := b.consensusNodeManager.blockIndex.NodesByHeight(block.Height)
189                 for _, b := range blocks {
190                         if b.Hash == block.Hash() {
191                                 continue
192                         }
193                         if ok, err := b.BlockWitness.Test(uint32(node.order)); err != nil && ok {
194                                 // Consensus node is signed twice with the same block height, discard the signature
195                                 block.Witness[node.order] = nil
196                                 break
197                         }
198                 }
199
200                 if ed25519.Verify(ed25519.PublicKey(pubkey), block.Hash().Bytes(), block.Witness[node.order]) {
201                         correctSignNum++
202                         isBlocker, err := b.consensusNodeManager.isBlocker(block.Height, block.Timestamp, pubkey)
203                         if err != nil {
204                                 return 0, err
205                         }
206                         if isBlocker {
207                                 hasBlockerSign = true
208                         }
209                 } else {
210                         // discard the invalid signature
211                         block.Witness[node.order] = nil
212                 }
213         }
214         if !hasBlockerSign {
215                 return 0, errors.New("the block has no signature of the blocker")
216         }
217         return correctSignNum, nil
218 }
219
220 // SignBlock signing the block if current node is consensus node
221 func (b *bbft) SignBlock(block *types.Block) error {
222         var xprv chainkd.XPrv
223         xpub := [64]byte(xprv.XPub())
224         node, err := b.consensusNodeManager.getConsensusNode(block.Height, hex.EncodeToString(xpub[:]))
225         if err != nil && err != errNotFoundConsensusNode {
226                 return err
227         }
228
229         if node == nil {
230                 return nil
231         }
232
233         block.Witness[node.order] = xprv.Sign(block.Hash().Bytes())
234         return nil
235 }
236
237 // UpdateConsensusNodes used to update consensus node after each round of voting
238 func (b *bbft) UpdateConsensusNodes(blockHeight uint64) error {
239         return b.consensusNodeManager.updateConsensusNodes(blockHeight)
240 }