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"
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
37 var errIncorrectTallyCount = errors.New("incorrect tally count")
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
60 // newSnapshot creates a new snapshot with the specified startup parameters. only ever use if for
62 func newSnapshot(config *config.DposConfig, sigcache *lru.ARCCache, hash bc.Hash, votes []*Vote, lcrs uint64) *Snapshot {
68 Period: config.Period,
72 HistoryHash: []bc.Hash{},
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,
84 snap.HistoryHash = append(snap.HistoryHash, hash)
85 for _, vote := range votes {
86 // init Votes from each vote
87 snap.Votes[vote.Voter] = vote
89 _, ok := snap.Tally[vote.Candidate]
91 snap.Tally[vote.Candidate] = 0
93 fmt.Println("newSnapshot", vote.Candidate, vote.Stake)
94 fmt.Println(snap.Tally)
95 snap.Tally[vote.Candidate] += vote.Stake
97 snap.Voters[vote.Voter] = 0 // block height is 0 , vote in genesis block
99 snap.Candidates[vote.Voter] = candidateStateNormal
102 for i := 0; i < int(config.MaxSignerCount); i++ {
103 snap.Signers = append(snap.Signers, &config.SelfVoteSigners[i%len(config.SelfVoteSigners)])
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)
115 snap := new(Snapshot)
116 if err := json.Unmarshal(data, snap); err != nil {
120 snap.sigcache = sigcache
124 // store inserts the snapshot into the database.
125 func (s *Snapshot) store(store protocol.Store) error {
126 data, err := json.Marshal(s)
130 return store.Set(&s.Hash, data)
133 // copy creates a deep copy of the snapshot, though not the individual votes.
134 func (s *Snapshot) copy() *Snapshot {
137 sigcache: s.sigcache,
141 ConfirmedNumber: s.ConfirmedNumber,
143 HistoryHash: make([]bc.Hash, len(s.HistoryHash)),
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),
154 HeaderTime: s.HeaderTime,
155 LoopStartTime: s.LoopStartTime,
157 copy(cpy.HistoryHash, s.HistoryHash)
158 copy(cpy.Signers, s.Signers)
159 for voter, vote := range s.Votes {
160 cpy.Votes[voter] = &Vote{
162 Candidate: vote.Candidate,
166 for candidate, tally := range s.Tally {
167 cpy.Tally[candidate] = tally
169 for voter, number := range s.Voters {
170 cpy.Voters[voter] = number
172 for candidate, state := range s.Candidates {
173 cpy.Candidates[candidate] = state
175 for signer, cnt := range s.Punished {
176 cpy.Punished[signer] = cnt
178 for blockNumber, confirmers := range s.Confirmations {
179 cpy.Confirmations[blockNumber] = make([]string, len(confirmers))
180 copy(cpy.Confirmations[blockNumber], confirmers)
182 for txHash, proposal := range s.Proposals {
183 cpy.Proposals[txHash] = proposal.copy()
189 // apply creates a new authorization snapshot by applying the given headers to
191 func (s *Snapshot) apply(headers []*types.BlockHeader) (*Snapshot, error) {
192 // Allow passing in no headers for cleaner code
193 if len(headers) == 0 {
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
202 if headers[0].Height != s.Number+1 {
203 return nil, errInvalidVotingChain
205 // Iterate through the headers and create a new snapshot
207 for _, header := range headers {
209 // Resolve the authorization key and check against signers
210 coinbase, err := ecrecover(header, s.sigcache, nil)
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)
223 if coinbase != address.EncodeAddress() {
224 return nil, errUnauthorized
227 headerExtra := HeaderExtra{}
228 if err := json.Unmarshal(header.Extra[extraVanity:len(header.Extra)-extraSeal], &headerExtra); err != nil {
232 snap.HeaderTime = header.Timestamp
233 snap.LoopStartTime = headerExtra.LoopStartTime
235 for i := range headerExtra.SignerQueue {
236 snap.Signers = append(snap.Signers, &headerExtra.SignerQueue[i])
239 snap.ConfirmedNumber = headerExtra.ConfirmedBlockNumber
241 if len(snap.HistoryHash) >= int(s.config.MaxSignerCount)*2 {
242 snap.HistoryHash = snap.HistoryHash[1 : int(s.config.MaxSignerCount)*2]
245 snap.HistoryHash = append(snap.HistoryHash, header.Hash())
247 // deal the new confirmation in this block
248 snap.updateSnapshotByConfirmations(headerExtra.CurrentBlockConfirmations)
250 // deal the new vote from voter
251 snap.updateSnapshotByVotes(headerExtra.CurrentBlockVotes, header.Height)
253 // deal the voter which balance modified
254 //snap.updateSnapshotByMPVotes(headerExtra.ModifyPredecessorVotes)
256 // deal the snap related with punished
257 //snap.updateSnapshotForPunish(headerExtra.SignerMissing, header.Height, header.Coinbase)
260 snap.updateSnapshotByProposals(headerExtra.CurrentBlockProposals, header.Height)
263 snap.updateSnapshotByDeclares(headerExtra.CurrentBlockDeclares, header.Height)
265 // calculate proposal result
266 snap.calculateProposalResult(header.Height)
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()
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()
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)
299 func (s *Snapshot) verifyTallyCnt() error {
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
306 tallyTarget[v.Candidate] = v.Stake
309 for address, tally := range s.Tally {
310 if targetTally, ok := tallyTarget[address]; ok && targetTally == tally {
313 fmt.Println(address, "not find in votes")
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) {
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
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})
347 func (s *Snapshot) calculateProposalResult(headerHeight uint64) {
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))
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]))
368 if yesDeclareStake.Cmp(judegmentStake) > 0 {
369 // process add candidate
370 switch proposal.ProposalType {
371 case proposalTypeCandidateAdd:
373 s.Candidates[s.Proposals[hashKey].Candidate] = candidateStateNormal
375 case proposalTypeCandidateRemove:
376 if _, ok := s.Candidates[proposal.Candidate]; ok && candidateNeedPD {
377 delete(s.Candidates, proposal.Candidate)
379 case proposalTypeMinerRewardDistributionModify:
380 minerRewardPerThousand = s.Proposals[hashKey].MinerRewardPerThousand
391 func (s *Snapshot) updateSnapshotByProposals(proposals []Proposal, headerHeight uint64) {
392 for _, proposal := range proposals {
393 proposal.ReceivedNumber = headerHeight
394 s.Proposals[proposal.Hash] = &proposal
398 func (s *Snapshot) updateSnapshotForExpired() {
400 // deal the expired vote
401 var expiredVotes []*Vote
402 for voterAddress, voteNumber := range s.Voters {
403 if s.Number-voteNumber > s.config.Epoch {
405 if expiredVote, ok := s.Votes[voterAddress]; ok {
406 expiredVotes = append(expiredVotes, expiredVote)
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
416 if s.Tally[expiredVote.Candidate] == 0 {
417 delete(s.Tally, expiredVote.Candidate)
419 delete(s.Votes, expiredVote.Voter)
420 delete(s.Voters, expiredVote.Voter)
425 // deal the expired confirmation
426 for blockNumber := range s.Confirmations {
427 if s.Number-blockNumber > s.config.MaxSignerCount {
428 delete(s.Confirmations, blockNumber)
434 // remove 0 stake tally
435 for address, tally := range s.Tally {
437 delete(s.Tally, address)
443 func (s *Snapshot) updateSnapshotByConfirmations(confirmations []Confirmation) {
444 for _, confirmation := range confirmations {
445 _, ok := s.Confirmations[confirmation.BlockNumber]
447 s.Confirmations[confirmation.BlockNumber] = []string{}
449 addConfirmation := true
450 for _, address := range s.Confirmations[confirmation.BlockNumber] {
451 if confirmation.Signer == address {
452 addConfirmation = false
456 if addConfirmation == true {
457 s.Confirmations[confirmation.BlockNumber] = append(s.Confirmations[confirmation.BlockNumber], confirmation.Signer)
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
472 fmt.Println(s.Tally[lastVote.Candidate])
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
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
486 s.Votes[vote.Voter] = &Vote{vote.Voter, vote.Candidate, vote.Stake}
487 s.Voters[vote.Voter] = headerHeight
491 fmt.Println("updateSnapshotByVotes end")
494 func (s *Snapshot) updateSnapshotByMPVotes(votes []Vote) {
495 fmt.Println("8888888888888888888888888888888888")
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
511 fmt.Println("999999999999999999999999999999999")
514 func (s *Snapshot) updateSnapshotForPunish(signerMissing []string, headerNumber uint64, coinbase string) {
515 // set punished count to half of origin in Epoch
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
522 delete(s.Punished, bePublished)
527 // punish the missing signer
528 for _, signerMissing := range signerMissing {
529 if _, ok := s.Punished[signerMissing]; ok {
530 s.Punished[signerMissing] += missingPublishCredit
532 s.Punished[signerMissing] = missingPublishCredit
535 // reduce the punish of sign signer
536 if _, ok := s.Punished[coinbase]; ok {
538 if s.Punished[coinbase] > signRewardCredit {
539 s.Punished[coinbase] -= signRewardCredit
541 delete(s.Punished, coinbase)
544 // reduce the punish for all punished
545 for signerEach := range s.Punished {
546 if s.Punished[signerEach] > autoRewardCredit {
547 s.Punished[signerEach] -= autoRewardCredit
549 delete(s.Punished, signerEach)
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)
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) {
565 } else if *s.Signers[loopIndex] != signer {
572 // check if address belong to voter
573 func (s *Snapshot) isVoter(address string) bool {
574 if _, ok := s.Voters[address]; ok {
580 // check if address belong to candidate
581 func (s *Snapshot) isCandidate(address string) bool {
582 if _, ok := s.Candidates[address]; ok {
588 // get last block number meet the confirm condition
589 func (s *Snapshot) getLastConfirmedBlockNumber(confirmations []Confirmation) *big.Int {
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)
596 // update confirmation into snapshot
597 for _, confirmation := range confirmations {
598 _, ok := cpyConfirmations[confirmation.BlockNumber]
600 cpyConfirmations[confirmation.BlockNumber] = []string{}
602 addConfirmation := true
603 for _, address := range cpyConfirmations[confirmation.BlockNumber] {
604 if confirmation.Signer == address {
605 addConfirmation = false
609 if addConfirmation == true {
611 cpyConfirmations[confirmation.BlockNumber] = append(cpyConfirmations[confirmation.BlockNumber], confirmation.Signer)
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))
623 return big.NewInt(int64(i))