Skip to content

Commit

Permalink
Merge a7d64e9 into 20e6921
Browse files Browse the repository at this point in the history
  • Loading branch information
envestcc authored May 22, 2023
2 parents 20e6921 + a7d64e9 commit 515c1a6
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 0 deletions.
19 changes: 19 additions & 0 deletions action/protocol/staking/contractstaking/bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,22 @@ type Bucket struct {
AutoStake bool
ContractAddress string // contract address for the bucket
}

func assembleBucket(token uint64, bi *bucketInfo, bt *BucketType, contractAddr string) (*Bucket, error) {
vb := Bucket{
Index: token,
StakedAmount: bt.Amount,
StakedDurationBlockNumber: bt.Duration,
CreateBlockHeight: bi.CreatedAt,
StakeStartBlockHeight: bi.CreatedAt,
UnstakeStartBlockHeight: bi.UnstakedAt,
AutoStake: bi.UnlockedAt == maxBlockNumber,
Candidate: bi.Delegate,
Owner: bi.Owner,
ContractAddress: contractAddr,
}
if bi.UnlockedAt != maxBlockNumber {
vb.StakeStartBlockHeight = bi.UnlockedAt
}
return &vb, nil
}
188 changes: 188 additions & 0 deletions action/protocol/staking/contractstaking/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// Copyright (c) 2023 IoTeX Foundation
// This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
// or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
// This source code is governed by Apache License 2.0 that can be found in the LICENSE file.

package contractstaking

import (
"math/big"

"github.com/iotexproject/iotex-address/address"
"github.com/pkg/errors"
)

type (
contractStakingCache struct {
bucketInfoMap map[uint64]*bucketInfo // map[token]bucketInfo
candidateBucketMap map[string]map[uint64]bool // map[candidate]bucket
bucketTypeMap map[uint64]*BucketType // map[bucketTypeId]BucketType
propertyBucketTypeMap map[int64]map[uint64]uint64 // map[amount][duration]index
height uint64
totalBucketCount uint64 // total number of buckets including burned buckets
contractAddress string // contract address for the bucket
}
)

var (
// ErrBucketNotExist is the error when bucket does not exist
ErrBucketNotExist = errors.New("bucket does not exist")
)

func newContractStakingCache(contractAddr string) *contractStakingCache {
cache := &contractStakingCache{
bucketInfoMap: make(map[uint64]*bucketInfo),
bucketTypeMap: make(map[uint64]*BucketType),
propertyBucketTypeMap: make(map[int64]map[uint64]uint64),
candidateBucketMap: make(map[string]map[uint64]bool),
contractAddress: contractAddr,
}
return cache
}

func (s *contractStakingCache) Height() uint64 {
return s.height
}

func (s *contractStakingCache) CandidateVotes(candidate address.Address) *big.Int {
votes := big.NewInt(0)
m, ok := s.candidateBucketMap[candidate.String()]
if !ok {
return votes
}
for id, existed := range m {
if !existed {
continue
}
bi := s.mustGetBucketInfo(id)
// only count the bucket that is not unstaked
if bi.UnstakedAt != maxBlockNumber {
continue
}
bt := s.mustGetBucketType(bi.TypeIndex)
votes.Add(votes, bt.Amount)
}
return votes
}

func (s *contractStakingCache) Buckets() ([]*Bucket, error) {
vbs := []*Bucket{}
for id, bi := range s.getAllBucketInfo() {
bt := s.mustGetBucketType(bi.TypeIndex)
vb, err := assembleBucket(id, bi, bt, s.contractAddress)
if err != nil {
return nil, err
}
vbs = append(vbs, vb)
}
return vbs, nil
}

func (s *contractStakingCache) Bucket(id uint64) (*Bucket, error) {
return s.getBucket(id)
}

func (s *contractStakingCache) BucketsByCandidate(candidate address.Address) ([]*Bucket, error) {
bucketMap := s.getBucketInfoByCandidate(candidate)
vbs := make([]*Bucket, 0, len(bucketMap))
for id := range bucketMap {
vb, err := s.getBucket(id)
if err != nil {
return nil, err
}
vbs = append(vbs, vb)
}
return vbs, nil
}

func (s *contractStakingCache) BucketsByIndices(indices []uint64) ([]*Bucket, error) {
vbs := make([]*Bucket, 0, len(indices))
for _, id := range indices {
vb, err := s.getBucket(id)
if err != nil {
return nil, err
}
vbs = append(vbs, vb)
}
return vbs, nil
}

func (s *contractStakingCache) TotalBucketCount() uint64 {
return s.totalBucketCount
}

func (s *contractStakingCache) ActiveBucketTypes() map[uint64]*BucketType {
m := make(map[uint64]*BucketType)
for k, v := range s.bucketTypeMap {
if v.ActivatedAt != maxBlockNumber {
m[k] = v
}
}
return m
}

func (s *contractStakingCache) getBucketTypeIndex(amount *big.Int, duration uint64) (uint64, bool) {
m, ok := s.propertyBucketTypeMap[amount.Int64()]
if !ok {
return 0, false
}
id, ok := m[duration]
return id, ok
}

func (s *contractStakingCache) getBucketType(id uint64) (*BucketType, bool) {
bt, ok := s.bucketTypeMap[id]
return bt, ok
}

func (s *contractStakingCache) mustGetBucketType(id uint64) *BucketType {
bt, ok := s.bucketTypeMap[id]
if !ok {
panic("bucket type not found")
}
return bt
}

func (s *contractStakingCache) getBucketInfo(id uint64) (*bucketInfo, bool) {
bi, ok := s.bucketInfoMap[id]
return bi, ok
}

func (s *contractStakingCache) mustGetBucketInfo(id uint64) *bucketInfo {
bt, ok := s.bucketInfoMap[id]
if !ok {
panic("bucket info not found")
}
return bt
}

func (s *contractStakingCache) getBucket(id uint64) (*Bucket, error) {
bi, ok := s.getBucketInfo(id)
if !ok {
return nil, errors.Wrapf(ErrBucketNotExist, "id %d", id)
}
bt := s.mustGetBucketType(bi.TypeIndex)
return assembleBucket(id, bi, bt, s.contractAddress)
}

func (s *contractStakingCache) getTotalBucketTypeCount() uint64 {
return uint64(len(s.bucketTypeMap))
}

func (s *contractStakingCache) getAllBucketInfo() map[uint64]*bucketInfo {
m := make(map[uint64]*bucketInfo)
for k, v := range s.bucketInfoMap {
m[k] = v
}
return m
}

func (s *contractStakingCache) getBucketInfoByCandidate(candidate address.Address) map[uint64]*bucketInfo {
m := make(map[uint64]*bucketInfo)
for k, v := range s.candidateBucketMap[candidate.String()] {
if v {
m[k] = s.bucketInfoMap[k]
}
}
return m
}
84 changes: 84 additions & 0 deletions action/protocol/staking/contractstaking/indexer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) 2023 IoTeX Foundation
// This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
// or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
// This source code is governed by Apache License 2.0 that can be found in the LICENSE file.

package contractstaking

import (
"math/big"

"github.com/ethereum/go-ethereum/common/math"
"github.com/iotexproject/iotex-address/address"

"github.com/iotexproject/iotex-core/db"
)

const (
maxBlockNumber uint64 = math.MaxUint64
)

type (
// Indexer is the contract staking indexer
// Main functions:
// 1. handle contract staking contract events when new block comes to generate index data
// 2. provide query interface for contract staking index data
Indexer struct {
kvstore db.KVStore // persistent storage, used to initialize index cache at startup
cache *contractStakingCache // in-memory index for clean data, used to query index data
contractAddress string // stake contract address
}
)

// NewContractStakingIndexer creates a new contract staking indexer
func NewContractStakingIndexer(kvStore db.KVStore, contractAddr string) *Indexer {
return &Indexer{
kvstore: kvStore,
cache: newContractStakingCache(contractAddr),
}
}

// Height returns the tip block height
func (s *Indexer) Height() (uint64, error) {
return s.cache.Height(), nil
}

// CandidateVotes returns the candidate votes
func (s *Indexer) CandidateVotes(candidate address.Address) *big.Int {
return s.cache.CandidateVotes(candidate)
}

// Buckets returns the buckets
func (s *Indexer) Buckets() ([]*Bucket, error) {
return s.cache.Buckets()
}

// Bucket returns the bucket
func (s *Indexer) Bucket(id uint64) (*Bucket, error) {
return s.cache.Bucket(id)
}

// BucketsByIndices returns the buckets by indices
func (s *Indexer) BucketsByIndices(indices []uint64) ([]*Bucket, error) {
return s.cache.BucketsByIndices(indices)
}

// BucketsByCandidate returns the buckets by candidate
func (s *Indexer) BucketsByCandidate(candidate address.Address) ([]*Bucket, error) {
return s.cache.BucketsByCandidate(candidate)
}

// TotalBucketCount returns the total bucket count including active and burnt buckets
func (s *Indexer) TotalBucketCount() uint64 {
return s.cache.TotalBucketCount()
}

// BucketTypes returns the active bucket types
func (s *Indexer) BucketTypes() ([]*BucketType, error) {
btMap := s.cache.ActiveBucketTypes()
bts := make([]*BucketType, 0, len(btMap))
for _, bt := range btMap {
bts = append(bts, bt)
}
return bts, nil
}

0 comments on commit 515c1a6

Please sign in to comment.