OSDN Git Service

Add the basic framework for voting processing for dpos
[bytom/vapor.git] / consensus / consensus / dpos.go
1 package consensus
2
3 import (
4         "encoding/json"
5         "errors"
6         "fmt"
7         "math/rand"
8         "path/filepath"
9         "sync"
10         "time"
11
12         "github.com/vapor/crypto/ed25519/chainkd"
13
14         "github.com/vapor/consensus"
15
16         "github.com/vapor/chain"
17         "github.com/vapor/common"
18         "github.com/vapor/config"
19         "github.com/vapor/protocol/bc"
20         "github.com/vapor/protocol/bc/types"
21 )
22
23 type Delegate struct {
24         DelegateAddress string `json:"delegate_address"`
25         Votes           uint64 `json:"votes"`
26 }
27
28 type DelegateInfo struct {
29         Delegates []Delegate `json:"delegates"`
30 }
31
32 //OP_FAIL PUBKEY SIG(block.time) DELEGATE_IDS
33 func DelegateInfoToScript(delegateInfo DelegateInfo, xpub chainkd.XPub, h bc.Hash) {
34
35 }
36
37 //ScriptToDelegateInfo
38
39 const maxConfirmBlockCount = 2
40
41 type IrreversibleBlockInfo struct {
42         heights    []int64
43         hashs      []bc.Hash
44         HeightHash map[int64]bc.Hash
45 }
46
47 func newIrreversibleBlockInfo() *IrreversibleBlockInfo {
48         o := &IrreversibleBlockInfo{}
49         for i := 0; i < maxConfirmBlockCount; i++ {
50                 o.heights = append(o.heights, -1)
51         }
52         return o
53 }
54
55 type DposType struct {
56         c                         chain.Chain
57         MaxDelegateNumber         uint64
58         BlockIntervalTime         uint64
59         DposStartHeight           uint64
60         DposStartTime             uint64
61         cSuperForgerAddress       common.Address
62         irreversibleBlockFileName string
63         cIrreversibleBlockInfo    IrreversibleBlockInfo
64         lockIrreversibleBlockInfo sync.Mutex
65 }
66
67 var GDpos = &DposType{}
68
69 func init() {
70         GDpos.irreversibleBlockFileName = filepath.Join(config.DefaultDataDir(), "dpos", "irreversible_block.dat")
71         GDpos.ReadIrreversibleBlockInfo(&GDpos.cIrreversibleBlockInfo)
72
73 }
74
75 func (d *DposType) ReadIrreversibleBlockInfo(info *IrreversibleBlockInfo) error {
76         return nil
77 }
78
79 func (d *DposType) IsMining(cDelegateInfo *DelegateInfo, address common.Address, t uint64) error {
80
81         header := d.c.BestBlockHeader()
82         currentLoopIndex := d.GetLoopIndex(t)
83         currentDelegateIndex := d.GetDelegateIndex(t)
84         prevLoopIndex := d.GetLoopIndex(header.Timestamp)
85         prevDelegateIndex := d.GetDelegateIndex(header.Timestamp)
86
87         if currentLoopIndex > prevLoopIndex {
88                 *cDelegateInfo = d.GetNextDelegates(t)
89                 if cDelegateInfo.Delegates[currentLoopIndex].DelegateAddress == address.EncodeAddress() {
90                         return nil
91                 }
92                 return errors.New("Is not the current mining node")
93         } else if currentLoopIndex == prevLoopIndex && currentDelegateIndex > prevDelegateIndex {
94                 //currentDelegateInfo := DelegateInfo{}
95                 currentDelegateInfo, err := d.GetBlockDelegates(header)
96                 if err != nil {
97                         return err
98                 }
99                 if currentDelegateIndex+1 > uint64(len(currentDelegateInfo.Delegates)) {
100                         return errors.New("Out of the block node list")
101                 } else if currentDelegateInfo.Delegates[currentDelegateIndex].DelegateAddress == address.EncodeAddress() {
102                         return nil
103                 } else {
104                         return errors.New("Is not the current mining node")
105                 }
106         } else {
107                 return errors.New("Time anomaly")
108         }
109 }
110
111 func (d *DposType) GetLoopIndex(time uint64) uint64 {
112         if time < d.DposStartTime {
113                 return 0
114         }
115
116         return (time - d.DposStartTime) / (d.MaxDelegateNumber * d.BlockIntervalTime)
117 }
118
119 func (d *DposType) GetDelegateIndex(time uint64) uint64 {
120         if time < d.DposStartTime {
121                 return 0
122         }
123
124         return (time - d.DposStartTime) % (d.MaxDelegateNumber * d.BlockIntervalTime) / d.BlockIntervalTime
125 }
126
127 func (d *DposType) GetNextDelegates(t uint64) DelegateInfo {
128         delegates := DposVote.GetTopDelegateInfo(consensus.MinHoldBalance, d.MaxDelegateNumber-1)
129         delegate := Delegate{
130                 DelegateAddress: d.cSuperForgerAddress.EncodeAddress(),
131                 Votes:           7,
132         }
133         delegates = append(delegates, delegate)
134         delegateInfo := DelegateInfo{}
135         delegateInfo.Delegates = SortDelegate(delegates, t)
136         return delegateInfo
137 }
138
139 func (d *DposType) GetBlockDelegates(header *types.BlockHeader) (*DelegateInfo, error) {
140         loopIndex := d.GetLoopIndex(header.Timestamp)
141         for {
142                 preHeader, err := d.c.GetHeaderByHash(&header.PreviousBlockHash)
143                 if err != nil {
144                         return nil, err
145                 }
146                 if header.Height == d.DposStartHeight || d.GetLoopIndex(preHeader.Timestamp) < loopIndex {
147                         block, err := d.c.GetBlockByHeight(header.Height)
148                         if err != nil {
149                                 return nil, err
150                         }
151                         delegateInfo, err := d.GetBlockDelegate(block)
152                         if err != nil {
153                                 return nil, err
154                         }
155                         return delegateInfo, nil
156                 }
157                 header = preHeader
158         }
159 }
160
161 func (d *DposType) GetBlockDelegate(block *types.Block) (*DelegateInfo, error) {
162         tx := block.Transactions[0]
163         var delegate TypedData
164         if len(tx.TxData.Inputs) == 1 && tx.TxData.Inputs[0].InputType() == types.CoinbaseInputType {
165                 if err := json.Unmarshal(tx.TxData.ReferenceData, delegate); err != nil {
166                         return nil, err
167                 }
168                 if delegateInfo, ok := delegate.(*DelegateInfoList); ok {
169                         return &delegateInfo.Delegate, nil
170                 }
171         }
172         return nil, errors.New("The first transaction is not a coinbase transaction")
173 }
174
175 func (d *DposType) CheckCoinbase(tx types.TxData, t uint64, Height uint64) error {
176
177         return nil
178 }
179
180 func (d *DposType) CheckBlockHeader(header types.BlockHeader) error {
181         blockT := time.Unix(int64(header.Timestamp), 0)
182
183         if blockT.Sub(time.Now()).Seconds() > 3 {
184                 return errors.New("block time is error")
185         }
186
187         return nil
188 }
189
190 func (d *DposType) CheckBlock(block types.Block, fIsCheckDelegateInfo bool) error {
191         blockT := time.Unix(int64(block.Timestamp), 0)
192
193         if blockT.Sub(time.Now()).Seconds() > 3 {
194                 return errors.New("block time is error")
195         }
196
197         if err := d.CheckCoinbase(block.Transactions[0].TxData, block.Timestamp, block.Height); err != nil {
198                 return err
199         }
200
201         preBlock, err := d.c.GetBlockByHash(&block.PreviousBlockHash)
202         if err != nil {
203                 return err
204         }
205
206         currentLoopIndex := d.GetLoopIndex(block.Timestamp)
207         currentDelegateIndex := d.GetDelegateIndex(block.Timestamp)
208         prevLoopIndex := d.GetLoopIndex(preBlock.Timestamp)
209         prevDelegateIndex := d.GetDelegateIndex(preBlock.Timestamp)
210
211         delegateInfo := &DelegateInfo{}
212
213         if currentLoopIndex < prevLoopIndex {
214                 return errors.New("Block time exception")
215         } else if currentLoopIndex > prevLoopIndex {
216                 if fIsCheckDelegateInfo {
217                         if err := d.CheckBlockDelegate(block); err != nil {
218                                 return err
219                         }
220                         d.ProcessIrreversibleBlock(block.Height, block.Hash())
221                 }
222                 delegateInfo, err = d.GetBlockDelegate(&block)
223                 if err != nil {
224                         return err
225                 }
226         } else {
227                 if currentDelegateIndex < prevDelegateIndex {
228                         return errors.New("Block time exception")
229                 }
230
231                 delegateInfo, err = d.GetBlockDelegates(&preBlock.BlockHeader)
232                 if err != nil {
233                         return err
234                 }
235         }
236
237         delegateAddress := d.getBlockForgerAddress(block)
238         if currentDelegateIndex < uint64(len(delegateInfo.Delegates)) &&
239                 delegateInfo.Delegates[currentDelegateIndex].DelegateAddress == delegateAddress.EncodeAddress() {
240                 return nil
241         }
242         h := block.Hash()
243         return fmt.Errorf("CheckBlock GetDelegateID blockhash:%s error", h.String())
244 }
245
246 func (d *DposType) CheckBlockDelegate(block types.Block) error {
247         return nil
248 }
249
250 func (d *DposType) ProcessIrreversibleBlock(height uint64, hash bc.Hash) {
251
252 }
253
254 func (d *DposType) getBlockForgerAddress(block types.Block) common.Address {
255         tx := block.Transactions[0].TxData
256
257         if len(tx.Inputs) == 1 && tx.Inputs[0].InputType() == types.CoinbaseInputType {
258                 address, err := common.NewAddressWitnessPubKeyHash(tx.Outputs[0].ControlProgram[2:], &consensus.ActiveNetParams)
259                 if err != nil {
260                         address, err := common.NewAddressWitnessScriptHash(tx.Outputs[0].ControlProgram[2:], &consensus.ActiveNetParams)
261                         if err != nil {
262                                 return nil
263                         }
264                         return address
265                 }
266                 return address
267         }
268
269         return nil
270 }
271
272 func (d *DposType) IsValidBlockCheckIrreversibleBlock(height uint64, hash bc.Hash) error {
273         d.lockIrreversibleBlockInfo.Lock()
274         defer d.lockIrreversibleBlockInfo.Unlock()
275
276         if h, ok := d.cIrreversibleBlockInfo.HeightHash[int64(height)]; ok {
277                 if h != hash {
278                         return fmt.Errorf("invalid block[%d:%s]", height, hash.String())
279                 }
280         }
281
282         return nil
283 }
284
285 func SortDelegate(delegates []Delegate, t uint64) []Delegate {
286         var result []Delegate
287         r := getRand(uint64(len(delegates)), int64(t))
288         for _, i := range r {
289                 result = append(result, delegates[i])
290         }
291         return result
292 }
293
294 func getRand(num uint64, seed int64) []uint64 {
295         rand.Seed(seed)
296         var r []uint64
297         s := make(map[uint64]bool)
298         for {
299                 v := rand.Uint64()
300                 v %= num
301                 if _, ok := s[v]; ok {
302                         continue
303                 }
304                 s[v] = true
305                 r = append(r, v)
306                 if uint64(len(r)) >= num {
307                         break
308                 }
309         }
310
311         return r
312 }