OSDN Git Service

add dpos consensus
[bytom/vapor.git] / consensus / consensus / dpos / snapshot.go
1 package dpos
2
3 import (
4         "encoding/json"
5         "errors"
6         "fmt"
7         "math/big"
8         "sort"
9         "time"
10
11         lru "github.com/hashicorp/golang-lru"
12         "github.com/vapor/common"
13         "github.com/vapor/config"
14         "github.com/vapor/consensus"
15         "github.com/vapor/crypto"
16         "github.com/vapor/crypto/ed25519/chainkd"
17         "github.com/vapor/protocol"
18         "github.com/vapor/protocol/bc"
19         "github.com/vapor/protocol/bc/types"
20 )
21
22 const (
23         defaultFullCredit               = 1000 // no punished
24         missingPublishCredit            = 100  // punished for missing one block seal
25         signRewardCredit                = 10   // seal one block
26         autoRewardCredit                = 1    // credit auto recover for each block
27         minCalSignerQueueCredit         = 300  // when calculate the signerQueue
28         defaultOfficialMaxSignerCount   = 21   // official max signer count
29         defaultOfficialFirstLevelCount  = 10   // official first level , 100% in signer queue
30         defaultOfficialSecondLevelCount = 20   // official second level, 60% in signer queue
31         defaultOfficialThirdLevelCount  = 30   // official third level, 40% in signer queue
32         // the credit of one signer is at least minCalSignerQueueCredit
33         candidateStateNormal = 1
34         candidateMaxLen      = 500 // if candidateNeedPD is false and candidate is more than candidateMaxLen, then minimum tickets candidates will be remove in each LCRS*loop
35 )
36
37 var errIncorrectTallyCount = errors.New("incorrect tally count")
38
39 type Snapshot struct {
40         config          *config.DposConfig    // Consensus engine configuration parameters
41         sigcache        *lru.ARCCache         // Cache of recent block signatures to speed up ecrecover
42         LCRS            uint64                // Loop count to recreate signers from top tally
43         Period          uint64                `json:"period"`           // Period of seal each block
44         Number          uint64                `json:"number"`           // Block Number where the snapshot was created
45         ConfirmedNumber uint64                `json:"confirmed_number"` // Block Number confirmed when the snapshot was created
46         Hash            bc.Hash               `json:"hash"`             // Block hash where the snapshot was created
47         HistoryHash     []bc.Hash             `json:"historyHash"`      // Block hash list for two recent loop
48         Signers         []*string             `json:"signers"`          // Signers queue in current header
49         Votes           map[string]*Vote      `json:"votes"`            // All validate votes from genesis block
50         Tally           map[string]uint64     `json:"tally"`            // Stake for each candidate address
51         Voters          map[string]uint64     `json:"voters"`           // Block height for each voter address
52         Candidates      map[string]uint64     `json:"candidates"`       // Candidates for Signers (0- adding procedure 1- normal 2- removing procedure)
53         Punished        map[string]uint64     `json:"punished"`         // The signer be punished count cause of missing seal
54         Confirmations   map[uint64][]string   `json:"confirms"`         // The signer confirm given block height
55         Proposals       map[bc.Hash]*Proposal `json:"proposals"`        // The Proposals going or success (failed proposal will be removed)
56         HeaderTime      uint64                `json:"headerTime"`       // Time of the current header
57         LoopStartTime   uint64                `json:"loopStartTime"`    // Start Time of the current loop
58 }
59
60 // newSnapshot creates a new snapshot with the specified startup parameters. only ever use if for
61 // the genesis block.
62 func newSnapshot(config *config.DposConfig, sigcache *lru.ARCCache, hash bc.Hash, votes []*Vote, lcrs uint64) *Snapshot {
63
64         snap := &Snapshot{
65                 config:          config,
66                 sigcache:        sigcache,
67                 LCRS:            lcrs,
68                 Period:          config.Period,
69                 Number:          0,
70                 ConfirmedNumber: 0,
71                 Hash:            hash,
72                 HistoryHash:     []bc.Hash{},
73                 Signers:         []*string{},
74                 Votes:           make(map[string]*Vote),
75                 Tally:           make(map[string]uint64),
76                 Voters:          make(map[string]uint64),
77                 Punished:        make(map[string]uint64),
78                 Candidates:      make(map[string]uint64),
79                 Confirmations:   make(map[uint64][]string),
80                 Proposals:       make(map[bc.Hash]*Proposal),
81                 HeaderTime:      uint64(time.Now().Unix()) - 1,
82                 LoopStartTime:   config.GenesisTimestamp,
83         }
84         snap.HistoryHash = append(snap.HistoryHash, hash)
85         for _, vote := range votes {
86                 // init Votes from each vote
87                 snap.Votes[vote.Voter] = vote
88                 // init Tally
89                 _, ok := snap.Tally[vote.Candidate]
90                 if !ok {
91                         snap.Tally[vote.Candidate] = 0
92                 }
93                 fmt.Println("newSnapshot", vote.Candidate, vote.Stake)
94                 fmt.Println(snap.Tally)
95                 snap.Tally[vote.Candidate] += vote.Stake
96                 // init Voters
97                 snap.Voters[vote.Voter] = 0 // block height is 0 , vote in genesis block
98                 // init Candidates
99                 snap.Candidates[vote.Voter] = candidateStateNormal
100         }
101
102         for i := 0; i < int(config.MaxSignerCount); i++ {
103                 snap.Signers = append(snap.Signers, &config.SelfVoteSigners[i%len(config.SelfVoteSigners)])
104         }
105
106         return snap
107 }
108
109 // loadSnapshot loads an existing snapshot from the database.
110 func loadSnapshot(config *config.DposConfig, sigcache *lru.ARCCache, store protocol.Store, hash bc.Hash) (*Snapshot, error) {
111         data, err := store.Get(&hash)
112         if err != nil {
113                 return nil, err
114         }
115         snap := new(Snapshot)
116         if err := json.Unmarshal(data, snap); err != nil {
117                 return nil, err
118         }
119         snap.config = config
120         snap.sigcache = sigcache
121         return snap, nil
122 }
123
124 // store inserts the snapshot into the database.
125 func (s *Snapshot) store(store protocol.Store) error {
126         data, err := json.Marshal(s)
127         if err != nil {
128                 return err
129         }
130         return store.Set(&s.Hash, data)
131 }
132
133 // copy creates a deep copy of the snapshot, though not the individual votes.
134 func (s *Snapshot) copy() *Snapshot {
135         cpy := &Snapshot{
136                 config:          s.config,
137                 sigcache:        s.sigcache,
138                 LCRS:            s.LCRS,
139                 Period:          s.Period,
140                 Number:          s.Number,
141                 ConfirmedNumber: s.ConfirmedNumber,
142                 Hash:            s.Hash,
143                 HistoryHash:     make([]bc.Hash, len(s.HistoryHash)),
144
145                 Signers:       make([]*string, len(s.Signers)),
146                 Votes:         make(map[string]*Vote),
147                 Tally:         make(map[string]uint64),
148                 Voters:        make(map[string]uint64),
149                 Candidates:    make(map[string]uint64),
150                 Punished:      make(map[string]uint64),
151                 Proposals:     make(map[bc.Hash]*Proposal),
152                 Confirmations: make(map[uint64][]string),
153
154                 HeaderTime:    s.HeaderTime,
155                 LoopStartTime: s.LoopStartTime,
156         }
157         copy(cpy.HistoryHash, s.HistoryHash)
158         copy(cpy.Signers, s.Signers)
159         for voter, vote := range s.Votes {
160                 cpy.Votes[voter] = &Vote{
161                         Voter:     vote.Voter,
162                         Candidate: vote.Candidate,
163                         Stake:     vote.Stake,
164                 }
165         }
166         for candidate, tally := range s.Tally {
167                 cpy.Tally[candidate] = tally
168         }
169         for voter, number := range s.Voters {
170                 cpy.Voters[voter] = number
171         }
172         for candidate, state := range s.Candidates {
173                 cpy.Candidates[candidate] = state
174         }
175         for signer, cnt := range s.Punished {
176                 cpy.Punished[signer] = cnt
177         }
178         for blockNumber, confirmers := range s.Confirmations {
179                 cpy.Confirmations[blockNumber] = make([]string, len(confirmers))
180                 copy(cpy.Confirmations[blockNumber], confirmers)
181         }
182         for txHash, proposal := range s.Proposals {
183                 cpy.Proposals[txHash] = proposal.copy()
184         }
185
186         return cpy
187 }
188
189 // apply creates a new authorization snapshot by applying the given headers to
190 // the original one.
191 func (s *Snapshot) apply(headers []*types.BlockHeader) (*Snapshot, error) {
192         // Allow passing in no headers for cleaner code
193         if len(headers) == 0 {
194                 return s, nil
195         }
196         // Sanity check that the headers can be applied
197         for i := 0; i < len(headers)-1; i++ {
198                 if headers[i+1].Height != headers[i].Height+1 {
199                         return nil, errInvalidVotingChain
200                 }
201         }
202         if headers[0].Height != s.Number+1 {
203                 return nil, errInvalidVotingChain
204         }
205         // Iterate through the headers and create a new snapshot
206         snap := s.copy()
207         for _, header := range headers {
208
209                 // Resolve the authorization key and check against signers
210                 coinbase, err := ecrecover(header, s.sigcache, nil)
211                 if err != nil {
212                         return nil, err
213                 }
214
215                 xpub := &chainkd.XPub{}
216                 xpub.UnmarshalText(header.Coinbase)
217                 derivedPK := xpub.PublicKey()
218                 pubHash := crypto.Ripemd160(derivedPK)
219                 address, err := common.NewAddressWitnessPubKeyHash(pubHash, &consensus.ActiveNetParams)
220                 if err != nil {
221                         return nil, err
222                 }
223                 if coinbase != address.EncodeAddress() {
224                         return nil, errUnauthorized
225                 }
226
227                 headerExtra := HeaderExtra{}
228                 if err := json.Unmarshal(header.Extra[extraVanity:len(header.Extra)-extraSeal], &headerExtra); err != nil {
229                         return nil, err
230                 }
231
232                 snap.HeaderTime = header.Timestamp
233                 snap.LoopStartTime = headerExtra.LoopStartTime
234                 snap.Signers = nil
235                 for i := range headerExtra.SignerQueue {
236                         snap.Signers = append(snap.Signers, &headerExtra.SignerQueue[i])
237                 }
238
239                 snap.ConfirmedNumber = headerExtra.ConfirmedBlockNumber
240
241                 if len(snap.HistoryHash) >= int(s.config.MaxSignerCount)*2 {
242                         snap.HistoryHash = snap.HistoryHash[1 : int(s.config.MaxSignerCount)*2]
243                 }
244
245                 snap.HistoryHash = append(snap.HistoryHash, header.Hash())
246
247                 // deal the new confirmation in this block
248                 snap.updateSnapshotByConfirmations(headerExtra.CurrentBlockConfirmations)
249
250                 // deal the new vote from voter
251                 snap.updateSnapshotByVotes(headerExtra.CurrentBlockVotes, header.Height)
252
253                 // deal the voter which balance modified
254                 //snap.updateSnapshotByMPVotes(headerExtra.ModifyPredecessorVotes)
255
256                 // deal the snap related with punished
257                 //snap.updateSnapshotForPunish(headerExtra.SignerMissing, header.Height, header.Coinbase)
258
259                 // deal proposals
260                 snap.updateSnapshotByProposals(headerExtra.CurrentBlockProposals, header.Height)
261
262                 // deal declares
263                 snap.updateSnapshotByDeclares(headerExtra.CurrentBlockDeclares, header.Height)
264
265                 // calculate proposal result
266                 snap.calculateProposalResult(header.Height)
267
268                 // check the len of candidate if not candidateNeedPD
269                 if !candidateNeedPD && (snap.Number+1)%(snap.config.MaxSignerCount*snap.LCRS) == 0 && len(snap.Candidates) > candidateMaxLen {
270                         snap.removeExtraCandidate()
271                 }
272
273         }
274         snap.Number += uint64(len(headers))
275         snap.Hash = headers[len(headers)-1].Hash()
276         fmt.Println("updateSnapshotForExpired before", snap.Tally)
277         snap.updateSnapshotForExpired()
278         fmt.Println("updateSnapshotForExpired after", snap.Tally)
279         err := snap.verifyTallyCnt()
280         if err != nil {
281                 return nil, err
282         }
283         return snap, nil
284 }
285
286 func (s *Snapshot) removeExtraCandidate() {
287         // remove minimum tickets tally beyond candidateMaxLen
288         fmt.Println("removeExtraCandidate")
289         tallySlice := s.buildTallySlice()
290         sort.Sort(TallySlice(tallySlice))
291         if len(tallySlice) > candidateMaxLen {
292                 removeNeedTally := tallySlice[candidateMaxLen:]
293                 for _, tallySlice := range removeNeedTally {
294                         delete(s.Candidates, tallySlice.addr)
295                 }
296         }
297 }
298
299 func (s *Snapshot) verifyTallyCnt() error {
300
301         tallyTarget := make(map[string]uint64)
302         for _, v := range s.Votes {
303                 if _, ok := tallyTarget[v.Candidate]; ok {
304                         tallyTarget[v.Candidate] = tallyTarget[v.Candidate] + v.Stake
305                 } else {
306                         tallyTarget[v.Candidate] = v.Stake
307                 }
308         }
309         for address, tally := range s.Tally {
310                 if targetTally, ok := tallyTarget[address]; ok && targetTally == tally {
311                         continue
312                 } else {
313                         fmt.Println(address, "not find in votes")
314                 }
315         }
316
317         return nil
318 }
319
320 func (s *Snapshot) updateSnapshotByDeclares(declares []Declare, headerHeight uint64) {
321         for _, declare := range declares {
322                 if proposal, ok := s.Proposals[declare.ProposalHash]; ok {
323                         // check the proposal enable status and valid block number
324                         if proposal.ReceivedNumber+proposal.ValidationLoopCnt*s.config.MaxSignerCount < headerHeight || !s.isCandidate(declare.Declarer) {
325                                 continue
326                         }
327                         // check if this signer already declare on this proposal
328                         alreadyDeclare := false
329                         for _, v := range proposal.Declares {
330                                 if v.Declarer == declare.Declarer {
331                                         // this declarer already declare for this proposal
332                                         alreadyDeclare = true
333                                         break
334                                 }
335                         }
336                         if alreadyDeclare {
337                                 continue
338                         }
339                         // add declare to proposal
340                         s.Proposals[declare.ProposalHash].Declares = append(s.Proposals[declare.ProposalHash].Declares,
341                                 &Declare{declare.ProposalHash, declare.Declarer, declare.Decision})
342
343                 }
344         }
345 }
346
347 func (s *Snapshot) calculateProposalResult(headerHeight uint64) {
348
349         for hashKey, proposal := range s.Proposals {
350                 // the result will be calculate at receiverdNumber + vlcnt + 1
351                 if proposal.ReceivedNumber+proposal.ValidationLoopCnt*s.config.MaxSignerCount+1 == headerHeight {
352                         // calculate the current stake of this proposal
353                         judegmentStake := big.NewInt(0)
354                         for _, tally := range s.Tally {
355                                 judegmentStake.Add(judegmentStake, new(big.Int).SetUint64(tally))
356                         }
357                         judegmentStake.Mul(judegmentStake, big.NewInt(2))
358                         judegmentStake.Div(judegmentStake, big.NewInt(3))
359                         // calculate declare stake
360                         yesDeclareStake := big.NewInt(0)
361                         for _, declare := range proposal.Declares {
362                                 if declare.Decision {
363                                         if _, ok := s.Tally[declare.Declarer]; ok {
364                                                 yesDeclareStake.Add(yesDeclareStake, new(big.Int).SetUint64(s.Tally[declare.Declarer]))
365                                         }
366                                 }
367                         }
368                         if yesDeclareStake.Cmp(judegmentStake) > 0 {
369                                 // process add candidate
370                                 switch proposal.ProposalType {
371                                 case proposalTypeCandidateAdd:
372                                         if candidateNeedPD {
373                                                 s.Candidates[s.Proposals[hashKey].Candidate] = candidateStateNormal
374                                         }
375                                 case proposalTypeCandidateRemove:
376                                         if _, ok := s.Candidates[proposal.Candidate]; ok && candidateNeedPD {
377                                                 delete(s.Candidates, proposal.Candidate)
378                                         }
379                                 case proposalTypeMinerRewardDistributionModify:
380                                         minerRewardPerThousand = s.Proposals[hashKey].MinerRewardPerThousand
381
382                                 }
383                         }
384
385                 }
386
387         }
388
389 }
390
391 func (s *Snapshot) updateSnapshotByProposals(proposals []Proposal, headerHeight uint64) {
392         for _, proposal := range proposals {
393                 proposal.ReceivedNumber = headerHeight
394                 s.Proposals[proposal.Hash] = &proposal
395         }
396 }
397
398 func (s *Snapshot) updateSnapshotForExpired() {
399
400         // deal the expired vote
401         var expiredVotes []*Vote
402         for voterAddress, voteNumber := range s.Voters {
403                 if s.Number-voteNumber > s.config.Epoch {
404                         // clear the vote
405                         if expiredVote, ok := s.Votes[voterAddress]; ok {
406                                 expiredVotes = append(expiredVotes, expiredVote)
407                         }
408                 }
409         }
410         // remove expiredVotes only enough voters left
411         if uint64(len(s.Voters)-len(expiredVotes)) >= s.config.MaxSignerCount {
412                 for _, expiredVote := range expiredVotes {
413                         s.Tally[expiredVote.Candidate] -= expiredVote.Stake
414                         // TODO
415                         /*
416                                 if s.Tally[expiredVote.Candidate] == 0 {
417                                         delete(s.Tally, expiredVote.Candidate)
418                                 }
419                                 delete(s.Votes, expiredVote.Voter)
420                                 delete(s.Voters, expiredVote.Voter)
421                         */
422                 }
423         }
424
425         // deal the expired confirmation
426         for blockNumber := range s.Confirmations {
427                 if s.Number-blockNumber > s.config.MaxSignerCount {
428                         delete(s.Confirmations, blockNumber)
429                 }
430         }
431
432         // TODO
433         /*
434                 // remove 0 stake tally
435                 for address, tally := range s.Tally {
436                         if tally <= 0 {
437                                 delete(s.Tally, address)
438                         }
439                 }
440         */
441 }
442
443 func (s *Snapshot) updateSnapshotByConfirmations(confirmations []Confirmation) {
444         for _, confirmation := range confirmations {
445                 _, ok := s.Confirmations[confirmation.BlockNumber]
446                 if !ok {
447                         s.Confirmations[confirmation.BlockNumber] = []string{}
448                 }
449                 addConfirmation := true
450                 for _, address := range s.Confirmations[confirmation.BlockNumber] {
451                         if confirmation.Signer == address {
452                                 addConfirmation = false
453                                 break
454                         }
455                 }
456                 if addConfirmation == true {
457                         s.Confirmations[confirmation.BlockNumber] = append(s.Confirmations[confirmation.BlockNumber], confirmation.Signer)
458                 }
459         }
460 }
461
462 func (s *Snapshot) updateSnapshotByVotes(votes []Vote, headerHeight uint64) {
463         fmt.Println("updateSnapshotByVotes start")
464         for _, vote := range votes {
465                 // update Votes, Tally, Voters data
466                 if lastVote, ok := s.Votes[vote.Voter]; ok {
467                         fmt.Println("lastVote.Candidate:", lastVote.Candidate)
468                         fmt.Println("lastVote.Stake:", lastVote.Stake)
469                         fmt.Println(s.Tally[lastVote.Candidate]-lastVote.Stake, s.Tally[lastVote.Candidate])
470                         s.Tally[lastVote.Candidate] = s.Tally[lastVote.Candidate] - lastVote.Stake
471                         fmt.Println(s.Tally)
472                         fmt.Println(s.Tally[lastVote.Candidate])
473                 }
474                 if _, ok := s.Tally[vote.Candidate]; ok {
475                         fmt.Println("vote.Candidate:", vote.Candidate)
476                         fmt.Println("vote.Stake:", vote.Stake)
477                         s.Tally[vote.Candidate] = s.Tally[vote.Candidate] + vote.Stake
478                 } else {
479                         fmt.Println("111 vote.Candidate:", vote.Candidate)
480                         fmt.Println("111 vote.Stake:", vote.Stake)
481                         s.Tally[vote.Candidate] = vote.Stake
482                         if !candidateNeedPD {
483                                 s.Candidates[vote.Candidate] = candidateStateNormal
484                         }
485                 }
486                 s.Votes[vote.Voter] = &Vote{vote.Voter, vote.Candidate, vote.Stake}
487                 s.Voters[vote.Voter] = headerHeight
488         }
489         fmt.Println(votes)
490         fmt.Println(s.Tally)
491         fmt.Println("updateSnapshotByVotes end")
492 }
493
494 func (s *Snapshot) updateSnapshotByMPVotes(votes []Vote) {
495         fmt.Println("8888888888888888888888888888888888")
496         fmt.Println(s.Tally)
497         for _, txVote := range votes {
498                 fmt.Println("updateSnapshotByMPVotesupdateSnapshotByMPVotesupdateSnapshotByMPVotes")
499                 if lastVote, ok := s.Votes[txVote.Voter]; ok {
500                         fmt.Println("txVote.Voter:", txVote.Voter)
501                         fmt.Println("lastVote.Candidate:", lastVote.Candidate, ",lastVote.Stake:", lastVote.Stake)
502                         fmt.Println("txVote.Stake:", txVote.Stake)
503                         s.Tally[lastVote.Candidate] = s.Tally[lastVote.Candidate] - lastVote.Stake
504                         s.Tally[lastVote.Candidate] = s.Tally[lastVote.Candidate] + txVote.Stake
505                         s.Votes[txVote.Voter] = &Vote{Voter: txVote.Voter, Candidate: lastVote.Candidate, Stake: txVote.Stake}
506                         fmt.Println(txVote.Voter, lastVote.Candidate, txVote.Stake)
507                         // do not modify header number of snap.Voters
508                 }
509         }
510         fmt.Println(s.Tally)
511         fmt.Println("999999999999999999999999999999999")
512 }
513
514 func (s *Snapshot) updateSnapshotForPunish(signerMissing []string, headerNumber uint64, coinbase string) {
515         // set punished count to half of origin in Epoch
516         /*
517                 if headerNumber.Uint64()%s.config.Epoch == 0 {
518                         for bePublished := range s.Punished {
519                                 if count := s.Punished[bePublished] / 2; count > 0 {
520                                         s.Punished[bePublished] = count
521                                 } else {
522                                         delete(s.Punished, bePublished)
523                                 }
524                         }
525                 }
526         */
527         // punish the missing signer
528         for _, signerMissing := range signerMissing {
529                 if _, ok := s.Punished[signerMissing]; ok {
530                         s.Punished[signerMissing] += missingPublishCredit
531                 } else {
532                         s.Punished[signerMissing] = missingPublishCredit
533                 }
534         }
535         // reduce the punish of sign signer
536         if _, ok := s.Punished[coinbase]; ok {
537
538                 if s.Punished[coinbase] > signRewardCredit {
539                         s.Punished[coinbase] -= signRewardCredit
540                 } else {
541                         delete(s.Punished, coinbase)
542                 }
543         }
544         // reduce the punish for all punished
545         for signerEach := range s.Punished {
546                 if s.Punished[signerEach] > autoRewardCredit {
547                         s.Punished[signerEach] -= autoRewardCredit
548                 } else {
549                         delete(s.Punished, signerEach)
550                 }
551         }
552 }
553
554 // inturn returns if a signer at a given block height is in-turn or not.
555 func (s *Snapshot) inturn(signer string, headerTime uint64) bool {
556         for _, addr := range s.Signers {
557                 fmt.Println("inturn [addr]:", *addr)
558         }
559         fmt.Println("signer:", signer)
560         // if all node stop more than period of one loop
561         loopIndex := int((headerTime-s.LoopStartTime)/s.config.Period) % len(s.Signers)
562         fmt.Println(headerTime-s.LoopStartTime, s.config.Period, len(s.Signers), loopIndex)
563         if loopIndex >= len(s.Signers) {
564                 return false
565         } else if *s.Signers[loopIndex] != signer {
566                 return false
567
568         }
569         return true
570 }
571
572 // check if address belong to voter
573 func (s *Snapshot) isVoter(address string) bool {
574         if _, ok := s.Voters[address]; ok {
575                 return true
576         }
577         return false
578 }
579
580 // check if address belong to candidate
581 func (s *Snapshot) isCandidate(address string) bool {
582         if _, ok := s.Candidates[address]; ok {
583                 return true
584         }
585         return false
586 }
587
588 // get last block number meet the confirm condition
589 func (s *Snapshot) getLastConfirmedBlockNumber(confirmations []Confirmation) *big.Int {
590
591         cpyConfirmations := make(map[uint64][]string)
592         for blockNumber, confirmers := range s.Confirmations {
593                 cpyConfirmations[blockNumber] = make([]string, len(confirmers))
594                 copy(cpyConfirmations[blockNumber], confirmers)
595         }
596         // update confirmation into snapshot
597         for _, confirmation := range confirmations {
598                 _, ok := cpyConfirmations[confirmation.BlockNumber]
599                 if !ok {
600                         cpyConfirmations[confirmation.BlockNumber] = []string{}
601                 }
602                 addConfirmation := true
603                 for _, address := range cpyConfirmations[confirmation.BlockNumber] {
604                         if confirmation.Signer == address {
605                                 addConfirmation = false
606                                 break
607                         }
608                 }
609                 if addConfirmation == true {
610
611                         cpyConfirmations[confirmation.BlockNumber] = append(cpyConfirmations[confirmation.BlockNumber], confirmation.Signer)
612                 }
613         }
614
615         i := s.Number
616         for ; i > s.Number-s.config.MaxSignerCount*2/3+1; i-- {
617                 if confirmers, ok := cpyConfirmations[i]; ok {
618                         if len(confirmers) > int(s.config.MaxSignerCount*2/3) {
619                                 return big.NewInt(int64(i))
620                         }
621                 }
622         }
623         return big.NewInt(int64(i))
624 }