github.com/insight-chain/inb-go@v1.1.3-0.20191221022159-da049980ae38/consensus/vdpos/signers.go (about) 1 // Copyright 2019 The inb-go Authors 2 // This file is part of the inb-go library. 3 // 4 // The inb-go library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The inb-go library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the inb-go library. If not, see <http://www.gnu.org/licenses/>. 16 17 // Package vdpos implements the delegated-proof-of-stake consensus engine. 18 package vdpos 19 20 import ( 21 "bytes" 22 "encoding/binary" 23 "fmt" 24 "github.com/insight-chain/inb-go/common" 25 "github.com/insight-chain/inb-go/core/types" 26 "github.com/insight-chain/inb-go/crypto" 27 "github.com/insight-chain/inb-go/log" 28 "github.com/insight-chain/inb-go/params" 29 "github.com/insight-chain/inb-go/rlp" 30 "github.com/insight-chain/inb-go/trie" 31 "math/big" 32 "math/rand" 33 "sort" 34 ) 35 36 const ( 37 candidateMaxLen = 50 //max lenth of candidate 38 defaultFullCredit = 1 //default rate of stake 39 ) 40 41 type TallyItem struct { 42 addr common.Address 43 votesValue *big.Int 44 } 45 type TallySlice []TallyItem 46 47 func (s TallySlice) Len() int { return len(s) } 48 func (s TallySlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 49 func (s TallySlice) Less(i, j int) bool { 50 isLess := s[i].votesValue.Cmp(s[j].votesValue) 51 if isLess > 0 { 52 return true 53 54 } else if isLess < 0 { 55 return false 56 } 57 return bytes.Compare(s[i].addr.Bytes(), s[j].addr.Bytes()) > 0 58 } 59 60 // verify the SignersPool base on block hash 61 func (s *SnapContext) verifySignersPool(signersPool []common.Address) error { 62 63 if len(signersPool) > int(s.config.MaxSignerCount) { 64 return errInvalidSignersPool 65 } 66 sq, err := s.createSignersPool() 67 if err != nil { 68 return err 69 } 70 if len(sq) == 0 || len(sq) != len(signersPool) { 71 return errInvalidSignersPool 72 } 73 for i, signer := range signersPool { 74 if signer != sq[i] { 75 return errInvalidSignersPool 76 } 77 } 78 return nil 79 } 80 81 // build TallySlice from TallyTrie 82 func (s *SnapContext) buildTallySlice() TallySlice { 83 var tallySlice TallySlice 84 tallTrie := s.VdposContext.TallyTrie() 85 // use trie iterator 86 tallyIterator := trie.NewIterator(tallTrie.PrefixIterator(nil)) 87 existTally := tallyIterator.Next() 88 if !existTally { 89 return nil 90 } 91 for existTally { 92 tallyRLP := tallyIterator.Value 93 tally := new(types.Tally) 94 if err := rlp.DecodeBytes(tallyRLP, tally); err != nil { 95 log.Error("Failed to decode tally") 96 return nil 97 } 98 address := tally.Address 99 tlsv := tally.TimeLimitedStakingValue 100 if tlsv.Cmp(new(big.Int).Mul(big.NewInt(1000000), big.NewInt(params.Inber))) >= 0 { 101 votesValue := tally.VotesValue 102 tallySlice = append(tallySlice, TallyItem{address, new(big.Int).Mul(votesValue, big.NewInt(defaultFullCredit))}) 103 } 104 existTally = tallyIterator.Next() 105 } 106 107 return tallySlice 108 } 109 110 func (s *SnapContext) createSignersPool() ([]common.Address, error) { 111 // check up if we really need to create signersPool 112 if (s.Number+1)%(s.config.MaxSignerCount*s.config.SignerBlocks) != 0 { 113 return nil, errCreateSignersPoolNotAllowed 114 } 115 116 var topStakeAddress []common.Address 117 var tallySliceOrder TallySlice 118 119 // use parent block hash as seed so that every signers can use the same one 120 seed := int64(binary.LittleEndian.Uint32(crypto.Keccak512(s.ParentHash.Bytes()))) 121 122 // only recalculate signers from to tally per defaultLoopCntRecalculateSigners loop, 123 // other loop end just random the order of signers base on parent block hash 124 if (s.Number+1)%(s.config.MaxSignerCount*s.config.SignerBlocks*s.config.LoopCntRecalculate) == 0 { 125 tallySlice := s.buildTallySlice() 126 if tallySlice != nil { 127 sort.Sort(TallySlice(tallySlice)) 128 // remove minimum tickets tally beyond candidateMaxLen 129 s.removeExtraCandidate(&tallySlice) 130 for _, item := range tallySlice { 131 log.Debug(item.addr.Hex()) 132 } 133 poolLength := int(s.config.MaxSignerCount) 134 if poolLength > len(tallySlice) { 135 poolLength = len(tallySlice) 136 } 137 tallySliceOrder = tallySlice[:poolLength] 138 s.random(tallySliceOrder, seed) 139 for _, itemx := range tallySliceOrder { 140 log.Debug(itemx.addr.Hex()) 141 } 142 } 143 } else { 144 if s.SignersPool == nil { 145 return nil, errCreateSignersPoolNotAllowed 146 } 147 tallTrie := s.VdposContext.TallyTrie() 148 for _, signer := range s.SignersPool { 149 tallyRLP := tallTrie.Get(signer[:]) 150 if tallyRLP != nil { 151 tally := new(types.Tally) 152 if err := rlp.DecodeBytes(tallyRLP, tally); err != nil { 153 return nil, fmt.Errorf("failed to decode tally: %s", err) 154 } 155 tallyItem := TallyItem{ 156 addr: tally.Address, 157 votesValue: tally.VotesValue, 158 } 159 tallySliceOrder = append(tallySliceOrder, tallyItem) 160 } 161 } 162 s.random(tallySliceOrder, seed) 163 for _, itemx := range tallySliceOrder { 164 log.Debug(itemx.addr.Hex()) 165 } 166 } 167 168 // Set the top signers in random order base on parent block hash 169 if len(tallySliceOrder) == 0 { 170 //return nil, errSignersPoolEmpty 171 log.Error("signers pool is empty when createSignersPool") 172 return s.SignersPool, nil 173 } 174 for i := 0; i < int(s.config.MaxSignerCount); i++ { 175 topStakeAddress = append(topStakeAddress, tallySliceOrder[i%len(tallySliceOrder)].addr) 176 } 177 178 return topStakeAddress, nil 179 180 } 181 182 func (s *SnapContext) random(arr TallySlice, seed int64) { 183 if len(arr) <= 0 { 184 return 185 } 186 rand.Seed(seed) 187 for i := len(arr) - 1; i >= 0; i-- { 188 num := rand.Intn(len(arr)) 189 arr[i], arr[num] = arr[num], arr[i] 190 } 191 return 192 } 193 194 // inturn returns if a signer at a given block height is in-turn or not. 195 func (s *SnapContext) inturn(signer common.Address, header *types.Header, parent *types.Header) bool { 196 if header.Number.Uint64() == 1 { 197 parent = header 198 } 199 parentExtra := HeaderExtra{} 200 err := decodeHeaderExtra(parent.Extra[extraVanity:len(parent.Extra)-extraSeal], &parentExtra) 201 if err != nil { 202 log.Error("Fail to decode header", "err", err) 203 return false 204 } 205 //signers, err := s.VdposContext.GetSignersFromTrie() 206 //if err != nil { 207 // return false 208 //} 209 210 headerTime := header.Time.Uint64() 211 loopStartTime := parentExtra.LoopStartTime 212 //loopStartTime := parent.LoopStartTime 213 signers := parentExtra.SignersPool 214 if signersCount := len(signers); signersCount > 0 { 215 // handle config.Period != config.SignerPeriod 216 if loopIndex := ((headerTime - loopStartTime) / (s.config.Period*(s.config.SignerBlocks-1) + s.config.SignerPeriod)) % uint64(signersCount); signers[loopIndex] == signer { 217 return true 218 } 219 } 220 return false 221 } 222 223 func (s *SnapContext) removeExtraCandidate(tally *TallySlice) { 224 tallySlice := *tally 225 if len(tallySlice) > candidateMaxLen { 226 needRemoveTally := tallySlice[candidateMaxLen:] 227 for _, tallySlice := range needRemoveTally { 228 s.VdposContext.TallyTrie().Delete(tallySlice.addr[:]) 229 } 230 } 231 }