github.com/iotexproject/iotex-core@v1.14.1-rc1/action/protocol/staking/candidate_statereader.go (about) 1 // Copyright (c) 2022 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 "math/big" 11 12 "github.com/pkg/errors" 13 14 "github.com/iotexproject/iotex-address/address" 15 "github.com/iotexproject/iotex-proto/golang/iotexapi" 16 "github.com/iotexproject/iotex-proto/golang/iotextypes" 17 18 "github.com/iotexproject/iotex-core/action/protocol" 19 "github.com/iotexproject/iotex-core/state" 20 ) 21 22 type ( 23 // BucketGetByIndex related to obtaining bucket by index 24 BucketGetByIndex interface { 25 getBucket(index uint64) (*VoteBucket, error) 26 } 27 // BucketGet related to obtaining bucket 28 BucketGet interface { 29 BucketGetByIndex 30 getTotalBucketCount() (uint64, error) 31 getAllBuckets() ([]*VoteBucket, uint64, error) 32 getBucketsWithIndices(indices BucketIndices) ([]*VoteBucket, error) 33 getBucketIndices(addr address.Address, prefix byte) (*BucketIndices, uint64, error) 34 voterBucketIndices(addr address.Address) (*BucketIndices, uint64, error) 35 candBucketIndices(addr address.Address) (*BucketIndices, uint64, error) 36 } 37 // CandidateGet related to obtaining Candidate 38 CandidateGet interface { 39 getCandidate(name address.Address) (*Candidate, uint64, error) 40 getAllCandidates() (CandidateList, uint64, error) 41 } 42 // ReadState related to read bucket and candidate by request 43 ReadState interface { 44 readStateBuckets(ctx context.Context, req *iotexapi.ReadStakingDataRequest_VoteBuckets) (*iotextypes.VoteBucketList, uint64, error) 45 readStateBucketsByVoter(ctx context.Context, req *iotexapi.ReadStakingDataRequest_VoteBucketsByVoter) (*iotextypes.VoteBucketList, uint64, error) 46 readStateBucketsByCandidate(ctx context.Context, req *iotexapi.ReadStakingDataRequest_VoteBucketsByCandidate) (*iotextypes.VoteBucketList, uint64, error) 47 readStateBucketByIndices(ctx context.Context, req *iotexapi.ReadStakingDataRequest_VoteBucketsByIndexes) (*iotextypes.VoteBucketList, uint64, error) 48 readStateBucketCount(ctx context.Context, _ *iotexapi.ReadStakingDataRequest_BucketsCount) (*iotextypes.BucketsCount, uint64, error) 49 readStateCandidates(ctx context.Context, req *iotexapi.ReadStakingDataRequest_Candidates) (*iotextypes.CandidateListV2, uint64, error) 50 readStateCandidateByName(ctx context.Context, req *iotexapi.ReadStakingDataRequest_CandidateByName) (*iotextypes.CandidateV2, uint64, error) 51 readStateCandidateByAddress(ctx context.Context, req *iotexapi.ReadStakingDataRequest_CandidateByAddress) (*iotextypes.CandidateV2, uint64, error) 52 readStateTotalStakingAmount(ctx context.Context, _ *iotexapi.ReadStakingDataRequest_TotalStakingAmount) (*iotextypes.AccountMeta, uint64, error) 53 } 54 // CandidateStateReader contains candidate center and bucket pool 55 CandidateStateReader interface { 56 BucketGet 57 CandidateGet 58 ReadState 59 Height() uint64 60 SR() protocol.StateReader 61 BaseView() *ViewData 62 NewBucketPool(enableSMStorage bool) (*BucketPool, error) 63 GetCandidateByName(string) *Candidate 64 GetCandidateByOwner(address.Address) *Candidate 65 AllCandidates() CandidateList 66 TotalStakedAmount() *big.Int 67 ActiveBucketsCount() uint64 68 ContainsSelfStakingBucket(index uint64) bool 69 } 70 71 candSR struct { 72 protocol.StateReader 73 height uint64 74 view *ViewData 75 } 76 77 // ViewData is the data that need to be stored in protocol's view 78 ViewData struct { 79 candCenter *CandidateCenter 80 bucketPool *BucketPool 81 } 82 ) 83 84 func newCandidateStateReader(sr protocol.StateReader) CandidateStateReader { 85 return &candSR{ 86 StateReader: sr, 87 } 88 } 89 90 func (c *candSR) Height() uint64 { 91 return c.height 92 } 93 94 func (c *candSR) SR() protocol.StateReader { 95 return c.StateReader 96 } 97 98 func (c *candSR) BaseView() *ViewData { 99 return c.view 100 } 101 102 func (c *candSR) GetCandidateByName(name string) *Candidate { 103 return c.view.candCenter.GetByName(name) 104 } 105 106 func (c *candSR) GetCandidateByOwner(owner address.Address) *Candidate { 107 return c.view.candCenter.GetByOwner(owner) 108 } 109 110 func (c *candSR) AllCandidates() CandidateList { 111 return c.view.candCenter.All() 112 } 113 114 func (c *candSR) ContainsSelfStakingBucket(index uint64) bool { 115 return c.view.candCenter.ContainsSelfStakingBucket(index) 116 } 117 118 func (c *candSR) TotalStakedAmount() *big.Int { 119 return c.view.bucketPool.Total() 120 } 121 122 func (c *candSR) ActiveBucketsCount() uint64 { 123 return c.view.bucketPool.Count() 124 } 125 126 // ConstructBaseView returns a candidate state reader that reflects the base view 127 // it will be used read-only 128 func ConstructBaseView(sr protocol.StateReader) (CandidateStateReader, error) { 129 if sr == nil { 130 return nil, ErrMissingField 131 } 132 133 height, err := sr.Height() 134 if err != nil { 135 return nil, err 136 } 137 v, err := sr.ReadView(_protocolID) 138 if err != nil { 139 return nil, err 140 } 141 142 view, ok := v.(*ViewData) 143 if !ok { 144 return nil, errors.Wrap(ErrTypeAssertion, "expecting *ViewData") 145 } 146 147 return &candSR{ 148 StateReader: sr, 149 height: height, 150 view: &ViewData{ 151 candCenter: view.candCenter, 152 bucketPool: view.bucketPool, 153 }, 154 }, nil 155 } 156 157 // CreateBaseView creates the base view from state reader 158 func CreateBaseView(sr protocol.StateReader, enableSMStorage bool) (*ViewData, uint64, error) { 159 if sr == nil { 160 return nil, 0, ErrMissingField 161 } 162 163 csr := newCandidateStateReader(sr) 164 all, height, err := csr.getAllCandidates() 165 if err != nil && errors.Cause(err) != state.ErrStateNotExist { 166 return nil, height, err 167 } 168 169 center, err := NewCandidateCenter(all) 170 if err != nil { 171 return nil, height, err 172 } 173 174 pool, err := csr.NewBucketPool(enableSMStorage) 175 if err != nil { 176 return nil, height, err 177 } 178 179 return &ViewData{ 180 candCenter: center, 181 bucketPool: pool, 182 }, height, nil 183 } 184 185 func (c *candSR) getTotalBucketCount() (uint64, error) { 186 var tc totalBucketCount 187 _, err := c.State( 188 &tc, 189 protocol.NamespaceOption(_stakingNameSpace), 190 protocol.KeyOption(TotalBucketKey)) 191 return tc.count, err 192 } 193 194 func (c *candSR) getBucket(index uint64) (*VoteBucket, error) { 195 var ( 196 vb VoteBucket 197 err error 198 ) 199 if _, err = c.State( 200 &vb, 201 protocol.NamespaceOption(_stakingNameSpace), 202 protocol.KeyOption(bucketKey(index))); err != nil { 203 return nil, err 204 } 205 var tc totalBucketCount 206 if _, err := c.State( 207 &tc, 208 protocol.NamespaceOption(_stakingNameSpace), 209 protocol.KeyOption(TotalBucketKey)); err != nil && errors.Cause(err) != state.ErrStateNotExist { 210 return nil, err 211 } 212 if errors.Cause(err) == state.ErrStateNotExist && index < tc.Count() { 213 return nil, ErrWithdrawnBucket 214 } 215 return &vb, nil 216 } 217 218 func (c *candSR) getAllBuckets() ([]*VoteBucket, uint64, error) { 219 height, iter, err := c.States( 220 protocol.NamespaceOption(_stakingNameSpace), 221 protocol.KeysOption(func() ([][]byte, error) { 222 // TODO (zhi): fix potential racing issue 223 count, err := c.getTotalBucketCount() 224 if err != nil { 225 return nil, err 226 } 227 keys := [][]byte{} 228 for i := uint64(0); i < count; i++ { 229 keys = append(keys, bucketKey(i)) 230 } 231 return keys, nil 232 }), 233 ) 234 if err != nil { 235 return nil, height, err 236 } 237 238 buckets := make([]*VoteBucket, 0, iter.Size()) 239 for i := 0; i < iter.Size(); i++ { 240 vb := &VoteBucket{} 241 switch err := iter.Next(vb); errors.Cause(err) { 242 case nil: 243 buckets = append(buckets, vb) 244 case state.ErrNilValue: 245 default: 246 return nil, height, errors.Wrapf(err, "failed to deserialize bucket") 247 } 248 } 249 return buckets, height, nil 250 } 251 252 func (c *candSR) getBucketsWithIndices(indices BucketIndices) ([]*VoteBucket, error) { 253 buckets := make([]*VoteBucket, 0, len(indices)) 254 for _, i := range indices { 255 b, err := c.getBucket(i) 256 if err != nil && err != ErrWithdrawnBucket { 257 return buckets, err 258 } 259 buckets = append(buckets, b) 260 } 261 return buckets, nil 262 } 263 264 // getExistingBucketsWithIndices returns buckets with given indices, if some of bucket 265 // does not exist, they will be ignored and return the rest of buckets 266 func (c *candSR) getExistingBucketsWithIndices(indices BucketIndices) ([]*VoteBucket, error) { 267 buckets := make([]*VoteBucket, 0, len(indices)) 268 for _, i := range indices { 269 b, err := c.getBucket(i) 270 if err != nil { 271 if errors.Is(err, state.ErrStateNotExist) { 272 continue 273 } 274 return buckets, err 275 } 276 buckets = append(buckets, b) 277 } 278 return buckets, nil 279 } 280 281 func (c *candSR) getBucketIndices(addr address.Address, prefix byte) (*BucketIndices, uint64, error) { 282 var ( 283 bis BucketIndices 284 key = AddrKeyWithPrefix(addr, prefix) 285 ) 286 height, err := c.State( 287 &bis, 288 protocol.NamespaceOption(_stakingNameSpace), 289 protocol.KeyOption(key)) 290 if err != nil { 291 return nil, height, err 292 } 293 return &bis, height, nil 294 } 295 296 func (c *candSR) voterBucketIndices(addr address.Address) (*BucketIndices, uint64, error) { 297 return c.getBucketIndices(addr, _voterIndex) 298 } 299 300 func (c *candSR) candBucketIndices(addr address.Address) (*BucketIndices, uint64, error) { 301 return c.getBucketIndices(addr, _candIndex) 302 } 303 304 func (c *candSR) getCandidate(name address.Address) (*Candidate, uint64, error) { 305 if name == nil { 306 return nil, 0, ErrNilParameters 307 } 308 var d Candidate 309 height, err := c.State(&d, protocol.NamespaceOption(_candidateNameSpace), protocol.KeyOption(name.Bytes())) 310 return &d, height, err 311 } 312 313 func (c *candSR) getAllCandidates() (CandidateList, uint64, error) { 314 height, iter, err := c.States(protocol.NamespaceOption(_candidateNameSpace)) 315 if err != nil { 316 return nil, height, err 317 } 318 319 cands := make(CandidateList, 0, iter.Size()) 320 for i := 0; i < iter.Size(); i++ { 321 c := &Candidate{} 322 if err := iter.Next(c); err != nil { 323 return nil, height, errors.Wrapf(err, "failed to deserialize candidate") 324 } 325 cands = append(cands, c) 326 } 327 return cands, height, nil 328 } 329 330 func (c *candSR) NewBucketPool(enableSMStorage bool) (*BucketPool, error) { 331 bp := BucketPool{ 332 enableSMStorage: enableSMStorage, 333 total: &totalAmount{ 334 amount: big.NewInt(0), 335 }, 336 } 337 338 if bp.enableSMStorage { 339 switch _, err := c.State(bp.total, protocol.NamespaceOption(_stakingNameSpace), protocol.KeyOption(_bucketPoolAddrKey)); errors.Cause(err) { 340 case nil: 341 return &bp, nil 342 case state.ErrStateNotExist: 343 // fall back to load all buckets 344 default: 345 return nil, err 346 } 347 } 348 349 // sum up all existing buckets 350 all, _, err := c.getAllBuckets() 351 if err != nil && errors.Cause(err) != state.ErrStateNotExist { 352 return nil, err 353 } 354 355 for _, v := range all { 356 if v.StakedAmount.Cmp(big.NewInt(0)) <= 0 { 357 return nil, state.ErrNotEnoughBalance 358 } 359 bp.total.amount.Add(bp.total.amount, v.StakedAmount) 360 } 361 bp.total.count = uint64(len(all)) 362 return &bp, nil 363 } 364 365 func (c *candSR) readStateBuckets(ctx context.Context, req *iotexapi.ReadStakingDataRequest_VoteBuckets) (*iotextypes.VoteBucketList, uint64, error) { 366 all, height, err := c.getAllBuckets() 367 if err != nil { 368 return nil, height, err 369 } 370 371 offset := int(req.GetPagination().GetOffset()) 372 limit := int(req.GetPagination().GetLimit()) 373 buckets := getPageOfBuckets(all, offset, limit) 374 pbBuckets, err := toIoTeXTypesVoteBucketList(c.SR(), buckets) 375 return pbBuckets, height, err 376 } 377 378 func (c *candSR) readStateBucketsByVoter(ctx context.Context, req *iotexapi.ReadStakingDataRequest_VoteBucketsByVoter) (*iotextypes.VoteBucketList, uint64, error) { 379 voter, err := address.FromString(req.GetVoterAddress()) 380 if err != nil { 381 return nil, 0, err 382 } 383 384 indices, height, err := c.voterBucketIndices(voter) 385 if errors.Cause(err) == state.ErrStateNotExist { 386 return &iotextypes.VoteBucketList{}, height, nil 387 } 388 if indices == nil || err != nil { 389 return nil, height, err 390 } 391 buckets, err := c.getBucketsWithIndices(*indices) 392 if err != nil { 393 return nil, height, err 394 } 395 396 offset := int(req.GetPagination().GetOffset()) 397 limit := int(req.GetPagination().GetLimit()) 398 buckets = getPageOfBuckets(buckets, offset, limit) 399 pbBuckets, err := toIoTeXTypesVoteBucketList(c.SR(), buckets) 400 return pbBuckets, height, err 401 } 402 403 func (c *candSR) readStateBucketsByCandidate(ctx context.Context, req *iotexapi.ReadStakingDataRequest_VoteBucketsByCandidate) (*iotextypes.VoteBucketList, uint64, error) { 404 cand := c.GetCandidateByName(req.GetCandName()) 405 if cand == nil { 406 return &iotextypes.VoteBucketList{}, 0, nil 407 } 408 409 indices, height, err := c.candBucketIndices(cand.Owner) 410 if errors.Cause(err) == state.ErrStateNotExist { 411 return &iotextypes.VoteBucketList{}, height, nil 412 } 413 if indices == nil || err != nil { 414 return nil, height, err 415 } 416 buckets, err := c.getBucketsWithIndices(*indices) 417 if err != nil { 418 return nil, height, err 419 } 420 421 offset := int(req.GetPagination().GetOffset()) 422 limit := int(req.GetPagination().GetLimit()) 423 buckets = getPageOfBuckets(buckets, offset, limit) 424 pbBuckets, err := toIoTeXTypesVoteBucketList(c.SR(), buckets) 425 return pbBuckets, height, err 426 } 427 428 func (c *candSR) readStateBucketByIndices(ctx context.Context, req *iotexapi.ReadStakingDataRequest_VoteBucketsByIndexes) (*iotextypes.VoteBucketList, uint64, error) { 429 height, err := c.SR().Height() 430 if err != nil { 431 return &iotextypes.VoteBucketList{}, height, err 432 } 433 buckets, err := c.getExistingBucketsWithIndices(BucketIndices(req.GetIndex())) 434 if err != nil { 435 return nil, height, err 436 } 437 pbBuckets, err := toIoTeXTypesVoteBucketList(c.SR(), buckets) 438 return pbBuckets, height, err 439 } 440 441 func (c *candSR) readStateBucketCount(ctx context.Context, _ *iotexapi.ReadStakingDataRequest_BucketsCount) (*iotextypes.BucketsCount, uint64, error) { 442 total, err := c.getTotalBucketCount() 443 if errors.Cause(err) == state.ErrStateNotExist { 444 return &iotextypes.BucketsCount{}, c.Height(), nil 445 } 446 if err != nil { 447 return nil, 0, err 448 } 449 active, h, err := getActiveBucketsCount(ctx, c) 450 if err != nil { 451 return nil, h, err 452 } 453 return &iotextypes.BucketsCount{ 454 Total: total, 455 Active: active, 456 }, h, nil 457 } 458 459 func (c *candSR) readStateCandidates(ctx context.Context, req *iotexapi.ReadStakingDataRequest_Candidates) (*iotextypes.CandidateListV2, uint64, error) { 460 offset := int(req.GetPagination().GetOffset()) 461 limit := int(req.GetPagination().GetLimit()) 462 candidates := getPageOfCandidates(c.AllCandidates(), offset, limit) 463 464 return toIoTeXTypesCandidateListV2(candidates), c.Height(), nil 465 } 466 467 func (c *candSR) readStateCandidateByName(ctx context.Context, req *iotexapi.ReadStakingDataRequest_CandidateByName) (*iotextypes.CandidateV2, uint64, error) { 468 cand := c.GetCandidateByName(req.GetCandName()) 469 if cand == nil { 470 return &iotextypes.CandidateV2{}, c.Height(), nil 471 } 472 return cand.toIoTeXTypes(), c.Height(), nil 473 } 474 475 func (c *candSR) readStateCandidateByAddress(ctx context.Context, req *iotexapi.ReadStakingDataRequest_CandidateByAddress) (*iotextypes.CandidateV2, uint64, error) { 476 owner, err := address.FromString(req.GetOwnerAddr()) 477 if err != nil { 478 return nil, 0, err 479 } 480 cand := c.GetCandidateByOwner(owner) 481 if cand == nil { 482 return &iotextypes.CandidateV2{}, c.Height(), nil 483 } 484 return cand.toIoTeXTypes(), c.Height(), nil 485 } 486 487 func (c *candSR) readStateTotalStakingAmount(ctx context.Context, _ *iotexapi.ReadStakingDataRequest_TotalStakingAmount) (*iotextypes.AccountMeta, uint64, error) { 488 meta := iotextypes.AccountMeta{} 489 meta.Address = address.StakingBucketPoolAddr 490 total, h, err := getTotalStakedAmount(ctx, c) 491 if err != nil { 492 return nil, h, err 493 } 494 meta.Balance = total.String() 495 return &meta, h, nil 496 }