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  }