Skip to content

Commit

Permalink
[staking] persist name/operator map and owner list to stateDB
Browse files Browse the repository at this point in the history
  • Loading branch information
dustinxie committed Oct 14, 2022
1 parent ebf4e0c commit af8a83b
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 21 deletions.
17 changes: 11 additions & 6 deletions action/protocol/staking/candidate_center.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type (
ownerMap map[string]*Candidate
operatorMap map[string]*Candidate
selfStkBucketMap map[uint64]*Candidate
owners CandidateList
}

// CandidateCenter is a struct to manage the candidates
Expand Down Expand Up @@ -58,12 +59,7 @@ func NewCandidateCenter(all CandidateList) (*CandidateCenter, error) {
}

c := CandidateCenter{
base: &candBase{
nameMap: make(map[string]*Candidate),
ownerMap: make(map[string]*Candidate),
operatorMap: make(map[string]*Candidate),
selfStkBucketMap: make(map[uint64]*Candidate),
},
base: newCandBase(),
change: delta,
}

Expand Down Expand Up @@ -415,6 +411,15 @@ func (cc *candChange) delete(owner address.Address) {
// candBase funcs
//======================================

func newCandBase() *candBase {
return &candBase{
nameMap: make(map[string]*Candidate),
ownerMap: make(map[string]*Candidate),
operatorMap: make(map[string]*Candidate),
selfStkBucketMap: make(map[uint64]*Candidate),
}
}

func (cb *candBase) size() int {
cb.lock.RLock()
defer cb.lock.RUnlock()
Expand Down
99 changes: 99 additions & 0 deletions action/protocol/staking/candidate_center_extra.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright (c) 2022 IoTeX Foundation
// This is an alpha (internal) release and is not suitable for production. 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 staking

func (cb *candBase) clone() *candBase {
cb.lock.RLock()
defer cb.lock.RUnlock()
clone := newCandBase()
for name, cand := range cb.nameMap {
clone.nameMap[name] = cand.Clone()
}
for owner, cand := range cb.ownerMap {
clone.ownerMap[owner] = cand.Clone()
}
for operator, cand := range cb.operatorMap {
clone.operatorMap[operator] = cand.Clone()
}
for bucket, cand := range cb.selfStkBucketMap {
clone.selfStkBucketMap[bucket] = cand.Clone()
}
if len(cb.owners) > 0 {
for _, cand := range cb.owners {
clone.owners = append(clone.owners, cand.Clone())
}
}
return clone
}

func (cb *candBase) candsInNameMap() CandidateList {
cb.lock.RLock()
defer cb.lock.RUnlock()
if len(cb.nameMap) == 0 {
return nil
}

list := make(CandidateList, 0, len(cb.nameMap))
for _, d := range cb.nameMap {
list = append(list, d.Clone())
}
return list
}

func (cb *candBase) candsInOperatorMap() CandidateList {
cb.lock.RLock()
defer cb.lock.RUnlock()
if len(cb.operatorMap) == 0 {
return nil
}

list := make(CandidateList, 0, len(cb.operatorMap))
for _, d := range cb.operatorMap {
list = append(list, d.Clone())
}
return list
}

func (cb *candBase) ownersList() CandidateList {
cb.lock.RLock()
defer cb.lock.RUnlock()
return cb.owners
}

func (cb *candBase) recordOwner(c *Candidate) {
cb.lock.Lock()
defer cb.lock.Unlock()
for i, d := range cb.owners {
if d.Owner.String() == c.Owner.String() {
cb.owners[i] = c.Clone()
return
}
}
// this is a new candidate
cb.owners = append(cb.owners, c.Clone())
}

func (cb *candBase) loadNameOperatorMapOwnerList(name, op, owners CandidateList) error {
cb.lock.Lock()
defer cb.lock.Unlock()
cb.nameMap = make(map[string]*Candidate)
for _, d := range name {
if err := d.Validate(); err != nil {
return err
}
cb.nameMap[d.Name] = d
}
cb.operatorMap = make(map[string]*Candidate)
for _, d := range op {
if err := d.Validate(); err != nil {
return err
}
cb.operatorMap[d.Operator.String()] = d
}
cb.owners = owners
return nil
}
19 changes: 6 additions & 13 deletions action/protocol/staking/candidate_statemanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,15 @@ type (
delBucket(index uint64) error
putBucketAndIndex(bucket *VoteBucket) (uint64, error)
delBucketAndIndex(owner, cand address.Address, index uint64) error
putBucketIndex(addr address.Address, prefix byte, index uint64) error
delBucketIndex(addr address.Address, prefix byte, index uint64) error
}
// CandidateSet related to setting candidates
CandidateSet interface {
putCandidate(d *Candidate) error
delCandidate(name address.Address) error
putVoterBucketIndex(addr address.Address, index uint64) error
delVoterBucketIndex(addr address.Address, index uint64) error
putCandBucketIndex(addr address.Address, index uint64) error
delCandBucketIndex(addr address.Address, index uint64) error
putCandidate(*Candidate) error
delCandidate(address.Address) error
putVoterBucketIndex(address.Address, uint64) error
delVoterBucketIndex(address.Address, uint64) error
putCandBucketIndex(address.Address, uint64) error
delCandBucketIndex(address.Address, uint64) error
}
// CandidateStateManager is candidate state manager on top of StateManager
CandidateStateManager interface {
Expand All @@ -55,7 +53,6 @@ type (
ContainsSelfStakingBucket(uint64) bool
GetByName(string) *Candidate
GetByOwner(address.Address) *Candidate
GetBySelfStakingIndex(uint64) *Candidate
Upsert(*Candidate) error
CreditBucketPool(*big.Int) error
DebitBucketPool(*big.Int, bool) error
Expand Down Expand Up @@ -141,10 +138,6 @@ func (csm *candSM) GetByOwner(addr address.Address) *Candidate {
return csm.candCenter.GetByOwner(addr)
}

func (csm *candSM) GetBySelfStakingIndex(index uint64) *Candidate {
return csm.candCenter.GetBySelfStakingIndex(index)
}

// Upsert writes the candidate into state manager and cand center
func (csm *candSM) Upsert(d *Candidate) error {
if err := csm.candCenter.Upsert(d); err != nil {
Expand Down
2 changes: 2 additions & 0 deletions action/protocol/staking/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,7 @@ func (p *Protocol) handleCandidateRegister(ctx context.Context, act *action.Cand
if err := csm.Upsert(c); err != nil {
return log, nil, csmErrorToHandleError(owner.String(), err)
}
csm.DirtyView().candCenter.base.recordOwner(c)

// update bucket pool
if err := csm.DebitBucketPool(act.Amount(), true); err != nil {
Expand Down Expand Up @@ -763,6 +764,7 @@ func (p *Protocol) handleCandidateUpdate(ctx context.Context, act *action.Candid
if err := csm.Upsert(c); err != nil {
return log, csmErrorToHandleError(c.Owner.String(), err)
}
csm.DirtyView().candCenter.base.recordOwner(c)

log.AddAddress(actCtx.Caller)
return log, nil
Expand Down
84 changes: 84 additions & 0 deletions action/protocol/staking/protocol.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const (

// _candidateNameSpace is the bucket name for candidate state
_candidateNameSpace = "Candidate"

// CandsMapNS is the bucket name to store candidate map
CandsMapNS = "CandsMap"
)

const (
Expand All @@ -57,6 +60,12 @@ var (
TotalBucketKey = append([]byte{_const}, []byte("totalBucket")...)
)

var (
_nameKey = []byte("name")
_operatorKey = []byte("operator")
_ownerKey = []byte("owner")
)

type (
// ReceiptError indicates a non-critical error with corresponding receipt status
ReceiptError interface {
Expand Down Expand Up @@ -166,6 +175,16 @@ func (p *Protocol) Start(ctx context.Context, sr protocol.StateReader) (interfac
if err != nil {
return nil, errors.Wrap(err, "failed to start staking protocol")
}

if p.needToReadCandsMap(height) {
name, operator, owners, err := readCandCenterStateFromStateDB(sr, height)
if err != nil {
return nil, errors.Wrap(err, "failed to read name/operator map")
}
if err = c.candCenter.base.loadNameOperatorMapOwnerList(name, operator, owners); err != nil {
return nil, errors.Wrap(err, "failed to load name/operator map to cand center")
}
}
return c, nil
}

Expand Down Expand Up @@ -293,6 +312,36 @@ func (p *Protocol) handleStakingIndexer(epochStartHeight uint64, sm protocol.Sta
return p.candBucketsIndexer.PutCandidates(epochStartHeight, candidateList)
}

// PreCommit preforms pre-commit
func (p *Protocol) PreCommit(ctx context.Context, sm protocol.StateManager) error {
height, err := sm.Height()
if err != nil {
return err
}
if !p.needToWriteCandsMap(height) {
return nil
}

featureWithHeightCtx := protocol.MustGetFeatureWithHeightCtx(ctx)
csm, err := NewCandidateStateManager(sm, featureWithHeightCtx.ReadStateFromDB(height))
if err != nil {
return err
}
cc := csm.DirtyView().candCenter
base := cc.base.clone()
if _, err = base.commit(cc.change); err != nil {
return errors.Wrap(err, "failed to apply candidate change in pre-commit")
}
// persist nameMap/operatorMap and ownerList to stateDB
name := base.candsInNameMap()
op := base.candsInOperatorMap()
owners := base.ownersList()
if len(name) == 0 || len(op) == 0 {
return ErrNilParameters
}
return errors.Wrap(p.writeCandCenterStateToStateDB(sm, name, op, owners), "failed to write name/operator map to stateDB")
}

// Commit commits the last change
func (p *Protocol) Commit(ctx context.Context, sm protocol.StateManager) error {
featureWithHeightCtx := protocol.MustGetFeatureWithHeightCtx(ctx)
Expand Down Expand Up @@ -543,3 +592,38 @@ func (p *Protocol) settleAction(
r.AddLogs(logs...).AddTransactionLogs(depositLog).AddTransactionLogs(tLogs...)
return &r, nil
}

func (p *Protocol) needToReadCandsMap(height uint64) bool {
return height > p.config.PersistStakingPatchBlock
}

func (p *Protocol) needToWriteCandsMap(height uint64) bool {
return height >= p.config.PersistStakingPatchBlock
}

func readCandCenterStateFromStateDB(sr protocol.StateReader, height uint64) (CandidateList, CandidateList, CandidateList, error) {
var (
name, operator, owner CandidateList
)
if _, err := sr.State(&name, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_nameKey)); err != nil {
return nil, nil, nil, err
}
if _, err := sr.State(&operator, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_operatorKey)); err != nil {
return nil, nil, nil, err
}
if _, err := sr.State(&owner, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_ownerKey)); err != nil {
return nil, nil, nil, err
}
return name, operator, owner, nil
}

func (p *Protocol) writeCandCenterStateToStateDB(sm protocol.StateManager, name, op, owners CandidateList) error {
if _, err := sm.PutState(name, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_nameKey)); err != nil {
return err
}
if _, err := sm.PutState(op, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_operatorKey)); err != nil {
return err
}
_, err := sm.PutState(owners, protocol.NamespaceOption(CandsMapNS), protocol.KeyOption(_ownerKey))
return err
}
3 changes: 2 additions & 1 deletion state/factory/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/iotexproject/iotex-core/action"
"github.com/iotexproject/iotex-core/action/protocol"
"github.com/iotexproject/iotex-core/action/protocol/execution/evm"
"github.com/iotexproject/iotex-core/action/protocol/staking"
"github.com/iotexproject/iotex-core/actpool"
"github.com/iotexproject/iotex-core/blockchain"
"github.com/iotexproject/iotex-core/blockchain/block"
Expand Down Expand Up @@ -296,7 +297,7 @@ func (sf *factory) flusherOptions(preEaster bool) []db.KVStoreFlusherOption {
if wi.Namespace() == ArchiveTrieNamespace {
return true
}
if wi.Namespace() != evm.CodeKVNameSpace {
if wi.Namespace() != evm.CodeKVNameSpace && wi.Namespace() != staking.CandsMapNS {
return false
}
return preEaster
Expand Down
3 changes: 2 additions & 1 deletion state/factory/statedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/iotexproject/iotex-core/action"
"github.com/iotexproject/iotex-core/action/protocol"
"github.com/iotexproject/iotex-core/action/protocol/execution/evm"
"github.com/iotexproject/iotex-core/action/protocol/staking"
"github.com/iotexproject/iotex-core/actpool"
"github.com/iotexproject/iotex-core/blockchain/block"
"github.com/iotexproject/iotex-core/blockchain/genesis"
Expand Down Expand Up @@ -430,7 +431,7 @@ func (sdb *stateDB) flusherOptions(preEaster bool) []db.KVStoreFlusherOption {
return append(
opts,
db.SerializeFilterOption(func(wi *batch.WriteInfo) bool {
return wi.Namespace() == evm.CodeKVNameSpace
return wi.Namespace() == evm.CodeKVNameSpace || wi.Namespace() == staking.CandsMapNS
}),
)
}
Expand Down

0 comments on commit af8a83b

Please sign in to comment.