Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IIP-13] introduce read contract staking buckets (2/3 split by 3853) #3862

Merged
merged 6 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions action/protocol/staking/contractstaking/bucket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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"
)

// Bucket defines the bucket struct for contract staking
type Bucket struct {
Index uint64
Candidate address.Address
Owner address.Address
StakedAmount *big.Int
StakedDurationBlockNumber uint64
CreateBlockHeight uint64
StakeStartBlockHeight uint64
UnstakeStartBlockHeight uint64
AutoStake bool
ContractAddress string
}

func assembleBucket(token uint64, bi *bucketInfo, bt *BucketType) (*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: StakingContractAddress,
}
if bi.UnlockedAt != maxBlockNumber {
vb.StakeStartBlockHeight = bi.UnlockedAt
}
dustinxie marked this conversation as resolved.
Show resolved Hide resolved
return &vb, nil
}
69 changes: 69 additions & 0 deletions action/protocol/staking/contractstaking/bucket_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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 (
"github.com/iotexproject/iotex-address/address"
"google.golang.org/protobuf/proto"

"github.com/iotexproject/iotex-core/action/protocol/staking/contractstaking/contractstakingpb"
"github.com/iotexproject/iotex-core/pkg/util/byteutil"
)

type (
// bucketInfo is the bucket information
bucketInfo struct {
TypeIndex uint64
CreatedAt uint64
UnlockedAt uint64
UnstakedAt uint64
Delegate address.Address // owner address of the delegate
Owner address.Address
}
)

func (bi *bucketInfo) toProto() *contractstakingpb.BucketInfo {
pb := &contractstakingpb.BucketInfo{
TypeIndex: bi.TypeIndex,
Delegate: bi.Delegate.String(),
CreatedAt: bi.CreatedAt,
Owner: bi.Owner.String(),
UnlockedAt: bi.UnlockedAt,
UnstakedAt: bi.UnstakedAt,
}
return pb
}

// Serialize serializes the bucket info
func (bi *bucketInfo) Serialize() []byte {
return byteutil.Must(proto.Marshal(bi.toProto()))
}

// Deserialize deserializes the bucket info
func (bi *bucketInfo) Deserialize(b []byte) error {
m := contractstakingpb.BucketInfo{}
if err := proto.Unmarshal(b, &m); err != nil {
return err
}
return bi.loadProto(&m)
}

func (bi *bucketInfo) loadProto(p *contractstakingpb.BucketInfo) error {
var err error
bi.TypeIndex = p.TypeIndex
bi.CreatedAt = p.CreatedAt
bi.UnlockedAt = p.UnlockedAt
bi.UnstakedAt = p.UnstakedAt
bi.Delegate, err = address.FromString(p.Delegate)
if err != nil {
return err
}
bi.Owner, err = address.FromString(p.Owner)
if err != nil {
return err
}
return nil
}
58 changes: 58 additions & 0 deletions action/protocol/staking/contractstaking/bucket_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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/pkg/errors"
"google.golang.org/protobuf/proto"

"github.com/iotexproject/iotex-core/action/protocol/staking/contractstaking/contractstakingpb"
"github.com/iotexproject/iotex-core/pkg/util/byteutil"
)

type (
// BucketType defines the type of contract staking bucket
BucketType struct {
Amount *big.Int
Duration uint64 // block numbers
ActivatedAt uint64 // block height
}
)

func (bt *BucketType) toProto() *contractstakingpb.BucketType {
return &contractstakingpb.BucketType{
Amount: bt.Amount.String(),
Duration: bt.Duration,
ActivatedAt: bt.ActivatedAt,
}
}

func (bt *BucketType) loadProto(p *contractstakingpb.BucketType) error {
var ok bool
bt.Amount, ok = big.NewInt(0).SetString(p.Amount, 10)
if !ok {
return errors.New("failed to parse amount")
}
bt.Duration = p.Duration
bt.ActivatedAt = p.ActivatedAt
return nil
}

// Serialize serializes the bucket type
func (bt *BucketType) Serialize() []byte {
return byteutil.Must(proto.Marshal(bt.toProto()))
}

// Deserialize deserializes the bucket type
func (bt *BucketType) Deserialize(b []byte) error {
m := contractstakingpb.BucketType{}
if err := proto.Unmarshal(b, &m); err != nil {
return err
}
return bt.loadProto(&m)
}
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 {
idBucketMap map[uint64]*bucketInfo // map[token]bucketInfo
candidateBucketMap map[string]map[uint64]bool // map[candidate]bucket
idBucketTypeMap map[uint64]*BucketType // map[token]BucketType
dustinxie marked this conversation as resolved.
Show resolved Hide resolved
propertyBucketTypeMap map[int64]map[uint64]uint64 // map[amount][duration]index
height uint64
totalBucketCount uint64 // total number of buckets including burned buckets
}
)

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

func newContractStakingCache() *contractStakingCache {
cache := &contractStakingCache{
idBucketMap: make(map[uint64]*bucketInfo),
idBucketTypeMap: make(map[uint64]*BucketType),
propertyBucketTypeMap: make(map[int64]map[uint64]uint64),
candidateBucketMap: make(map[string]map[uint64]bool),
}
return cache
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

cache := contractStakingCache{
return &cache

}

func (s *contractStakingCache) GetHeight() uint64 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to Height

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return s.height
}

func (s *contractStakingCache) GetCandidateVotes(candidate address.Address) *big.Int {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to CandidateVotes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

votes := big.NewInt(0)
m, ok := s.candidateBucketMap[candidate.String()]
if !ok {
return votes
}
for id, existed := range m {
if !existed {
continue
}
bi, ok := s.idBucketMap[id]
if !ok {
continue
dustinxie marked this conversation as resolved.
Show resolved Hide resolved
}
if bi.UnstakedAt != maxBlockNumber {
dustinxie marked this conversation as resolved.
Show resolved Hide resolved
continue
}
bt := s.mustGetBucketType(bi.TypeIndex)
votes.Add(votes, bt.Amount)
}
return votes
}

func (s *contractStakingCache) GetBuckets() ([]*Bucket, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to Buckets

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

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

func (s *contractStakingCache) GetBucket(id uint64) (*Bucket, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to Bucket

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return s.getBucket(id)
}

func (s *contractStakingCache) GetBucketsByCandidate(candidate address.Address) ([]*Bucket, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to BucketsByCandidate

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

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) GetBucketsByIndices(indices []uint64) ([]*Bucket, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to BucketsByIndices

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

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) GetTotalBucketCount() uint64 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to TotalBucketCount

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return s.totalBucketCount
}

func (s *contractStakingCache) GetActiveBucketTypes() map[uint64]*BucketType {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename to ActiveBucketTypes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

m := make(map[uint64]*BucketType)
for k, v := range s.idBucketTypeMap {
if v.ActivatedAt != maxBlockNumber {
dustinxie marked this conversation as resolved.
Show resolved Hide resolved
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.idBucketTypeMap[id]
return bt, ok
}

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

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

func (s *contractStakingCache) mustGetBucketInfo(id uint64) *bucketInfo {
bt, ok := s.idBucketMap[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)
}

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

func (s *contractStakingCache) getAllBucketInfo() map[uint64]*bucketInfo {
dustinxie marked this conversation as resolved.
Show resolved Hide resolved
m := make(map[uint64]*bucketInfo)
for k, v := range s.idBucketMap {
m[k] = v
}
return m
}

func (s *contractStakingCache) getBucketInfoByCandidate(candidate address.Address) map[uint64]*bucketInfo {
m := make(map[uint64]*bucketInfo)
dustinxie marked this conversation as resolved.
Show resolved Hide resolved
for k, v := range s.candidateBucketMap[candidate.String()] {
if v {
m[k] = s.idBucketMap[k]
}
}
return m
}
Loading