github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/native/native_neo.go (about) 1 package native 2 3 import ( 4 "context" 5 "crypto/elliptic" 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "math/big" 10 "sort" 11 "strings" 12 13 "github.com/nspcc-dev/neo-go/pkg/config" 14 "github.com/nspcc-dev/neo-go/pkg/core/dao" 15 "github.com/nspcc-dev/neo-go/pkg/core/interop" 16 "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" 17 istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage" 18 "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" 19 "github.com/nspcc-dev/neo-go/pkg/core/state" 20 "github.com/nspcc-dev/neo-go/pkg/core/storage" 21 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 22 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 23 "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" 24 "github.com/nspcc-dev/neo-go/pkg/io" 25 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 26 "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" 27 "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" 28 "github.com/nspcc-dev/neo-go/pkg/util" 29 "github.com/nspcc-dev/neo-go/pkg/vm/emit" 30 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 31 ) 32 33 // NEO represents NEO native contract. 34 type NEO struct { 35 nep17TokenNative 36 GAS *GAS 37 Policy *Policy 38 39 // Configuration and standby keys are set in constructor and then 40 // only read from. 41 cfg config.ProtocolConfiguration 42 standbyKeys keys.PublicKeys 43 } 44 45 type NeoCache struct { 46 // gasPerBlock represents the history of generated gas per block. 47 gasPerBlock gasRecord 48 49 registerPrice int64 50 51 votesChanged bool 52 nextValidators keys.PublicKeys 53 // newEpochNextValidators contains cached next block newEpochNextValidators. This list is updated once 54 // per dBFT epoch in PostPersist of the last block in the epoch if candidates 55 // votes ratio has been changed or register/unregister operation was performed 56 // within the last processed epoch. The updated value is being persisted 57 // following the standard layered DAO persist rules, so that external users 58 // will get the proper value with upper Blockchain's DAO (but this value is 59 // relevant only by the moment of first epoch block creation). 60 newEpochNextValidators keys.PublicKeys 61 // committee contains cached committee members and their votes. 62 // It is updated once in a while depending on committee size 63 // (every 28 blocks for mainnet). It's value 64 // is always equal to the value stored by `prefixCommittee`. 65 committee keysWithVotes 66 // newEpochCommittee contains cached committee members updated once per dBFT 67 // epoch in PostPersist of the last block in the epoch. 68 newEpochCommittee keysWithVotes 69 // committeeHash contains the script hash of the committee. 70 committeeHash util.Uint160 71 // newEpochCommitteeHash contains the script hash of the newEpochCommittee. 72 newEpochCommitteeHash util.Uint160 73 74 // gasPerVoteCache contains the last updated value of GAS per vote reward for candidates. 75 // It is set in state-modifying methods only and read in `PostPersist`, thus is not protected 76 // by any mutex. 77 gasPerVoteCache map[string]big.Int 78 } 79 80 const ( 81 neoContractID = -5 82 // NEOTotalSupply is the total amount of NEO in the system. 83 NEOTotalSupply = 100000000 84 // DefaultRegisterPrice is the default price for candidate register. 85 DefaultRegisterPrice = 1000 * GASFactor 86 // prefixCandidate is a prefix used to store validator's data. 87 prefixCandidate = 33 88 // prefixVotersCount is a prefix for storing total amount of NEO of voters. 89 prefixVotersCount = 1 90 // prefixVoterRewardPerCommittee is a prefix for storing committee GAS reward. 91 prefixVoterRewardPerCommittee = 23 92 // voterRewardFactor is a factor by which voter reward per committee is multiplied 93 // to make calculations more precise. 94 voterRewardFactor = 100_000_000 95 // prefixGASPerBlock is a prefix for storing amount of GAS generated per block. 96 prefixGASPerBlock = 29 97 // prefixRegisterPrice is a prefix for storing candidate register price. 98 prefixRegisterPrice = 13 99 // effectiveVoterTurnout represents minimal ratio of total supply to total amount voted value 100 // which is require to use non-standby validators. 101 effectiveVoterTurnout = 5 102 // neoHolderRewardRatio is a percent of generated GAS that is distributed to NEO holders. 103 neoHolderRewardRatio = 10 104 // neoHolderRewardRatio is a percent of generated GAS that is distributed to committee. 105 committeeRewardRatio = 10 106 // neoHolderRewardRatio is a percent of generated GAS that is distributed to voters. 107 voterRewardRatio = 80 108 109 // maxGetCandidatesRespLen is the maximum number of candidates to return from the 110 // getCandidates method. 111 maxGetCandidatesRespLen = 256 112 ) 113 114 var ( 115 // prefixCommittee is a key used to store committee. 116 prefixCommittee = []byte{14} 117 118 bigCommitteeRewardRatio = big.NewInt(committeeRewardRatio) 119 bigVoterRewardRatio = big.NewInt(voterRewardRatio) 120 bigVoterRewardFactor = big.NewInt(voterRewardFactor) 121 bigEffectiveVoterTurnout = big.NewInt(effectiveVoterTurnout) 122 big100 = big.NewInt(100) 123 ) 124 125 var ( 126 _ interop.Contract = (*NEO)(nil) 127 _ dao.NativeContractCache = (*NeoCache)(nil) 128 ) 129 130 // Copy implements NativeContractCache interface. 131 func (c *NeoCache) Copy() dao.NativeContractCache { 132 cp := &NeoCache{} 133 copyNeoCache(c, cp) 134 return cp 135 } 136 137 func copyNeoCache(src, dst *NeoCache) { 138 dst.votesChanged = src.votesChanged 139 // Can safely omit copying because the new array is created each time 140 // newEpochNextValidators list, nextValidators and committee are updated. 141 dst.nextValidators = src.nextValidators 142 dst.committee = src.committee 143 dst.committeeHash = src.committeeHash 144 dst.newEpochNextValidators = src.newEpochNextValidators 145 dst.newEpochCommittee = src.newEpochCommittee 146 dst.newEpochCommitteeHash = src.newEpochCommitteeHash 147 148 dst.registerPrice = src.registerPrice 149 150 // Can't omit copying because gasPerBlock is append-only, thus to be able to 151 // discard cache changes in case of FAULTed transaction we need a separate 152 // container for updated gasPerBlock values. 153 dst.gasPerBlock = make(gasRecord, len(src.gasPerBlock)) 154 copy(dst.gasPerBlock, src.gasPerBlock) 155 156 dst.gasPerVoteCache = make(map[string]big.Int) 157 for k, v := range src.gasPerVoteCache { 158 dst.gasPerVoteCache[k] = v 159 } 160 } 161 162 // makeValidatorKey creates a key from the account script hash. 163 func makeValidatorKey(key *keys.PublicKey) []byte { 164 b := key.Bytes() 165 // Don't create a new buffer. 166 b = append(b, 0) 167 copy(b[1:], b[0:]) 168 b[0] = prefixCandidate 169 return b 170 } 171 172 // newNEO returns NEO native contract. 173 func newNEO(cfg config.ProtocolConfiguration) *NEO { 174 n := &NEO{} 175 defer n.BuildHFSpecificMD(n.ActiveIn()) 176 177 nep17 := newNEP17Native(nativenames.Neo, neoContractID) 178 nep17.symbol = "NEO" 179 nep17.decimals = 0 180 nep17.factor = 1 181 nep17.incBalance = n.increaseBalance 182 nep17.balFromBytes = n.balanceFromBytes 183 184 n.nep17TokenNative = *nep17 185 186 err := n.initConfigCache(cfg) 187 if err != nil { 188 panic(fmt.Errorf("failed to initialize NEO config cache: %w", err)) 189 } 190 191 desc := newDescriptor("unclaimedGas", smartcontract.IntegerType, 192 manifest.NewParameter("account", smartcontract.Hash160Type), 193 manifest.NewParameter("end", smartcontract.IntegerType)) 194 md := newMethodAndPrice(n.unclaimedGas, 1<<17, callflag.ReadStates) 195 n.AddMethod(md, desc) 196 197 desc = newDescriptor("registerCandidate", smartcontract.BoolType, 198 manifest.NewParameter("pubkey", smartcontract.PublicKeyType)) 199 md = newMethodAndPrice(n.registerCandidate, 0, callflag.States) 200 n.AddMethod(md, desc) 201 202 desc = newDescriptor("unregisterCandidate", smartcontract.BoolType, 203 manifest.NewParameter("pubkey", smartcontract.PublicKeyType)) 204 md = newMethodAndPrice(n.unregisterCandidate, 1<<16, callflag.States) 205 n.AddMethod(md, desc) 206 207 desc = newDescriptor("vote", smartcontract.BoolType, 208 manifest.NewParameter("account", smartcontract.Hash160Type), 209 manifest.NewParameter("voteTo", smartcontract.PublicKeyType)) 210 md = newMethodAndPrice(n.vote, 1<<16, callflag.States) 211 n.AddMethod(md, desc) 212 213 desc = newDescriptor("getCandidates", smartcontract.ArrayType) 214 md = newMethodAndPrice(n.getCandidatesCall, 1<<22, callflag.ReadStates) 215 n.AddMethod(md, desc) 216 217 desc = newDescriptor("getAllCandidates", smartcontract.InteropInterfaceType) 218 md = newMethodAndPrice(n.getAllCandidatesCall, 1<<22, callflag.ReadStates) 219 n.AddMethod(md, desc) 220 221 desc = newDescriptor("getCandidateVote", smartcontract.IntegerType, 222 manifest.NewParameter("pubKey", smartcontract.PublicKeyType)) 223 md = newMethodAndPrice(n.getCandidateVoteCall, 1<<15, callflag.ReadStates) 224 n.AddMethod(md, desc) 225 226 desc = newDescriptor("getAccountState", smartcontract.ArrayType, 227 manifest.NewParameter("account", smartcontract.Hash160Type)) 228 md = newMethodAndPrice(n.getAccountState, 1<<15, callflag.ReadStates) 229 n.AddMethod(md, desc) 230 231 desc = newDescriptor("getCommittee", smartcontract.ArrayType) 232 md = newMethodAndPrice(n.getCommittee, 1<<16, callflag.ReadStates) 233 n.AddMethod(md, desc) 234 235 desc = newDescriptor("getCommitteeAddress", smartcontract.Hash160Type) 236 md = newMethodAndPrice(n.getCommitteeAddress, 1<<16, callflag.ReadStates, config.HFCockatrice) 237 n.AddMethod(md, desc) 238 239 desc = newDescriptor("getNextBlockValidators", smartcontract.ArrayType) 240 md = newMethodAndPrice(n.getNextBlockValidators, 1<<16, callflag.ReadStates) 241 n.AddMethod(md, desc) 242 243 desc = newDescriptor("getGasPerBlock", smartcontract.IntegerType) 244 md = newMethodAndPrice(n.getGASPerBlock, 1<<15, callflag.ReadStates) 245 n.AddMethod(md, desc) 246 247 desc = newDescriptor("setGasPerBlock", smartcontract.VoidType, 248 manifest.NewParameter("gasPerBlock", smartcontract.IntegerType)) 249 md = newMethodAndPrice(n.setGASPerBlock, 1<<15, callflag.States) 250 n.AddMethod(md, desc) 251 252 desc = newDescriptor("getRegisterPrice", smartcontract.IntegerType) 253 md = newMethodAndPrice(n.getRegisterPrice, 1<<15, callflag.ReadStates) 254 n.AddMethod(md, desc) 255 256 desc = newDescriptor("setRegisterPrice", smartcontract.VoidType, 257 manifest.NewParameter("registerPrice", smartcontract.IntegerType)) 258 md = newMethodAndPrice(n.setRegisterPrice, 1<<15, callflag.States) 259 n.AddMethod(md, desc) 260 261 eDesc := newEventDescriptor("CandidateStateChanged", 262 manifest.NewParameter("pubkey", smartcontract.PublicKeyType), 263 manifest.NewParameter("registered", smartcontract.BoolType), 264 manifest.NewParameter("votes", smartcontract.IntegerType), 265 ) 266 eMD := newEvent(eDesc) 267 n.AddEvent(eMD) 268 269 eDesc = newEventDescriptor("Vote", 270 manifest.NewParameter("account", smartcontract.Hash160Type), 271 manifest.NewParameter("from", smartcontract.PublicKeyType), 272 manifest.NewParameter("to", smartcontract.PublicKeyType), 273 manifest.NewParameter("amount", smartcontract.IntegerType), 274 ) 275 eMD = newEvent(eDesc) 276 n.AddEvent(eMD) 277 278 eDesc = newEventDescriptor("CommitteeChanged", 279 manifest.NewParameter("old", smartcontract.ArrayType), 280 manifest.NewParameter("new", smartcontract.ArrayType), 281 ) 282 eMD = newEvent(eDesc) 283 n.AddEvent(eMD) 284 285 return n 286 } 287 288 // Initialize initializes a NEO contract. 289 func (n *NEO) Initialize(ic *interop.Context, hf *config.Hardfork, newMD *interop.HFSpecificContractMD) error { 290 if hf != n.ActiveIn() { 291 return nil 292 } 293 294 if err := n.nep17TokenNative.Initialize(ic); err != nil { 295 return err 296 } 297 298 _, totalSupply := n.nep17TokenNative.getTotalSupply(ic.DAO) 299 if totalSupply.Sign() != 0 { 300 return errors.New("already initialized") 301 } 302 303 cache := &NeoCache{ 304 gasPerVoteCache: make(map[string]big.Int), 305 votesChanged: true, 306 } 307 308 // We need cache to be present in DAO before the subsequent call to `mint`. 309 ic.DAO.SetCache(n.ID, cache) 310 311 committee0 := n.standbyKeys[:n.cfg.GetCommitteeSize(ic.Block.Index)] 312 cvs := toKeysWithVotes(committee0) 313 err := n.updateCache(cache, cvs, ic.BlockHeight()) 314 if err != nil { 315 return err 316 } 317 318 ic.DAO.PutStorageItem(n.ID, prefixCommittee, cvs.Bytes(ic.DAO.GetItemCtx())) 319 320 h, err := getStandbyValidatorsHash(ic) 321 if err != nil { 322 return err 323 } 324 n.mint(ic, h, big.NewInt(NEOTotalSupply), false) 325 326 var index uint32 327 value := big.NewInt(5 * GASFactor) 328 n.putGASRecord(ic.DAO, index, value) 329 330 gr := &gasRecord{{Index: index, GASPerBlock: *value}} 331 cache.gasPerBlock = *gr 332 ic.DAO.PutStorageItem(n.ID, []byte{prefixVotersCount}, state.StorageItem{}) 333 334 setIntWithKey(n.ID, ic.DAO, []byte{prefixRegisterPrice}, DefaultRegisterPrice) 335 cache.registerPrice = int64(DefaultRegisterPrice) 336 337 var numOfCNs = n.cfg.GetNumOfCNs(ic.Block.Index + 1) 338 err = n.updateCachedNewEpochValues(ic.DAO, cache, ic.BlockHeight(), numOfCNs) 339 if err != nil { 340 return fmt.Errorf("failed to update next block newEpoch* cache: %w", err) 341 } 342 return nil 343 } 344 345 // InitializeCache initializes all NEO cache with the proper values from the storage. 346 // Cache initialization should be done apart from Initialize because Initialize is 347 // called only when deploying native contracts. InitializeCache implements the Contract 348 // interface. 349 func (n *NEO) InitializeCache(blockHeight uint32, d *dao.Simple) error { 350 cache := &NeoCache{ 351 gasPerVoteCache: make(map[string]big.Int), 352 votesChanged: true, 353 } 354 355 var committee = keysWithVotes{} 356 si := d.GetStorageItem(n.ID, prefixCommittee) 357 if err := committee.DecodeBytes(si); err != nil { 358 return fmt.Errorf("failed to decode committee: %w", err) 359 } 360 if err := n.updateCache(cache, committee, blockHeight); err != nil { 361 return fmt.Errorf("failed to update cache: %w", err) 362 } 363 364 cache.gasPerBlock = n.getSortedGASRecordFromDAO(d) 365 cache.registerPrice = getIntWithKey(n.ID, d, []byte{prefixRegisterPrice}) 366 367 // Update newEpoch* cache for external users. It holds values for the previous 368 // dBFT epoch if the current one isn't yet finished. 369 if n.cfg.ShouldUpdateCommitteeAt(blockHeight + 1) { 370 var numOfCNs = n.cfg.GetNumOfCNs(blockHeight + 1) 371 err := n.updateCachedNewEpochValues(d, cache, blockHeight, numOfCNs) 372 if err != nil { 373 return fmt.Errorf("failed to update next block newEpoch* cache: %w", err) 374 } 375 } else { 376 // nextValidators, committee and committee hash are filled in by this moment 377 // via n.updateCache call. 378 cache.newEpochNextValidators = cache.nextValidators.Copy() 379 cache.newEpochCommittee = make(keysWithVotes, len(cache.committee)) 380 copy(cache.newEpochCommittee, cache.committee) 381 cache.newEpochCommitteeHash = cache.committeeHash 382 } 383 384 d.SetCache(n.ID, cache) 385 return nil 386 } 387 388 // ActiveIn implements the Contract interface. 389 func (n *NEO) ActiveIn() *config.Hardfork { 390 return nil 391 } 392 393 func (n *NEO) initConfigCache(cfg config.ProtocolConfiguration) error { 394 var err error 395 396 n.cfg = cfg 397 n.standbyKeys, err = keys.NewPublicKeysFromStrings(n.cfg.StandbyCommittee) 398 return err 399 } 400 401 func (n *NEO) updateCache(cache *NeoCache, cvs keysWithVotes, blockHeight uint32) error { 402 cache.committee = cvs 403 404 var committee = getCommitteeMembers(cache.committee) 405 script, err := smartcontract.CreateMajorityMultiSigRedeemScript(committee.Copy()) 406 if err != nil { 407 return err 408 } 409 cache.committeeHash = hash.Hash160(script) 410 411 nextVals := committee[:n.cfg.GetNumOfCNs(blockHeight+1)].Copy() 412 sort.Sort(nextVals) 413 cache.nextValidators = nextVals 414 return nil 415 } 416 417 // updateCachedNewEpochValues sets newEpochNextValidators, newEpochCommittee and 418 // newEpochCommitteeHash cache that will be used by external users to retrieve 419 // next block validators list of the next dBFT epoch that wasn't yet started and 420 // will be used by corresponding values initialisation on the next epoch start. 421 // The updated new epoch cached values computed using the persisted blocks state 422 // of the latest epoch. 423 func (n *NEO) updateCachedNewEpochValues(d *dao.Simple, cache *NeoCache, blockHeight uint32, numOfCNs int) error { 424 committee, cvs, err := n.computeCommitteeMembers(blockHeight, d) 425 if err != nil { 426 return fmt.Errorf("failed to compute committee members: %w", err) 427 } 428 cache.newEpochCommittee = cvs 429 430 script, err := smartcontract.CreateMajorityMultiSigRedeemScript(committee.Copy()) 431 if err != nil { 432 return err 433 } 434 cache.newEpochCommitteeHash = hash.Hash160(script) 435 436 nextVals := committee[:numOfCNs].Copy() 437 sort.Sort(nextVals) 438 cache.newEpochNextValidators = nextVals 439 return nil 440 } 441 442 // OnPersist implements the Contract interface. 443 func (n *NEO) OnPersist(ic *interop.Context) error { 444 if n.cfg.ShouldUpdateCommitteeAt(ic.Block.Index) { 445 cache := ic.DAO.GetRWCache(n.ID).(*NeoCache) 446 // Cached newEpoch* values always have proper value set (either by PostPersist 447 // during the last epoch block handling or by initialization code). 448 449 var oldCommittee, newCommittee stackitem.Item 450 for i := 0; i < len(cache.committee); i++ { 451 if cache.newEpochCommittee[i].Key != cache.committee[i].Key || 452 (i == 0 && len(cache.newEpochCommittee) != len(cache.committee)) { 453 oldCommittee, newCommittee = cache.committee.toNotificationItem(), cache.newEpochCommittee.toNotificationItem() 454 break 455 } 456 } 457 458 cache.nextValidators = cache.newEpochNextValidators 459 cache.committee = cache.newEpochCommittee 460 cache.committeeHash = cache.newEpochCommitteeHash 461 cache.votesChanged = false 462 463 // We need to put in storage anyway, as it affects dumps 464 ic.DAO.PutStorageItem(n.ID, prefixCommittee, cache.committee.Bytes(ic.DAO.GetItemCtx())) 465 466 if oldCommittee != nil { 467 ic.AddNotification(n.Hash, "CommitteeChanged", stackitem.NewArray([]stackitem.Item{ 468 oldCommittee, newCommittee, 469 })) 470 } 471 } 472 return nil 473 } 474 475 // PostPersist implements the Contract interface. 476 func (n *NEO) PostPersist(ic *interop.Context) error { 477 gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index) 478 cache := ic.DAO.GetROCache(n.ID).(*NeoCache) 479 pubs := getCommitteeMembers(cache.committee) 480 committeeSize := n.cfg.GetCommitteeSize(ic.Block.Index) 481 index := int(ic.Block.Index) % committeeSize 482 committeeReward := new(big.Int).Mul(gas, bigCommitteeRewardRatio) 483 n.GAS.mint(ic, pubs[index].GetScriptHash(), committeeReward.Div(committeeReward, big100), false) 484 485 var isCacheRW bool 486 if n.cfg.ShouldUpdateCommitteeAt(ic.Block.Index) { 487 var voterReward = new(big.Int).Set(bigVoterRewardRatio) 488 voterReward.Mul(voterReward, gas) 489 voterReward.Mul(voterReward, big.NewInt(voterRewardFactor*int64(committeeSize))) 490 var validatorsCount = n.cfg.GetNumOfCNs(ic.Block.Index) 491 voterReward.Div(voterReward, big.NewInt(int64(committeeSize+validatorsCount))) 492 voterReward.Div(voterReward, big100) 493 494 var ( 495 cs = cache.committee 496 key = make([]byte, 34) 497 ) 498 for i := range cs { 499 if cs[i].Votes.Sign() > 0 { 500 var tmp = new(big.Int) 501 if i < validatorsCount { 502 tmp.Set(intTwo) 503 } else { 504 tmp.Set(intOne) 505 } 506 tmp.Mul(tmp, voterReward) 507 tmp.Div(tmp, cs[i].Votes) 508 509 key = makeVoterKey([]byte(cs[i].Key), key) 510 r := n.getLatestGASPerVote(ic.DAO, key) 511 tmp.Add(tmp, &r) 512 513 if !isCacheRW { 514 cache = ic.DAO.GetRWCache(n.ID).(*NeoCache) 515 isCacheRW = true 516 } 517 cache.gasPerVoteCache[cs[i].Key] = *tmp 518 519 ic.DAO.PutBigInt(n.ID, key, tmp) 520 } 521 } 522 } 523 // Update newEpoch cache for external users and further committee, committeeHash 524 // and nextBlockValidators cache initialisation if committee should be updated in 525 // the next block. 526 if n.cfg.ShouldUpdateCommitteeAt(ic.Block.Index + 1) { 527 var ( 528 h = ic.Block.Index // consider persisting block as stored to get _next_ block newEpochNextValidators 529 numOfCNs = n.cfg.GetNumOfCNs(h + 1) 530 ) 531 if cache.votesChanged || 532 numOfCNs != len(cache.newEpochNextValidators) || 533 n.cfg.GetCommitteeSize(h+1) != len(cache.newEpochCommittee) { 534 if !isCacheRW { 535 cache = ic.DAO.GetRWCache(n.ID).(*NeoCache) 536 } 537 err := n.updateCachedNewEpochValues(ic.DAO, cache, h, numOfCNs) 538 if err != nil { 539 return fmt.Errorf("failed to update next block newEpoch* cache: %w", err) 540 } 541 } 542 } 543 544 return nil 545 } 546 547 func (n *NEO) getLatestGASPerVote(d *dao.Simple, key []byte) big.Int { 548 var g big.Int 549 cache := d.GetROCache(n.ID).(*NeoCache) 550 if g, ok := cache.gasPerVoteCache[string(key[1:])]; ok { 551 return g 552 } 553 item := d.GetStorageItem(n.ID, key) 554 if item == nil { 555 g = *big.NewInt(0) 556 } else { 557 g = *bigint.FromBytes(item) 558 } 559 return g 560 } 561 562 func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int, checkBal *big.Int) (func(), error) { 563 var postF func() 564 565 acc, err := state.NEOBalanceFromBytes(*si) 566 if err != nil { 567 return nil, err 568 } 569 if (amount.Sign() == -1 && acc.Balance.CmpAbs(amount) == -1) || 570 (amount.Sign() == 0 && checkBal != nil && acc.Balance.Cmp(checkBal) == -1) { 571 return nil, errors.New("insufficient funds") 572 } 573 newGas, err := n.distributeGas(ic, acc) 574 if err != nil { 575 return nil, err 576 } 577 if newGas != nil { // Can be if it was already distributed in the same block. 578 postF = func() { n.GAS.mint(ic, h, newGas, true) } 579 } 580 if amount.Sign() == 0 { 581 *si = acc.Bytes(ic.DAO.GetItemCtx()) 582 return postF, nil 583 } 584 if err := n.ModifyAccountVotes(acc, ic.DAO, amount, false); err != nil { 585 return nil, err 586 } 587 if acc.VoteTo != nil { 588 if err := n.modifyVoterTurnout(ic.DAO, amount); err != nil { 589 return nil, err 590 } 591 } 592 acc.Balance.Add(&acc.Balance, amount) 593 if acc.Balance.Sign() != 0 { 594 *si = acc.Bytes(ic.DAO.GetItemCtx()) 595 } else { 596 *si = nil 597 } 598 return postF, nil 599 } 600 601 func (n *NEO) balanceFromBytes(si *state.StorageItem) (*big.Int, error) { 602 acc, err := state.NEOBalanceFromBytes(*si) 603 if err != nil { 604 return nil, err 605 } 606 return &acc.Balance, err 607 } 608 609 func (n *NEO) distributeGas(ic *interop.Context, acc *state.NEOBalance) (*big.Int, error) { 610 if ic.Block == nil || ic.Block.Index == 0 || ic.Block.Index == acc.BalanceHeight { 611 return nil, nil 612 } 613 gen, err := n.calculateBonus(ic.DAO, acc, ic.Block.Index) 614 if err != nil { 615 return nil, err 616 } 617 acc.BalanceHeight = ic.Block.Index 618 if acc.VoteTo != nil { 619 latestGasPerVote := n.getLatestGASPerVote(ic.DAO, makeVoterKey(acc.VoteTo.Bytes())) 620 acc.LastGasPerVote = latestGasPerVote 621 } 622 623 return gen, nil 624 } 625 626 func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem.Item { 627 u := toUint160(args[0]) 628 end := uint32(toBigInt(args[1]).Int64()) 629 gen, err := n.CalculateBonus(ic, u, end) 630 if err != nil { 631 panic(err) 632 } 633 return stackitem.NewBigInteger(gen) 634 } 635 636 func (n *NEO) getGASPerBlock(ic *interop.Context, _ []stackitem.Item) stackitem.Item { 637 gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index) 638 return stackitem.NewBigInteger(gas) 639 } 640 641 func (n *NEO) getSortedGASRecordFromDAO(d *dao.Simple) gasRecord { 642 var gr = make(gasRecord, 0) 643 d.Seek(n.ID, storage.SeekRange{Prefix: []byte{prefixGASPerBlock}}, func(k, v []byte) bool { 644 gr = append(gr, gasIndexPair{ 645 Index: binary.BigEndian.Uint32(k), 646 GASPerBlock: *bigint.FromBytes(v), 647 }) 648 return true 649 }) 650 return gr 651 } 652 653 // GetGASPerBlock returns gas generated for block with provided index. 654 func (n *NEO) GetGASPerBlock(d *dao.Simple, index uint32) *big.Int { 655 cache := d.GetROCache(n.ID).(*NeoCache) 656 gr := cache.gasPerBlock 657 for i := len(gr) - 1; i >= 0; i-- { 658 if gr[i].Index <= index { 659 g := gr[i].GASPerBlock 660 return &g 661 } 662 } 663 panic("NEO cache not initialized") 664 } 665 666 // GetCommitteeAddress returns address of the committee. 667 func (n *NEO) GetCommitteeAddress(d *dao.Simple) util.Uint160 { 668 cache := d.GetROCache(n.ID).(*NeoCache) 669 return cache.committeeHash 670 } 671 672 func (n *NEO) checkCommittee(ic *interop.Context) bool { 673 ok, err := runtime.CheckHashedWitness(ic, n.GetCommitteeAddress(ic.DAO)) 674 if err != nil { 675 panic(err) 676 } 677 return ok 678 } 679 680 func (n *NEO) setGASPerBlock(ic *interop.Context, args []stackitem.Item) stackitem.Item { 681 gas := toBigInt(args[0]) 682 err := n.SetGASPerBlock(ic, ic.Block.Index+1, gas) 683 if err != nil { 684 panic(err) 685 } 686 return stackitem.Null{} 687 } 688 689 // SetGASPerBlock sets gas generated for blocks after index. 690 func (n *NEO) SetGASPerBlock(ic *interop.Context, index uint32, gas *big.Int) error { 691 if gas.Sign() == -1 || gas.Cmp(big.NewInt(10*GASFactor)) == 1 { 692 return errors.New("invalid value for GASPerBlock") 693 } 694 if !n.checkCommittee(ic) { 695 return errors.New("invalid committee signature") 696 } 697 n.putGASRecord(ic.DAO, index, gas) 698 cache := ic.DAO.GetRWCache(n.ID).(*NeoCache) 699 cache.gasPerBlock = append(cache.gasPerBlock, gasIndexPair{ 700 Index: index, 701 GASPerBlock: *gas, 702 }) 703 return nil 704 } 705 706 func (n *NEO) getRegisterPrice(ic *interop.Context, _ []stackitem.Item) stackitem.Item { 707 return stackitem.NewBigInteger(big.NewInt(n.getRegisterPriceInternal(ic.DAO))) 708 } 709 710 func (n *NEO) getRegisterPriceInternal(d *dao.Simple) int64 { 711 cache := d.GetROCache(n.ID).(*NeoCache) 712 return cache.registerPrice 713 } 714 715 func (n *NEO) setRegisterPrice(ic *interop.Context, args []stackitem.Item) stackitem.Item { 716 price := toBigInt(args[0]) 717 if price.Sign() <= 0 || !price.IsInt64() { 718 panic("invalid register price") 719 } 720 if !n.checkCommittee(ic) { 721 panic("invalid committee signature") 722 } 723 724 setIntWithKey(n.ID, ic.DAO, []byte{prefixRegisterPrice}, price.Int64()) 725 cache := ic.DAO.GetRWCache(n.ID).(*NeoCache) 726 cache.registerPrice = price.Int64() 727 return stackitem.Null{} 728 } 729 730 func (n *NEO) dropCandidateIfZero(d *dao.Simple, cache *NeoCache, pub *keys.PublicKey, c *candidate) bool { 731 if c.Registered || c.Votes.Sign() != 0 { 732 return false 733 } 734 d.DeleteStorageItem(n.ID, makeValidatorKey(pub)) 735 736 voterKey := makeVoterKey(pub.Bytes()) 737 d.DeleteStorageItem(n.ID, voterKey) 738 delete(cache.gasPerVoteCache, string(voterKey)) 739 740 return true 741 } 742 743 func makeVoterKey(pub []byte, prealloc ...[]byte) []byte { 744 var key []byte 745 if len(prealloc) != 0 { 746 key = prealloc[0] 747 } else { 748 key = make([]byte, 34) 749 } 750 key[0] = prefixVoterRewardPerCommittee 751 copy(key[1:], pub) 752 return key 753 } 754 755 // CalculateBonus calculates amount of gas generated for holding value NEO from start to end block 756 // and having voted for active committee member. 757 func (n *NEO) CalculateBonus(ic *interop.Context, acc util.Uint160, end uint32) (*big.Int, error) { 758 if ic.Block == nil || end != ic.Block.Index { 759 return nil, errors.New("can't calculate bonus of height unequal (BlockHeight + 1)") 760 } 761 key := makeAccountKey(acc) 762 si := ic.DAO.GetStorageItem(n.ID, key) 763 if si == nil { 764 return nil, storage.ErrKeyNotFound 765 } 766 st, err := state.NEOBalanceFromBytes(si) 767 if err != nil { 768 return nil, err 769 } 770 return n.calculateBonus(ic.DAO, st, end) 771 } 772 773 func (n *NEO) calculateBonus(d *dao.Simple, acc *state.NEOBalance, end uint32) (*big.Int, error) { 774 r, err := n.CalculateNEOHolderReward(d, &acc.Balance, acc.BalanceHeight, end) 775 if err != nil || acc.VoteTo == nil { 776 return r, err 777 } 778 779 var key = makeVoterKey(acc.VoteTo.Bytes()) 780 var reward = n.getLatestGASPerVote(d, key) 781 var tmp = big.NewInt(0).Sub(&reward, &acc.LastGasPerVote) 782 tmp.Mul(tmp, &acc.Balance) 783 tmp.Div(tmp, bigVoterRewardFactor) 784 tmp.Add(tmp, r) 785 return tmp, nil 786 } 787 788 // CalculateNEOHolderReward return GAS reward for holding `value` of NEO from start to end block. 789 func (n *NEO) CalculateNEOHolderReward(d *dao.Simple, value *big.Int, start, end uint32) (*big.Int, error) { 790 if value.Sign() == 0 || start >= end { 791 return big.NewInt(0), nil 792 } else if value.Sign() < 0 { 793 return nil, errors.New("negative value") 794 } 795 cache := d.GetROCache(n.ID).(*NeoCache) 796 gr := cache.gasPerBlock 797 var sum, tmp big.Int 798 for i := len(gr) - 1; i >= 0; i-- { 799 if gr[i].Index >= end { 800 continue 801 } else if gr[i].Index <= start { 802 tmp.SetInt64(int64(end - start)) 803 tmp.Mul(&tmp, &gr[i].GASPerBlock) 804 sum.Add(&sum, &tmp) 805 break 806 } 807 tmp.SetInt64(int64(end - gr[i].Index)) 808 tmp.Mul(&tmp, &gr[i].GASPerBlock) 809 sum.Add(&sum, &tmp) 810 end = gr[i].Index 811 } 812 res := new(big.Int).Mul(value, &sum) 813 res.Mul(res, tmp.SetInt64(neoHolderRewardRatio)) 814 res.Div(res, tmp.SetInt64(100*NEOTotalSupply)) 815 return res, nil 816 } 817 818 func (n *NEO) registerCandidate(ic *interop.Context, args []stackitem.Item) stackitem.Item { 819 pub := toPublicKey(args[0]) 820 ok, err := runtime.CheckKeyedWitness(ic, pub) 821 if err != nil { 822 panic(err) 823 } else if !ok { 824 return stackitem.NewBool(false) 825 } 826 if !ic.VM.AddGas(n.getRegisterPriceInternal(ic.DAO)) { 827 panic("insufficient gas") 828 } 829 err = n.RegisterCandidateInternal(ic, pub) 830 return stackitem.NewBool(err == nil) 831 } 832 833 // RegisterCandidateInternal registers pub as a new candidate. 834 func (n *NEO) RegisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey) error { 835 var emitEvent = true 836 837 key := makeValidatorKey(pub) 838 si := ic.DAO.GetStorageItem(n.ID, key) 839 var c *candidate 840 if si == nil { 841 c = &candidate{Registered: true} 842 } else { 843 c = new(candidate).FromBytes(si) 844 emitEvent = !c.Registered 845 c.Registered = true 846 } 847 err := putConvertibleToDAO(n.ID, ic.DAO, key, c) 848 if emitEvent { 849 cache := ic.DAO.GetRWCache(n.ID).(*NeoCache) 850 cache.votesChanged = true 851 ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{ 852 stackitem.NewByteArray(pub.Bytes()), 853 stackitem.NewBool(c.Registered), 854 stackitem.NewBigInteger(&c.Votes), 855 })) 856 } 857 return err 858 } 859 860 func (n *NEO) unregisterCandidate(ic *interop.Context, args []stackitem.Item) stackitem.Item { 861 pub := toPublicKey(args[0]) 862 ok, err := runtime.CheckKeyedWitness(ic, pub) 863 if err != nil { 864 panic(err) 865 } else if !ok { 866 return stackitem.NewBool(false) 867 } 868 err = n.UnregisterCandidateInternal(ic, pub) 869 return stackitem.NewBool(err == nil) 870 } 871 872 // UnregisterCandidateInternal unregisters pub as a candidate. 873 func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey) error { 874 var err error 875 876 key := makeValidatorKey(pub) 877 si := ic.DAO.GetStorageItem(n.ID, key) 878 if si == nil { 879 return nil 880 } 881 cache := ic.DAO.GetRWCache(n.ID).(*NeoCache) 882 // Not only current committee/validators cache is interested in votesChanged, but also 883 // newEpoch cache, thus, modify votesChanged to update the latter. 884 cache.votesChanged = true 885 c := new(candidate).FromBytes(si) 886 emitEvent := c.Registered 887 c.Registered = false 888 ok := n.dropCandidateIfZero(ic.DAO, cache, pub, c) 889 if !ok { 890 err = putConvertibleToDAO(n.ID, ic.DAO, key, c) 891 } 892 if emitEvent { 893 ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{ 894 stackitem.NewByteArray(pub.Bytes()), 895 stackitem.NewBool(c.Registered), 896 stackitem.NewBigInteger(&c.Votes), 897 })) 898 } 899 return err 900 } 901 902 func (n *NEO) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item { 903 acc := toUint160(args[0]) 904 var pub *keys.PublicKey 905 if _, ok := args[1].(stackitem.Null); !ok { 906 pub = toPublicKey(args[1]) 907 } 908 err := n.VoteInternal(ic, acc, pub) 909 return stackitem.NewBool(err == nil) 910 } 911 912 // VoteInternal votes from account h for validarors specified in pubs. 913 func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.PublicKey) error { 914 ok, err := runtime.CheckHashedWitness(ic, h) 915 if err != nil { 916 return err 917 } else if !ok { 918 return errors.New("invalid signature") 919 } 920 key := makeAccountKey(h) 921 si := ic.DAO.GetStorageItem(n.ID, key) 922 if si == nil { 923 return errors.New("invalid account") 924 } 925 acc, err := state.NEOBalanceFromBytes(si) 926 if err != nil { 927 return err 928 } 929 // we should put it in storage anyway as it affects dumps 930 ic.DAO.PutStorageItem(n.ID, key, si) 931 if pub != nil { 932 valKey := makeValidatorKey(pub) 933 valSi := ic.DAO.GetStorageItem(n.ID, valKey) 934 if valSi == nil { 935 return errors.New("unknown validator") 936 } 937 cd := new(candidate).FromBytes(valSi) 938 // we should put it in storage anyway as it affects dumps 939 ic.DAO.PutStorageItem(n.ID, valKey, valSi) 940 if !cd.Registered { 941 return errors.New("validator must be registered") 942 } 943 } 944 945 if (acc.VoteTo == nil) != (pub == nil) { 946 val := &acc.Balance 947 if pub == nil { 948 val = new(big.Int).Neg(val) 949 } 950 if err := n.modifyVoterTurnout(ic.DAO, val); err != nil { 951 return err 952 } 953 } 954 newGas, err := n.distributeGas(ic, acc) 955 if err != nil { 956 return err 957 } 958 if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance), false); err != nil { 959 return err 960 } 961 if pub != nil && pub != acc.VoteTo { 962 acc.LastGasPerVote = n.getLatestGASPerVote(ic.DAO, makeVoterKey(pub.Bytes())) 963 } 964 oldVote := acc.VoteTo 965 acc.VoteTo = pub 966 if err := n.ModifyAccountVotes(acc, ic.DAO, &acc.Balance, true); err != nil { 967 return err 968 } 969 if pub == nil { 970 acc.LastGasPerVote = *big.NewInt(0) 971 } 972 ic.DAO.PutStorageItem(n.ID, key, acc.Bytes(ic.DAO.GetItemCtx())) 973 974 ic.AddNotification(n.Hash, "Vote", stackitem.NewArray([]stackitem.Item{ 975 stackitem.NewByteArray(h.BytesBE()), 976 keyToStackItem(oldVote), 977 keyToStackItem(pub), 978 stackitem.NewBigInteger(&acc.Balance), 979 })) 980 981 if newGas != nil { // Can be if it was already distributed in the same block. 982 n.GAS.mint(ic, h, newGas, true) 983 } 984 return nil 985 } 986 987 func keyToStackItem(k *keys.PublicKey) stackitem.Item { 988 if k == nil { 989 return stackitem.Null{} 990 } 991 return stackitem.NewByteArray(k.Bytes()) 992 } 993 994 // ModifyAccountVotes modifies votes of the specified account by value (can be negative). 995 // typ specifies if this modify is occurring during transfer or vote (with old or new validator). 996 func (n *NEO) ModifyAccountVotes(acc *state.NEOBalance, d *dao.Simple, value *big.Int, isNewVote bool) error { 997 cache := d.GetRWCache(n.ID).(*NeoCache) 998 cache.votesChanged = true 999 if acc.VoteTo != nil { 1000 key := makeValidatorKey(acc.VoteTo) 1001 si := d.GetStorageItem(n.ID, key) 1002 if si == nil { 1003 return errors.New("invalid validator") 1004 } 1005 cd := new(candidate).FromBytes(si) 1006 cd.Votes.Add(&cd.Votes, value) 1007 if !isNewVote { 1008 ok := n.dropCandidateIfZero(d, cache, acc.VoteTo, cd) 1009 if ok { 1010 return nil 1011 } 1012 } 1013 return putConvertibleToDAO(n.ID, d, key, cd) 1014 } 1015 return nil 1016 } 1017 1018 func (n *NEO) getCandidates(d *dao.Simple, sortByKey bool, max int) ([]keyWithVotes, error) { 1019 arr := make([]keyWithVotes, 0) 1020 buf := io.NewBufBinWriter() 1021 d.Seek(n.ID, storage.SeekRange{Prefix: []byte{prefixCandidate}}, func(k, v []byte) bool { 1022 c := new(candidate).FromBytes(v) 1023 emit.CheckSig(buf.BinWriter, k) 1024 if c.Registered && !n.Policy.IsBlocked(d, hash.Hash160(buf.Bytes())) { 1025 arr = append(arr, keyWithVotes{Key: string(k), Votes: &c.Votes}) 1026 } 1027 buf.Reset() 1028 return !sortByKey || max > 0 && len(arr) < max 1029 }) 1030 1031 if !sortByKey { 1032 // sortByKey assumes to sort by serialized key bytes (that's the way keys 1033 // are stored and retrieved from the storage by default). Otherwise, need 1034 // to sort using big.Int comparator. 1035 sort.Slice(arr, func(i, j int) bool { 1036 // The most-voted validators should end up in the front of the list. 1037 cmp := arr[i].Votes.Cmp(arr[j].Votes) 1038 if cmp != 0 { 1039 return cmp > 0 1040 } 1041 // Ties are broken with deserialized public keys. 1042 // Sort by ECPoint's (X, Y) components: compare X first, and then compare Y. 1043 cmpX := strings.Compare(arr[i].Key[1:], arr[j].Key[1:]) 1044 if cmpX != 0 { 1045 return cmpX == -1 1046 } 1047 // The case when X components are the same is extremely rare, thus we perform 1048 // key deserialization only if needed. No error can occur. 1049 ki, _ := keys.NewPublicKeyFromBytes([]byte(arr[i].Key), elliptic.P256()) 1050 kj, _ := keys.NewPublicKeyFromBytes([]byte(arr[j].Key), elliptic.P256()) 1051 return ki.Y.Cmp(kj.Y) == -1 1052 }) 1053 } 1054 return arr, nil 1055 } 1056 1057 // GetCandidates returns current registered validators list with keys 1058 // and votes. 1059 func (n *NEO) GetCandidates(d *dao.Simple) ([]state.Validator, error) { 1060 kvs, err := n.getCandidates(d, true, maxGetCandidatesRespLen) 1061 if err != nil { 1062 return nil, err 1063 } 1064 arr := make([]state.Validator, len(kvs)) 1065 for i := range kvs { 1066 arr[i].Key, err = keys.NewPublicKeyFromBytes([]byte(kvs[i].Key), elliptic.P256()) 1067 if err != nil { 1068 return nil, err 1069 } 1070 arr[i].Votes = kvs[i].Votes 1071 } 1072 return arr, nil 1073 } 1074 1075 func (n *NEO) getCandidatesCall(ic *interop.Context, _ []stackitem.Item) stackitem.Item { 1076 validators, err := n.getCandidates(ic.DAO, true, maxGetCandidatesRespLen) 1077 if err != nil { 1078 panic(err) 1079 } 1080 arr := make([]stackitem.Item, len(validators)) 1081 for i := range validators { 1082 arr[i] = stackitem.NewStruct([]stackitem.Item{ 1083 stackitem.NewByteArray([]byte(validators[i].Key)), 1084 stackitem.NewBigInteger(validators[i].Votes), 1085 }) 1086 } 1087 return stackitem.NewArray(arr) 1088 } 1089 1090 func (n *NEO) getCommitteeAddress(ic *interop.Context, _ []stackitem.Item) stackitem.Item { 1091 return stackitem.NewByteArray(n.GetCommitteeAddress(ic.DAO).BytesBE()) 1092 } 1093 1094 func (n *NEO) getAllCandidatesCall(ic *interop.Context, _ []stackitem.Item) stackitem.Item { 1095 ctx, cancel := context.WithCancel(context.Background()) 1096 prefix := []byte{prefixCandidate} 1097 buf := io.NewBufBinWriter() 1098 keep := func(kv storage.KeyValue) bool { 1099 c := new(candidate).FromBytes(kv.Value) 1100 emit.CheckSig(buf.BinWriter, kv.Key) 1101 if c.Registered && !n.Policy.IsBlocked(ic.DAO, hash.Hash160(buf.Bytes())) { 1102 buf.Reset() 1103 return true 1104 } 1105 buf.Reset() 1106 return false 1107 } 1108 seekres := ic.DAO.SeekAsync(ctx, n.ID, storage.SeekRange{Prefix: prefix}) 1109 filteredRes := make(chan storage.KeyValue) 1110 go func() { 1111 for kv := range seekres { 1112 if keep(kv) { 1113 filteredRes <- kv 1114 } 1115 } 1116 close(filteredRes) 1117 }() 1118 1119 opts := istorage.FindRemovePrefix | istorage.FindDeserialize | istorage.FindPick1 1120 item := istorage.NewIterator(filteredRes, prefix, int64(opts)) 1121 ic.RegisterCancelFunc(func() { 1122 cancel() 1123 for range seekres { //nolint:revive //empty-block 1124 } 1125 }) 1126 return stackitem.NewInterop(item) 1127 } 1128 1129 func (n *NEO) getCandidateVoteCall(ic *interop.Context, args []stackitem.Item) stackitem.Item { 1130 pub := toPublicKey(args[0]) 1131 key := makeValidatorKey(pub) 1132 si := ic.DAO.GetStorageItem(n.ID, key) 1133 if si == nil { 1134 return stackitem.NewBigInteger(big.NewInt(-1)) 1135 } 1136 c := new(candidate).FromBytes(si) 1137 if !c.Registered { 1138 return stackitem.NewBigInteger(big.NewInt(-1)) 1139 } 1140 return stackitem.NewBigInteger(&c.Votes) 1141 } 1142 1143 func (n *NEO) getAccountState(ic *interop.Context, args []stackitem.Item) stackitem.Item { 1144 key := makeAccountKey(toUint160(args[0])) 1145 si := ic.DAO.GetStorageItem(n.ID, key) 1146 if len(si) == 0 { 1147 return stackitem.Null{} 1148 } 1149 1150 item, err := stackitem.Deserialize(si) 1151 if err != nil { 1152 panic(err) // no errors are expected but we better be sure 1153 } 1154 return item 1155 } 1156 1157 // ComputeNextBlockValidators computes an actual list of current validators that is 1158 // relevant for the latest processed dBFT epoch and based on the changes made by 1159 // register/unregister/vote calls during the latest epoch. 1160 // Note: this method isn't actually "computes" new committee list and calculates 1161 // new validators list from it. Instead, it uses cache, and the cache itself is 1162 // updated during the PostPersist of the last block of every epoch. 1163 func (n *NEO) ComputeNextBlockValidators(d *dao.Simple) keys.PublicKeys { 1164 // It should always be OK with RO cache if using lower-layered DAO with proper 1165 // cache set. 1166 cache := d.GetROCache(n.ID).(*NeoCache) 1167 if vals := cache.newEpochNextValidators; vals != nil { 1168 return vals.Copy() 1169 } 1170 // It's a caller's program error to call ComputeNextBlockValidators not having 1171 // the right value in lower cache. With the current scheme of handling 1172 // newEpochNextValidators cache this code is expected to be unreachable, but 1173 // let's have this panic here just in case. 1174 panic("bug: unexpected external call to newEpochNextValidators cache") 1175 } 1176 1177 func (n *NEO) getCommittee(ic *interop.Context, _ []stackitem.Item) stackitem.Item { 1178 pubs := n.GetCommitteeMembers(ic.DAO) 1179 sort.Sort(pubs) 1180 return pubsToArray(pubs) 1181 } 1182 1183 func (n *NEO) modifyVoterTurnout(d *dao.Simple, amount *big.Int) error { 1184 key := []byte{prefixVotersCount} 1185 si := d.GetStorageItem(n.ID, key) 1186 if si == nil { 1187 return errors.New("voters count not found") 1188 } 1189 votersCount := bigint.FromBytes(si) 1190 votersCount.Add(votersCount, amount) 1191 d.PutBigInt(n.ID, key, votersCount) 1192 return nil 1193 } 1194 1195 // GetCommitteeMembers returns public keys of nodes in committee using cached value. 1196 func (n *NEO) GetCommitteeMembers(d *dao.Simple) keys.PublicKeys { 1197 cache := d.GetROCache(n.ID).(*NeoCache) 1198 return getCommitteeMembers(cache.committee) 1199 } 1200 1201 func getCommitteeMembers(cvs keysWithVotes) keys.PublicKeys { 1202 var committee = make(keys.PublicKeys, len(cvs)) 1203 var err error 1204 for i := range committee { 1205 committee[i], err = cvs[i].PublicKey() 1206 if err != nil { 1207 panic(err) 1208 } 1209 } 1210 return committee 1211 } 1212 1213 func toKeysWithVotes(pubs keys.PublicKeys) keysWithVotes { 1214 ks := make(keysWithVotes, len(pubs)) 1215 for i := range pubs { 1216 ks[i].UnmarshaledKey = pubs[i] 1217 ks[i].Key = string(pubs[i].Bytes()) 1218 ks[i].Votes = big.NewInt(0) 1219 } 1220 return ks 1221 } 1222 1223 // computeCommitteeMembers returns public keys of nodes in committee. 1224 func (n *NEO) computeCommitteeMembers(blockHeight uint32, d *dao.Simple) (keys.PublicKeys, keysWithVotes, error) { 1225 key := []byte{prefixVotersCount} 1226 si := d.GetStorageItem(n.ID, key) 1227 if si == nil { 1228 return nil, nil, errors.New("voters count not found") 1229 } 1230 votersCount := bigint.FromBytes(si) 1231 // votersCount / totalSupply must be >= 0.2 1232 votersCount.Mul(votersCount, bigEffectiveVoterTurnout) 1233 _, totalSupply := n.getTotalSupply(d) 1234 voterTurnout := votersCount.Div(votersCount, totalSupply) 1235 1236 count := n.cfg.GetCommitteeSize(blockHeight + 1) 1237 // Can be sorted and/or returned to outside users, thus needs to be copied. 1238 sbVals := keys.PublicKeys(n.standbyKeys[:count]).Copy() 1239 cs, err := n.getCandidates(d, false, -1) 1240 if err != nil { 1241 return nil, nil, err 1242 } 1243 if voterTurnout.Sign() != 1 || len(cs) < count { 1244 kvs := make(keysWithVotes, count) 1245 for i := range kvs { 1246 kvs[i].UnmarshaledKey = sbVals[i] 1247 kvs[i].Key = string(sbVals[i].Bytes()) 1248 votes := big.NewInt(0) 1249 for j := range cs { 1250 if cs[j].Key == kvs[i].Key { 1251 votes = cs[j].Votes 1252 break 1253 } 1254 } 1255 kvs[i].Votes = votes 1256 } 1257 return sbVals, kvs, nil 1258 } 1259 pubs := make(keys.PublicKeys, count) 1260 for i := range pubs { 1261 pubs[i], err = cs[i].PublicKey() 1262 if err != nil { 1263 return nil, nil, err 1264 } 1265 } 1266 return pubs, cs[:count], nil 1267 } 1268 1269 func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item { 1270 result := n.GetNextBlockValidatorsInternal(ic.DAO) 1271 return pubsToArray(result) 1272 } 1273 1274 // GetNextBlockValidatorsInternal returns next block validators. 1275 func (n *NEO) GetNextBlockValidatorsInternal(d *dao.Simple) keys.PublicKeys { 1276 cache := d.GetROCache(n.ID).(*NeoCache) 1277 return cache.nextValidators.Copy() 1278 } 1279 1280 // BalanceOf returns native NEO token balance for the acc. 1281 func (n *NEO) BalanceOf(d *dao.Simple, acc util.Uint160) (*big.Int, uint32) { 1282 key := makeAccountKey(acc) 1283 si := d.GetStorageItem(n.ID, key) 1284 if si == nil { 1285 return big.NewInt(0), 0 1286 } 1287 st, err := state.NEOBalanceFromBytes(si) 1288 if err != nil { 1289 panic(fmt.Errorf("failed to decode NEO balance state: %w", err)) 1290 } 1291 return &st.Balance, st.BalanceHeight 1292 } 1293 1294 func pubsToArray(pubs keys.PublicKeys) stackitem.Item { 1295 arr := make([]stackitem.Item, len(pubs)) 1296 for i := range pubs { 1297 arr[i] = stackitem.NewByteArray(pubs[i].Bytes()) 1298 } 1299 return stackitem.NewArray(arr) 1300 } 1301 1302 func toPublicKey(s stackitem.Item) *keys.PublicKey { 1303 buf, err := s.TryBytes() 1304 if err != nil { 1305 panic(err) 1306 } 1307 pub := new(keys.PublicKey) 1308 if err := pub.DecodeBytes(buf); err != nil { 1309 panic(err) 1310 } 1311 return pub 1312 } 1313 1314 // putGASRecord is a helper which creates key and puts GASPerBlock value into the storage. 1315 func (n *NEO) putGASRecord(dao *dao.Simple, index uint32, value *big.Int) { 1316 key := make([]byte, 5) 1317 key[0] = prefixGASPerBlock 1318 binary.BigEndian.PutUint32(key[1:], index) 1319 dao.PutBigInt(n.ID, key, value) 1320 }