github.com/aergoio/aergo@v1.3.1/contract/system/vote.go (about) 1 /** 2 * @file 3 * @copyright defined in aergo/LICENSE.txt 4 */ 5 6 package system 7 8 import ( 9 "bytes" 10 "encoding/binary" 11 "encoding/json" 12 "math/big" 13 14 "github.com/aergoio/aergo/internal/enc" 15 "github.com/aergoio/aergo/state" 16 "github.com/aergoio/aergo/types" 17 "github.com/mr-tron/base58" 18 ) 19 20 var defaultBpCount int 21 22 var voteKey = []byte("vote") 23 var sortKey = []byte("sort") 24 25 const PeerIDLength = 39 26 27 const VotingDelay = 60 * 60 * 24 //block interval 28 //const VotingDelay = 5 29 30 var defaultVoteKey = []byte(types.VoteBP)[2:] 31 32 func voting(txBody *types.TxBody, sender, receiver *state.V, scs *state.ContractState, 33 blockNo types.BlockNo, context *SystemContext) (*types.Event, error) { 34 key := []byte(context.Call.Name)[2:] 35 oldvote := context.Vote 36 staked := context.Staked 37 //update block number 38 staked.When = blockNo 39 err := setStaking(scs, sender.ID(), staked) 40 if err != nil { 41 return nil, err 42 } 43 44 voteResult, err := loadVoteResult(scs, key) 45 if err != nil { 46 return nil, err 47 } 48 49 err = voteResult.SubVote(oldvote) 50 if err != nil { 51 return nil, err 52 } 53 54 if staked.GetAmountBigInt().Cmp(new(big.Int).SetUint64(0)) == 0 { 55 return nil, types.ErrMustStakeBeforeVote 56 } 57 vote := &types.Vote{Amount: staked.GetAmount()} 58 args, err := json.Marshal(context.Call.Args) 59 if err != nil { 60 return nil, err 61 } 62 var candidates []byte 63 if bytes.Equal(key, defaultVoteKey) { 64 for _, v := range context.Call.Args { 65 candidate, _ := base58.Decode(v.(string)) 66 candidates = append(candidates, candidate...) 67 } 68 vote.Candidate = candidates 69 } else { 70 vote.Candidate = args 71 } 72 73 err = setVote(scs, key, sender.ID(), vote) 74 if err != nil { 75 return nil, err 76 } 77 err = voteResult.AddVote(vote) 78 if err != nil { 79 return nil, err 80 } 81 82 err = voteResult.Sync(scs) 83 if err != nil { 84 return nil, err 85 } 86 return &types.Event{ 87 ContractAddress: receiver.ID(), 88 EventIdx: 0, 89 EventName: context.Call.Name[2:], 90 JsonArgs: `{"who":"` + 91 types.EncodeAddress(txBody.Account) + 92 `", "vote":` + string(args) + `}`, 93 }, nil 94 } 95 96 func refreshAllVote(txBody *types.TxBody, scs *state.ContractState, 97 context *SystemContext) error { 98 account := context.Sender.ID() 99 staked := context.Staked 100 stakedAmount := new(big.Int).SetBytes(staked.Amount) 101 for _, keystr := range types.AllVotes { 102 key := []byte(keystr[2:]) 103 oldvote, err := getVote(scs, key, account) 104 if err != nil { 105 return err 106 } 107 if oldvote.Amount == nil || 108 new(big.Int).SetBytes(oldvote.Amount).Cmp(stakedAmount) <= 0 { 109 continue 110 } 111 voteResult, err := loadVoteResult(scs, key) 112 if err != nil { 113 return err 114 } 115 if err = voteResult.SubVote(oldvote); err != nil { 116 return err 117 } 118 oldvote.Amount = staked.GetAmount() 119 if err = setVote(scs, key, account, oldvote); err != nil { 120 return err 121 } 122 if err = voteResult.AddVote(oldvote); err != nil { 123 return err 124 } 125 if err = voteResult.Sync(scs); err != nil { 126 return err 127 } 128 } 129 return nil 130 } 131 132 //GetVote return amount, to, err 133 func GetVote(scs *state.ContractState, voter []byte, title []byte) (*types.Vote, error) { 134 return getVote(scs, title, voter) 135 } 136 137 func getVote(scs *state.ContractState, key, voter []byte) (*types.Vote, error) { 138 dataKey := append(append(voteKey, key...), voter...) 139 data, err := scs.GetData(dataKey) 140 if err != nil { 141 return nil, err 142 } 143 var vote types.Vote 144 if len(data) != 0 { 145 if bytes.Equal(key, defaultVoteKey) { 146 return deserializeVote(data), nil 147 } else { 148 return deserializeVoteEx(data), nil 149 } 150 } 151 152 return &vote, nil 153 } 154 155 func setVote(scs *state.ContractState, key, voter []byte, vote *types.Vote) error { 156 dataKey := append(append(voteKey, key...), voter...) 157 if bytes.Equal(key, defaultVoteKey) { 158 return scs.SetData(dataKey, serializeVote(vote)) 159 } else { 160 return scs.SetData(dataKey, serializeVoteEx(vote)) 161 } 162 } 163 164 // BuildOrderedCandidates returns a candidate list ordered by votes.xs 165 func BuildOrderedCandidates(vote map[string]*big.Int) []string { 166 // TODO: cleanup 167 voteResult := newVoteResult(defaultVoteKey) 168 voteResult.rmap = vote 169 l := voteResult.buildVoteList() 170 bps := make([]string, 0, len(l.Votes)) 171 for _, v := range l.Votes { 172 bp := enc.ToString(v.Candidate) 173 bps = append(bps, bp) 174 } 175 return bps 176 } 177 178 // AccountStateReader is an interface for getting a system account state. 179 type AccountStateReader interface { 180 GetSystemAccountState() (*state.ContractState, error) 181 } 182 183 // GetVoteResult returns the top n voting result from the system account state. 184 func GetVoteResult(ar AccountStateReader, id []byte, n int) (*types.VoteList, error) { 185 scs, err := ar.GetSystemAccountState() 186 if err != nil { 187 return nil, err 188 } 189 return getVoteResult(scs, id, n) 190 } 191 192 // InitDefaultBpCount sets defaultBpCount to bpCount. 193 // 194 // Caution: This function must be called only once before all the aergosvr 195 // services start. 196 func InitDefaultBpCount(bpCount int) { 197 // Ensure that it is not modified after it is initialized. 198 if defaultBpCount > 0 { 199 return 200 } 201 defaultBpCount = bpCount 202 } 203 204 func getDefaultBpCount() int { 205 return defaultBpCount 206 } 207 208 // GetRankers returns the IDs of the top n rankers. 209 func GetRankers(ar AccountStateReader) ([]string, error) { 210 n := getDefaultBpCount() 211 212 vl, err := GetVoteResult(ar, defaultVoteKey, n) 213 if err != nil { 214 return nil, err 215 } 216 217 bps := make([]string, 0, n) 218 for _, v := range vl.Votes { 219 bps = append(bps, enc.ToString(v.Candidate)) 220 } 221 222 return bps, nil 223 } 224 225 func serializeVoteList(vl *types.VoteList, ex bool) []byte { 226 var data []byte 227 for _, v := range vl.GetVotes() { 228 var serialized []byte 229 if ex { 230 serialized = serializeVoteEx(v) 231 } else { 232 serialized = serializeVote(v) 233 } 234 vsize := make([]byte, 8) 235 binary.LittleEndian.PutUint64(vsize, uint64(len(serialized))) 236 data = append(data, vsize...) 237 data = append(data, serialized...) 238 } 239 return data 240 } 241 242 func serializeVote(v *types.Vote) []byte { 243 var ret []byte 244 if v != nil { 245 ret = append(ret, v.GetCandidate()...) 246 ret = append(ret, v.GetAmount()...) 247 } 248 return ret 249 } 250 251 func serializeVoteEx(v *types.Vote) []byte { 252 var ret []byte 253 if v != nil { 254 size := make([]byte, 8) 255 binary.LittleEndian.PutUint64(size, uint64(len(v.Candidate))) 256 ret = append(ret, size...) 257 ret = append(ret, v.GetCandidate()...) 258 ret = append(ret, v.GetAmount()...) 259 } 260 return ret 261 } 262 263 func deserializeVote(data []byte) *types.Vote { 264 pos := len(data) % PeerIDLength 265 candidate := data[:len(data)-pos] 266 amount := data[len(data)-pos:] 267 if len(candidate)%PeerIDLength != 0 { 268 panic("voting data corruption") 269 } 270 return &types.Vote{Amount: amount, Candidate: candidate} 271 } 272 273 func deserializeVoteEx(data []byte) *types.Vote { 274 size := int(binary.LittleEndian.Uint64(data[:8])) 275 candidate := data[8 : 8+size] 276 amount := data[8+size:] 277 return &types.Vote{Amount: amount, Candidate: candidate} 278 } 279 280 func deserializeVoteList(data []byte, ex bool) *types.VoteList { 281 vl := &types.VoteList{Votes: []*types.Vote{}} 282 var end int 283 for offset := 0; offset < len(data); offset = end { 284 size := binary.LittleEndian.Uint64(data[offset : offset+8]) 285 end = offset + 8 + int(size) 286 v := data[offset+8 : end] 287 if ex { 288 vl.Votes = append(vl.Votes, deserializeVoteEx(v)) 289 } else { 290 vl.Votes = append(vl.Votes, deserializeVote(v)) 291 } 292 } 293 return vl 294 }