github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/staking/protocol.go (about) 1 // Copyright (c) 2020 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package staking 7 8 import ( 9 "context" 10 "encoding/hex" 11 "math/big" 12 "time" 13 14 "github.com/pkg/errors" 15 "go.uber.org/zap" 16 "google.golang.org/protobuf/proto" 17 18 "github.com/iotexproject/go-pkgs/hash" 19 "github.com/iotexproject/iotex-address/address" 20 "github.com/iotexproject/iotex-proto/golang/iotexapi" 21 "github.com/iotexproject/iotex-proto/golang/iotextypes" 22 23 "github.com/iotexproject/iotex-core/action" 24 "github.com/iotexproject/iotex-core/action/protocol" 25 accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util" 26 "github.com/iotexproject/iotex-core/action/protocol/rolldpos" 27 "github.com/iotexproject/iotex-core/blockchain/genesis" 28 "github.com/iotexproject/iotex-core/pkg/log" 29 "github.com/iotexproject/iotex-core/state" 30 ) 31 32 const ( 33 // _protocolID is the protocol ID 34 _protocolID = "staking" 35 36 // _stakingNameSpace is the bucket name for staking state 37 _stakingNameSpace = "Staking" 38 39 // _candidateNameSpace is the bucket name for candidate state 40 _candidateNameSpace = "Candidate" 41 42 // CandsMapNS is the bucket name to store candidate map 43 CandsMapNS = "CandsMap" 44 ) 45 46 const ( 47 // keys in the namespace StakingNameSpace are prefixed with 1-byte tag, which serves 2 purposes: 48 // 1. to be able to store multiple objects under the same key (like bucket index for voter and candidate) 49 // 2. can call underlying KVStore's Filter() to retrieve a certain type of objects 50 _const = byte(iota) 51 _bucket 52 _voterIndex 53 _candIndex 54 _endorsement 55 ) 56 57 // Errors 58 var ( 59 ErrWithdrawnBucket = errors.New("the bucket is already withdrawn") 60 ErrEndorsementNotExist = errors.New("the endorsement does not exist") 61 TotalBucketKey = append([]byte{_const}, []byte("totalBucket")...) 62 ) 63 64 var ( 65 _nameKey = []byte("name") 66 _operatorKey = []byte("operator") 67 _ownerKey = []byte("owner") 68 ) 69 70 type ( 71 // ReceiptError indicates a non-critical error with corresponding receipt status 72 ReceiptError interface { 73 Error() string 74 ReceiptStatus() uint64 75 } 76 77 // Protocol defines the protocol of handling staking 78 Protocol struct { 79 addr address.Address 80 depositGas DepositGas 81 config Configuration 82 candBucketsIndexer *CandidatesBucketsIndexer 83 contractStakingIndexer ContractStakingIndexer 84 voteReviser *VoteReviser 85 patch *PatchStore 86 } 87 88 // Configuration is the staking protocol configuration. 89 Configuration struct { 90 VoteWeightCalConsts genesis.VoteWeightCalConsts 91 RegistrationConsts RegistrationConsts 92 WithdrawWaitingPeriod time.Duration 93 MinStakeAmount *big.Int 94 BootstrapCandidates []genesis.BootstrapCandidate 95 PersistStakingPatchBlock uint64 96 EndorsementWithdrawWaitingBlocks uint64 97 } 98 99 // DepositGas deposits gas to some pool 100 DepositGas func(ctx context.Context, sm protocol.StateManager, amount *big.Int) (*action.TransactionLog, error) 101 ) 102 103 // FindProtocol return a registered protocol from registry 104 func FindProtocol(registry *protocol.Registry) *Protocol { 105 if registry == nil { 106 return nil 107 } 108 p, ok := registry.Find(_protocolID) 109 if !ok { 110 return nil 111 } 112 rp, ok := p.(*Protocol) 113 if !ok { 114 log.S().Panic("fail to cast rolldpos protocol") 115 } 116 return rp 117 } 118 119 // NewProtocol instantiates the protocol of staking 120 func NewProtocol( 121 depositGas DepositGas, 122 cfg *BuilderConfig, 123 candBucketsIndexer *CandidatesBucketsIndexer, 124 contractStakingIndexer ContractStakingIndexer, 125 correctCandsHeight uint64, 126 reviseHeights ...uint64, 127 ) (*Protocol, error) { 128 h := hash.Hash160b([]byte(_protocolID)) 129 addr, err := address.FromBytes(h[:]) 130 if err != nil { 131 return nil, err 132 } 133 134 minStakeAmount, ok := new(big.Int).SetString(cfg.Staking.MinStakeAmount, 10) 135 if !ok { 136 return nil, action.ErrInvalidAmount 137 } 138 139 regFee, ok := new(big.Int).SetString(cfg.Staking.RegistrationConsts.Fee, 10) 140 if !ok { 141 return nil, action.ErrInvalidAmount 142 } 143 144 minSelfStake, ok := new(big.Int).SetString(cfg.Staking.RegistrationConsts.MinSelfStake, 10) 145 if !ok { 146 return nil, action.ErrInvalidAmount 147 } 148 149 // new vote reviser, revise ate greenland 150 voteReviser := NewVoteReviser(cfg.Staking.VoteWeightCalConsts, correctCandsHeight, reviseHeights...) 151 152 return &Protocol{ 153 addr: addr, 154 config: Configuration{ 155 VoteWeightCalConsts: cfg.Staking.VoteWeightCalConsts, 156 RegistrationConsts: RegistrationConsts{ 157 Fee: regFee, 158 MinSelfStake: minSelfStake, 159 }, 160 WithdrawWaitingPeriod: cfg.Staking.WithdrawWaitingPeriod, 161 MinStakeAmount: minStakeAmount, 162 BootstrapCandidates: cfg.Staking.BootstrapCandidates, 163 PersistStakingPatchBlock: cfg.PersistStakingPatchBlock, 164 EndorsementWithdrawWaitingBlocks: cfg.Staking.EndorsementWithdrawWaitingBlocks, 165 }, 166 depositGas: depositGas, 167 candBucketsIndexer: candBucketsIndexer, 168 voteReviser: voteReviser, 169 patch: NewPatchStore(cfg.StakingPatchDir), 170 contractStakingIndexer: contractStakingIndexer, 171 }, nil 172 } 173 174 // ProtocolAddr returns the address generated from protocol id 175 func ProtocolAddr() address.Address { 176 return protocol.HashStringToAddress(_protocolID) 177 } 178 179 // Start starts the protocol 180 func (p *Protocol) Start(ctx context.Context, sr protocol.StateReader) (interface{}, error) { 181 featureCtx := protocol.MustGetFeatureWithHeightCtx(ctx) 182 height, err := sr.Height() 183 if err != nil { 184 return nil, err 185 } 186 187 // load view from SR 188 c, _, err := CreateBaseView(sr, featureCtx.ReadStateFromDB(height)) 189 if err != nil { 190 return nil, errors.Wrap(err, "failed to start staking protocol") 191 } 192 193 if p.needToReadCandsMap(ctx, height) { 194 name, operator, owners, err := readCandCenterStateFromStateDB(sr) 195 if err != nil { 196 // stateDB does not have name/operator map yet 197 if name, operator, owners, err = p.patch.Read(height); err != nil { 198 return nil, errors.Wrap(err, "failed to read name/operator map") 199 } 200 } 201 if err = c.candCenter.base.loadNameOperatorMapOwnerList(name, operator, owners); err != nil { 202 return nil, errors.Wrap(err, "failed to load name/operator map to cand center") 203 } 204 } 205 return c, nil 206 } 207 208 // CreateGenesisStates is used to setup BootstrapCandidates from genesis config. 209 func (p *Protocol) CreateGenesisStates( 210 ctx context.Context, 211 sm protocol.StateManager, 212 ) error { 213 if len(p.config.BootstrapCandidates) == 0 { 214 return nil 215 } 216 // TODO: set init values based on ctx 217 csm, err := NewCandidateStateManager(sm, false) 218 if err != nil { 219 return err 220 } 221 222 for _, bc := range p.config.BootstrapCandidates { 223 owner, err := address.FromString(bc.OwnerAddress) 224 if err != nil { 225 return err 226 } 227 228 operator, err := address.FromString(bc.OperatorAddress) 229 if err != nil { 230 return err 231 } 232 233 reward, err := address.FromString(bc.RewardAddress) 234 if err != nil { 235 return err 236 } 237 238 selfStake, ok := new(big.Int).SetString(bc.SelfStakingTokens, 10) 239 if !ok { 240 return action.ErrInvalidAmount 241 } 242 bucket := NewVoteBucket(owner, owner, selfStake, 7, time.Now(), true) 243 bucketIdx, err := csm.putBucketAndIndex(bucket) 244 if err != nil { 245 return err 246 } 247 c := &Candidate{ 248 Owner: owner, 249 Operator: operator, 250 Reward: reward, 251 Name: bc.Name, 252 Votes: p.calculateVoteWeight(bucket, true), 253 SelfStakeBucketIdx: bucketIdx, 254 SelfStake: selfStake, 255 } 256 257 // put in statedb and cand center 258 if err := csm.Upsert(c); err != nil { 259 return err 260 } 261 if err := csm.DebitBucketPool(selfStake, true); err != nil { 262 return err 263 } 264 } 265 266 // commit updated view 267 return errors.Wrap(csm.Commit(ctx), "failed to commit candidate change in CreateGenesisStates") 268 } 269 270 // CreatePreStates updates state manager 271 func (p *Protocol) CreatePreStates(ctx context.Context, sm protocol.StateManager) error { 272 g := genesis.MustExtractGenesisContext(ctx) 273 blkCtx := protocol.MustGetBlockCtx(ctx) 274 featureCtx := protocol.MustGetFeatureCtx(ctx) 275 featureWithHeightCtx := protocol.MustGetFeatureWithHeightCtx(ctx) 276 if blkCtx.BlockHeight == g.GreenlandBlockHeight { 277 csr, err := ConstructBaseView(sm) 278 if err != nil { 279 return err 280 } 281 if _, err = sm.PutState(csr.BaseView().bucketPool.total, protocol.NamespaceOption(_stakingNameSpace), protocol.KeyOption(_bucketPoolAddrKey)); err != nil { 282 return err 283 } 284 } 285 286 if p.voteReviser.NeedRevise(blkCtx.BlockHeight) { 287 csm, err := NewCandidateStateManager(sm, featureWithHeightCtx.ReadStateFromDB(blkCtx.BlockHeight)) 288 if err != nil { 289 return err 290 } 291 if err := p.voteReviser.Revise(csm, blkCtx.BlockHeight); err != nil { 292 return err 293 } 294 } 295 if p.candBucketsIndexer == nil { 296 return nil 297 } 298 rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx)) 299 currentEpochNum := rp.GetEpochNum(blkCtx.BlockHeight) 300 if currentEpochNum == 0 { 301 return nil 302 } 303 epochStartHeight := rp.GetEpochHeight(currentEpochNum) 304 if epochStartHeight != blkCtx.BlockHeight || featureCtx.SkipStakingIndexer { 305 return nil 306 } 307 308 return p.handleStakingIndexer(rp.GetEpochHeight(currentEpochNum-1), sm) 309 } 310 311 func (p *Protocol) handleStakingIndexer(epochStartHeight uint64, sm protocol.StateManager) error { 312 csr := newCandidateStateReader(sm) 313 allBuckets, _, err := csr.getAllBuckets() 314 if err != nil && errors.Cause(err) != state.ErrStateNotExist { 315 return err 316 } 317 buckets, err := toIoTeXTypesVoteBucketList(sm, allBuckets) 318 if err != nil { 319 return err 320 } 321 err = p.candBucketsIndexer.PutBuckets(epochStartHeight, buckets) 322 if err != nil { 323 return err 324 } 325 all, _, err := csr.getAllCandidates() 326 if err != nil && errors.Cause(err) != state.ErrStateNotExist { 327 return err 328 } 329 candidateList := toIoTeXTypesCandidateListV2(all) 330 return p.candBucketsIndexer.PutCandidates(epochStartHeight, candidateList) 331 } 332 333 // PreCommit preforms pre-commit 334 func (p *Protocol) PreCommit(ctx context.Context, sm protocol.StateManager) error { 335 height, err := sm.Height() 336 if err != nil { 337 return err 338 } 339 if !p.needToWriteCandsMap(ctx, height) { 340 return nil 341 } 342 343 featureWithHeightCtx := protocol.MustGetFeatureWithHeightCtx(ctx) 344 csm, err := NewCandidateStateManager(sm, featureWithHeightCtx.ReadStateFromDB(height)) 345 if err != nil { 346 return err 347 } 348 cc := csm.DirtyView().candCenter 349 base := cc.base.clone() 350 if _, err = base.commit(cc.change, featureWithHeightCtx.CandCenterHasAlias(height)); err != nil { 351 return errors.Wrap(err, "failed to apply candidate change in pre-commit") 352 } 353 // persist nameMap/operatorMap and ownerList to stateDB 354 name := base.candsInNameMap() 355 op := base.candsInOperatorMap() 356 owners := base.ownersList() 357 if len(name) == 0 || len(op) == 0 { 358 return ErrNilParameters 359 } 360 if err := writeCandCenterStateToStateDB(sm, name, op, owners); err != nil { 361 return errors.Wrap(err, "failed to write name/operator map to stateDB") 362 } 363 return nil 364 } 365 366 // Commit commits the last change 367 func (p *Protocol) Commit(ctx context.Context, sm protocol.StateManager) error { 368 featureWithHeightCtx := protocol.MustGetFeatureWithHeightCtx(ctx) 369 height, err := sm.Height() 370 if err != nil { 371 return err 372 } 373 csm, err := NewCandidateStateManager(sm, featureWithHeightCtx.ReadStateFromDB(height)) 374 if err != nil { 375 return err 376 } 377 378 // commit updated view 379 return errors.Wrap(csm.Commit(ctx), "failed to commit candidate change in Commit") 380 } 381 382 // Handle handles a staking message 383 func (p *Protocol) Handle(ctx context.Context, act action.Action, sm protocol.StateManager) (*action.Receipt, error) { 384 featureWithHeightCtx := protocol.MustGetFeatureWithHeightCtx(ctx) 385 height, err := sm.Height() 386 if err != nil { 387 return nil, err 388 } 389 csm, err := NewCandidateStateManager(sm, featureWithHeightCtx.ReadStateFromDB(height)) 390 if err != nil { 391 return nil, err 392 } 393 394 return p.handle(ctx, act, csm) 395 } 396 397 func (p *Protocol) handle(ctx context.Context, act action.Action, csm CandidateStateManager) (*action.Receipt, error) { 398 var ( 399 rLog *receiptLog 400 tLogs []*action.TransactionLog 401 err error 402 logs []*action.Log 403 ) 404 405 switch act := act.(type) { 406 case *action.CreateStake: 407 rLog, tLogs, err = p.handleCreateStake(ctx, act, csm) 408 case *action.Unstake: 409 rLog, err = p.handleUnstake(ctx, act, csm) 410 case *action.WithdrawStake: 411 rLog, tLogs, err = p.handleWithdrawStake(ctx, act, csm) 412 case *action.ChangeCandidate: 413 rLog, err = p.handleChangeCandidate(ctx, act, csm) 414 case *action.TransferStake: 415 rLog, err = p.handleTransferStake(ctx, act, csm) 416 case *action.DepositToStake: 417 rLog, tLogs, err = p.handleDepositToStake(ctx, act, csm) 418 case *action.Restake: 419 rLog, err = p.handleRestake(ctx, act, csm) 420 case *action.CandidateRegister: 421 rLog, tLogs, err = p.handleCandidateRegister(ctx, act, csm) 422 case *action.CandidateUpdate: 423 rLog, err = p.handleCandidateUpdate(ctx, act, csm) 424 case *action.CandidateActivate: 425 rLog, tLogs, err = p.handleCandidateActivate(ctx, act, csm) 426 case *action.CandidateEndorsement: 427 rLog, tLogs, err = p.handleCandidateEndorsement(ctx, act, csm) 428 default: 429 return nil, nil 430 } 431 432 if l := rLog.Build(ctx, err); l != nil { 433 logs = append(logs, l) 434 } 435 if err == nil { 436 return p.settleAction(ctx, csm.SM(), uint64(iotextypes.ReceiptStatus_Success), logs, tLogs) 437 } 438 439 if receiptErr, ok := err.(ReceiptError); ok { 440 actionCtx := protocol.MustGetActionCtx(ctx) 441 log.L().With( 442 zap.String("actionHash", hex.EncodeToString(actionCtx.ActionHash[:]))).Debug("Failed to commit staking action", zap.Error(err)) 443 return p.settleAction(ctx, csm.SM(), receiptErr.ReceiptStatus(), logs, tLogs) 444 } 445 return nil, err 446 } 447 448 // Validate validates a staking message 449 func (p *Protocol) Validate(ctx context.Context, act action.Action, sr protocol.StateReader) error { 450 if act == nil { 451 return action.ErrNilAction 452 } 453 switch act := act.(type) { 454 case *action.CreateStake: 455 return p.validateCreateStake(ctx, act) 456 case *action.Unstake: 457 return p.validateUnstake(ctx, act) 458 case *action.WithdrawStake: 459 return p.validateWithdrawStake(ctx, act) 460 case *action.ChangeCandidate: 461 return p.validateChangeCandidate(ctx, act) 462 case *action.TransferStake: 463 return p.validateTransferStake(ctx, act) 464 case *action.DepositToStake: 465 return p.validateDepositToStake(ctx, act) 466 case *action.Restake: 467 return p.validateRestake(ctx, act) 468 case *action.CandidateRegister: 469 return p.validateCandidateRegister(ctx, act) 470 case *action.CandidateUpdate: 471 return p.validateCandidateUpdate(ctx, act) 472 case *action.CandidateActivate: 473 return p.validateCandidateActivate(ctx, act) 474 case *action.CandidateEndorsement: 475 return p.validateCandidateEndorsement(ctx, act) 476 } 477 return nil 478 } 479 480 func (p *Protocol) isActiveCandidate(ctx context.Context, csr CandidateStateReader, cand *Candidate) (bool, error) { 481 if cand.SelfStake.Cmp(p.config.RegistrationConsts.MinSelfStake) < 0 { 482 return false, nil 483 } 484 featureCtx := protocol.MustGetFeatureCtx(ctx) 485 if featureCtx.DisableDelegateEndorsement { 486 // before endorsement feature, candidates with enough amount must be active 487 return true, nil 488 } 489 bucket, err := csr.getBucket(cand.SelfStakeBucketIdx) 490 switch { 491 case errors.Cause(err) == state.ErrStateNotExist: 492 // endorse bucket has been withdrawn 493 return false, nil 494 case err != nil: 495 return false, errors.Wrapf(err, "failed to get bucket %d", cand.SelfStakeBucketIdx) 496 default: 497 } 498 selfStake, err := isSelfStakeBucket(featureCtx, csr, bucket) 499 if err != nil { 500 return false, errors.Wrapf(err, "failed to check self-stake bucket %d", cand.SelfStakeBucketIdx) 501 } 502 return selfStake, nil 503 } 504 505 // ActiveCandidates returns all active candidates in candidate center 506 func (p *Protocol) ActiveCandidates(ctx context.Context, sr protocol.StateReader, height uint64) (state.CandidateList, error) { 507 srHeight, err := sr.Height() 508 if err != nil { 509 return nil, errors.Wrap(err, "failed to get StateReader height") 510 } 511 c, err := ConstructBaseView(sr) 512 if err != nil { 513 return nil, errors.Wrap(err, "failed to get ActiveCandidates") 514 } 515 list := c.AllCandidates() 516 cand := make(CandidateList, 0, len(list)) 517 featureCtx := protocol.MustGetFeatureCtx(ctx) 518 for i := range list { 519 if p.contractStakingIndexer != nil && featureCtx.AddContractStakingVotes { 520 // specifying the height param instead of query latest from indexer directly, aims to cause error when indexer falls behind 521 // currently there are two possible sr (i.e. factory or workingSet), it means the height could be chain height or current block height 522 // using height-1 will cover the two scenario while detect whether the indexer is lagging behind 523 contractVotes, err := p.contractStakingIndexer.CandidateVotes(ctx, list[i].Owner, srHeight-1) 524 if err != nil { 525 return nil, errors.Wrap(err, "failed to get CandidateVotes from contractStakingIndexer") 526 } 527 list[i].Votes.Add(list[i].Votes, contractVotes) 528 } 529 active, err := p.isActiveCandidate(ctx, c, list[i]) 530 if err != nil { 531 return nil, err 532 } 533 if active { 534 cand = append(cand, list[i]) 535 } 536 } 537 return cand.toStateCandidateList() 538 } 539 540 // ReadState read the state on blockchain via protocol 541 func (p *Protocol) ReadState(ctx context.Context, sr protocol.StateReader, method []byte, args ...[]byte) ([]byte, uint64, error) { 542 m := iotexapi.ReadStakingDataMethod{} 543 if err := proto.Unmarshal(method, &m); err != nil { 544 return nil, uint64(0), errors.Wrap(err, "failed to unmarshal method name") 545 } 546 if len(args) != 1 { 547 return nil, uint64(0), errors.Errorf("invalid number of arguments %d", len(args)) 548 } 549 r := iotexapi.ReadStakingDataRequest{} 550 if err := proto.Unmarshal(args[0], &r); err != nil { 551 return nil, uint64(0), errors.Wrap(err, "failed to unmarshal request") 552 } 553 554 // stakeSR is the stake state reader including native and contract staking 555 stakeSR, err := newCompositeStakingStateReader(p.contractStakingIndexer, p.candBucketsIndexer, sr) 556 if err != nil { 557 return nil, 0, err 558 } 559 560 // get height arg 561 inputHeight, err := sr.Height() 562 if err != nil { 563 return nil, 0, err 564 } 565 rp := rolldpos.MustGetProtocol(protocol.MustGetRegistry(ctx)) 566 epochStartHeight := rp.GetEpochHeight(rp.GetEpochNum(inputHeight)) 567 nativeSR, err := ConstructBaseView(sr) 568 if err != nil { 569 return nil, 0, err 570 } 571 572 var ( 573 height uint64 574 resp proto.Message 575 ) 576 switch m.GetMethod() { 577 case iotexapi.ReadStakingDataMethod_BUCKETS: 578 if epochStartHeight != 0 && p.candBucketsIndexer != nil { 579 resp, height, err = p.candBucketsIndexer.GetBuckets(epochStartHeight, r.GetBuckets().GetPagination().GetOffset(), r.GetBuckets().GetPagination().GetLimit()) 580 } else { 581 resp, height, err = nativeSR.readStateBuckets(ctx, r.GetBuckets()) 582 } 583 case iotexapi.ReadStakingDataMethod_BUCKETS_BY_VOTER: 584 resp, height, err = nativeSR.readStateBucketsByVoter(ctx, r.GetBucketsByVoter()) 585 case iotexapi.ReadStakingDataMethod_BUCKETS_BY_CANDIDATE: 586 resp, height, err = nativeSR.readStateBucketsByCandidate(ctx, r.GetBucketsByCandidate()) 587 case iotexapi.ReadStakingDataMethod_BUCKETS_BY_INDEXES: 588 resp, height, err = nativeSR.readStateBucketByIndices(ctx, r.GetBucketsByIndexes()) 589 case iotexapi.ReadStakingDataMethod_BUCKETS_COUNT: 590 resp, height, err = nativeSR.readStateBucketCount(ctx, r.GetBucketsCount()) 591 case iotexapi.ReadStakingDataMethod_CANDIDATES: 592 resp, height, err = stakeSR.readStateCandidates(ctx, r.GetCandidates()) 593 case iotexapi.ReadStakingDataMethod_CANDIDATE_BY_NAME: 594 resp, height, err = stakeSR.readStateCandidateByName(ctx, r.GetCandidateByName()) 595 case iotexapi.ReadStakingDataMethod_CANDIDATE_BY_ADDRESS: 596 resp, height, err = stakeSR.readStateCandidateByAddress(ctx, r.GetCandidateByAddress()) 597 case iotexapi.ReadStakingDataMethod_TOTAL_STAKING_AMOUNT: 598 resp, height, err = nativeSR.readStateTotalStakingAmount(ctx, r.GetTotalStakingAmount()) 599 case iotexapi.ReadStakingDataMethod_COMPOSITE_BUCKETS: 600 resp, height, err = stakeSR.readStateBuckets(ctx, r.GetBuckets()) 601 case iotexapi.ReadStakingDataMethod_COMPOSITE_BUCKETS_BY_VOTER: 602 resp, height, err = stakeSR.readStateBucketsByVoter(ctx, r.GetBucketsByVoter()) 603 case iotexapi.ReadStakingDataMethod_COMPOSITE_BUCKETS_BY_CANDIDATE: 604 resp, height, err = stakeSR.readStateBucketsByCandidate(ctx, r.GetBucketsByCandidate()) 605 case iotexapi.ReadStakingDataMethod_COMPOSITE_BUCKETS_BY_INDEXES: 606 resp, height, err = stakeSR.readStateBucketByIndices(ctx, r.GetBucketsByIndexes()) 607 case iotexapi.ReadStakingDataMethod_COMPOSITE_BUCKETS_COUNT: 608 resp, height, err = stakeSR.readStateBucketCount(ctx, r.GetBucketsCount()) 609 case iotexapi.ReadStakingDataMethod_COMPOSITE_TOTAL_STAKING_AMOUNT: 610 resp, height, err = stakeSR.readStateTotalStakingAmount(ctx, r.GetTotalStakingAmount()) 611 case iotexapi.ReadStakingDataMethod_CONTRACT_STAKING_BUCKET_TYPES: 612 resp, height, err = stakeSR.readStateContractStakingBucketTypes(ctx, r.GetContractStakingBucketTypes()) 613 default: 614 err = errors.New("corresponding method isn't found") 615 } 616 if err != nil { 617 return nil, height, err 618 } 619 data, err := proto.Marshal(resp) 620 if err != nil { 621 return nil, height, err 622 } 623 return data, height, nil 624 } 625 626 // Register registers the protocol with a unique ID 627 func (p *Protocol) Register(r *protocol.Registry) error { 628 return r.Register(_protocolID, p) 629 } 630 631 // ForceRegister registers the protocol with a unique ID and force replacing the previous protocol if it exists 632 func (p *Protocol) ForceRegister(r *protocol.Registry) error { 633 return r.ForceRegister(_protocolID, p) 634 } 635 636 // Name returns the name of protocol 637 func (p *Protocol) Name() string { 638 return _protocolID 639 } 640 641 func (p *Protocol) calculateVoteWeight(v *VoteBucket, selfStake bool) *big.Int { 642 return CalculateVoteWeight(p.config.VoteWeightCalConsts, v, selfStake) 643 } 644 645 // settleAccount deposits gas fee and updates caller's nonce 646 func (p *Protocol) settleAction( 647 ctx context.Context, 648 sm protocol.StateManager, 649 status uint64, 650 logs []*action.Log, 651 tLogs []*action.TransactionLog, 652 ) (*action.Receipt, error) { 653 actionCtx := protocol.MustGetActionCtx(ctx) 654 blkCtx := protocol.MustGetBlockCtx(ctx) 655 gasFee := big.NewInt(0).Mul(actionCtx.GasPrice, big.NewInt(0).SetUint64(actionCtx.IntrinsicGas)) 656 depositLog, err := p.depositGas(ctx, sm, gasFee) 657 if err != nil { 658 return nil, errors.Wrap(err, "failed to deposit gas") 659 } 660 accountCreationOpts := []state.AccountCreationOption{} 661 if protocol.MustGetFeatureCtx(ctx).CreateLegacyNonceAccount { 662 accountCreationOpts = append(accountCreationOpts, state.LegacyNonceAccountTypeOption()) 663 } 664 acc, err := accountutil.LoadAccount(sm, actionCtx.Caller, accountCreationOpts...) 665 if err != nil { 666 return nil, err 667 } 668 if err := acc.SetPendingNonce(actionCtx.Nonce + 1); err != nil { 669 return nil, errors.Wrap(err, "failed to set nonce") 670 } 671 if err := accountutil.StoreAccount(sm, actionCtx.Caller, acc); err != nil { 672 return nil, errors.Wrap(err, "failed to update nonce") 673 } 674 r := action.Receipt{ 675 Status: status, 676 BlockHeight: blkCtx.BlockHeight, 677 ActionHash: actionCtx.ActionHash, 678 GasConsumed: actionCtx.IntrinsicGas, 679 ContractAddress: p.addr.String(), 680 } 681 r.AddLogs(logs...).AddTransactionLogs(depositLog).AddTransactionLogs(tLogs...) 682 return &r, nil 683 } 684 685 func (p *Protocol) needToReadCandsMap(ctx context.Context, height uint64) bool { 686 fCtx := protocol.MustGetFeatureWithHeightCtx(ctx) 687 return height > p.config.PersistStakingPatchBlock && fCtx.CandCenterHasAlias(height) 688 } 689 690 func (p *Protocol) needToWriteCandsMap(ctx context.Context, height uint64) bool { 691 fCtx := protocol.MustGetFeatureWithHeightCtx(ctx) 692 return height >= p.config.PersistStakingPatchBlock && fCtx.CandCenterHasAlias(height) 693 } 694 695 func readCandCenterStateFromStateDB(sr protocol.StateReader) (CandidateList, CandidateList, CandidateList, error) { 696 var ( 697 name, operator, owner CandidateList 698 ) 699 if _, err := sr.State(&name, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_nameKey)); err != nil { 700 return nil, nil, nil, err 701 } 702 if _, err := sr.State(&operator, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_operatorKey)); err != nil { 703 return nil, nil, nil, err 704 } 705 if _, err := sr.State(&owner, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_ownerKey)); err != nil { 706 return nil, nil, nil, err 707 } 708 return name, operator, owner, nil 709 } 710 711 func writeCandCenterStateToStateDB(sm protocol.StateManager, name, op, owners CandidateList) error { 712 if _, err := sm.PutState(name, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_nameKey)); err != nil { 713 return err 714 } 715 if _, err := sm.PutState(op, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_operatorKey)); err != nil { 716 return err 717 } 718 _, err := sm.PutState(owners, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_ownerKey)) 719 return err 720 } 721 722 // isSelfStakeBucket returns true if the bucket is self-stake bucket and not expired 723 func isSelfStakeBucket(featureCtx protocol.FeatureCtx, csc CandidiateStateCommon, bucket *VoteBucket) (bool, error) { 724 // bucket index should be settled in one of candidates 725 selfStake := csc.ContainsSelfStakingBucket(bucket.Index) 726 if featureCtx.DisableDelegateEndorsement || !selfStake { 727 return selfStake, nil 728 } 729 730 // bucket should not be unstaked if it is self-owned 731 if address.Equal(bucket.Owner, bucket.Candidate) { 732 return !bucket.isUnstaked(), nil 733 } 734 // otherwise bucket should be an endorse bucket which is not expired 735 esm := NewEndorsementStateReader(csc.SR()) 736 height, err := esm.Height() 737 if err != nil { 738 return false, err 739 } 740 endorse, err := esm.Get(bucket.Index) 741 if err != nil { 742 return false, err 743 } 744 return endorse.Status(height) != EndorseExpired, nil 745 }