Add more staking RPCs

pull/1844/head
Rongjian Lan 5 years ago
parent 41b55ceea6
commit 9cd994ecf2
  1. 37
      cmd/staking/root.go
  2. 63
      core/blockchain.go
  3. 22
      core/state_transition.go
  4. 51
      hmy/api_backend.go
  5. 3
      internal/hmyapi/backend.go
  6. 59
      internal/hmyapi/blockchain.go
  7. 17
      internal/hmyapi/types.go
  8. 4
      shard/committee/assignment.go
  9. 40
      staking/types/delegation.go
  10. 4
      staking/types/messages.go
  11. 11
      staking/types/validator.go

@ -53,7 +53,7 @@ var (
name = "NewName"
index = 0
minDele = 777
rate = "0.0"
rate = "0.15"
testAccounts = []string{
"one1pdv9lrdwl0rg5vglh4xtyrv3wjk3wsqket7zxy",
@ -68,6 +68,7 @@ func (s *staker) run(cmd *cobra.Command, args []string) error {
scryptP := keystore.StandardScryptP
ks := keystore.NewKeyStore(keystoreDir, scryptN, scryptP)
dAddr, _ := common2.Bech32ToAddress(testAccounts[index])
dAddr2, _ := common2.Bech32ToAddress(testAccounts[0])
account := accounts.Account{Address: dAddr}
ks.Unlock(account, testAccountPassword)
gasPrice := big.NewInt(1)
@ -100,6 +101,31 @@ func (s *staker) run(cmd *cobra.Command, args []string) error {
SlotPubKeys: []shard.BlsPublicKey{pub},
Amount: big.NewInt(denominations.One),
}
} else if cmdType == "edit" {
newRate, _ := numeric.NewDecFromStr(rate)
edit := staking.EditValidator{
Description: &staking.Description{
Name: name,
},
CommissionRate: &newRate,
ValidatorAddress: common.Address(dAddr),
}
return staking.DirectiveEditValidator, edit
} else if cmdType == "delegate" {
return staking.DirectiveDelegate, staking.Delegate{
dAddr,
dAddr2,
big.NewInt(1000),
}
} else if cmdType == "undelegate" {
return staking.DirectiveUndelegate, staking.Undelegate{
dAddr,
dAddr2,
big.NewInt(1000),
}
}
return staking.DirectiveCollectRewards, staking.CollectRewards{
dAddr,
}
/*
ValidatorAddress common.Address `json:"validator_address" yaml:"validator_address"`
@ -112,15 +138,6 @@ func (s *staker) run(cmd *cobra.Command, args []string) error {
}
*/
newRate, _ := numeric.NewDecFromStr(rate)
return staking.DirectiveEditValidator, staking.EditValidator{
Description: &staking.Description{
Name: name,
},
MinSelfDelegation: big.NewInt(int64(minDele)),
CommissionRate: &newRate,
ValidatorAddress: common.Address(dAddr),
}
}
stakingTx, err := staking.NewStakingTransaction(uint64(nonce), 600000, gasPrice, stakePayloadMaker)

@ -2309,30 +2309,15 @@ func (bc *BlockChain) ReadTxLookupEntry(txID common.Hash) (common.Hash, uint64,
// ReadValidatorData reads staking information of given validatorWrapper
func (bc *BlockChain) ReadValidatorData(addr common.Address) (*staking.ValidatorWrapper, error) {
if cached, ok := bc.validatorCache.Get("validator-" + string(addr.Bytes())); ok {
by := cached.([]byte)
v := staking.ValidatorWrapper{}
if err := rlp.DecodeBytes(by, &v); err != nil {
return nil, err
}
return &v, nil
}
return rawdb.ReadValidatorData(bc.db, addr)
}
// WriteValidatorData writes staking information of given validatorWrapper
func (bc *BlockChain) WriteValidatorData(v *staking.ValidatorWrapper) error {
err := rawdb.WriteValidatorData(bc.db, v)
if err != nil {
return err
state, err := bc.StateAt(bc.CurrentBlock().Root())
if err != nil || state == nil {
return nil, err
}
by, err := rlp.EncodeToBytes(v)
if err != nil {
return err
wrapper := state.GetStakingInfo(addr)
if wrapper == nil {
return nil, fmt.Errorf("ValidatorData not found: %v", addr)
}
bc.validatorCache.Add("validator-"+string(v.Address.Bytes()), by)
return nil
return wrapper, nil
}
// ReadValidatorSnapshot reads the snapshot staking information of given validator address
@ -2472,7 +2457,7 @@ func (bc *BlockChain) WriteActiveValidatorList(addrs []common.Address) error {
// ReadValidatorListByDelegator reads the addresses of validators delegated by a delegator
func (bc *BlockChain) ReadValidatorListByDelegator(delegator common.Address) ([]common.Address, error) {
if cached, ok := bc.validatorListByDelegatorCache.Get(delegator.Bytes()); ok {
if cached, ok := bc.validatorListByDelegatorCache.Get(string(delegator.Bytes())); ok {
by := cached.([]byte)
m := []common.Address{}
if err := rlp.DecodeBytes(by, &m); err != nil {
@ -2491,7 +2476,7 @@ func (bc *BlockChain) WriteValidatorListByDelegator(delegator common.Address, ad
}
bytes, err := rlp.EncodeToBytes(addrs)
if err == nil {
bc.validatorListByDelegatorCache.Add(delegator.Bytes(), bytes)
bc.validatorListByDelegatorCache.Add(string(delegator.Bytes()), bytes)
}
return nil
}
@ -2523,6 +2508,23 @@ func (bc *BlockChain) UpdateStakingMetaData(tx *staking.StakingTransaction) erro
if len(list) > beforeLen {
err = bc.WriteValidatorList(list)
}
// TODO: combine this code with the one for Delegate.
validators, err := bc.ReadValidatorListByDelegator(createValidator.ValidatorAddress)
if err != nil {
return err
}
found := false
for _, validator := range validators {
if bytes.Compare(validator.Bytes(), createValidator.ValidatorAddress.Bytes()) == 0 {
found = true
break
}
}
if !found {
validators = append(validators, createValidator.ValidatorAddress)
}
err = bc.WriteValidatorListByDelegator(createValidator.ValidatorAddress, validators)
return err
// following cases are placeholder for now
@ -2562,19 +2564,6 @@ func (bc *BlockChain) ValidatorCandidates() []common.Address {
return list
}
// ValidatorInformation returns the information of validator
func (bc *BlockChain) ValidatorInformation(addr common.Address) (*staking.Validator, error) {
state, err := bc.StateAt(bc.CurrentBlock().Root())
if err != nil || state == nil {
return nil, err
}
wrapper := state.GetStakingInfo(addr)
if wrapper == nil {
return nil, fmt.Errorf("ValidatorInformation not found: %v", addr)
}
return &wrapper.Validator, nil
}
// DelegatorsInformation returns up to date information of delegators of a given validator address
func (bc *BlockChain) DelegatorsInformation(addr common.Address) []*staking.Delegation {
return make([]*staking.Delegation, 0)

@ -37,10 +37,6 @@ var (
errValidatorExist = errors.New("staking validator already exists")
errValidatorNotExist = errors.New("staking validator does not exist")
errNoDelegationToUndelegate = errors.New("no delegation to undelegate")
errInvalidSelfDelegation = errors.New("self delegation can not be less than min_self_delegation")
errInvalidTotalDelegation = errors.New("total delegation can not be bigger than max_total_delegation")
errMinSelfDelegationTooSmall = errors.New("min_self_delegation has to be greater than 1 ONE")
errInvalidMaxTotalDelegation = errors.New("max_total_delegation can not be less than min_self_delegation")
errCommissionRateChangeTooFast = errors.New("commission rate can not be changed more than MaxChangeRate within the same epoch")
errCommissionRateChangeTooHigh = errors.New("commission rate can not be higher than MaxCommissionRate")
)
@ -389,7 +385,7 @@ func (st *StateTransition) applyEditValidatorTx(editValidator *staking.EditValid
wrapper.Validator.UpdateHeight = blockNum
}
if newRate.Sub(rateAtBeginningOfEpoch).GT(wrapper.Validator.MaxChangeRate) {
if newRate.Sub(rateAtBeginningOfEpoch).Abs().GT(wrapper.Validator.MaxChangeRate) {
return errCommissionRateChangeTooFast
}
@ -415,7 +411,7 @@ func (st *StateTransition) applyDelegateTx(delegate *staking.Delegate) error {
stateDB := st.state
delegatorExist := false
for i := range wrapper.Delegations {
delegation := wrapper.Delegations[i]
delegation := &wrapper.Delegations[i]
if bytes.Equal(delegation.DelegatorAddress.Bytes(), delegate.DelegatorAddress.Bytes()) {
delegatorExist = true
totalInUndelegation := delegation.TotalInUndelegation()
@ -424,16 +420,16 @@ func (st *StateTransition) applyDelegateTx(delegate *staking.Delegate) error {
// Firstly use the tokens in undelegation to delegate (redelegate)
undelegateAmount := big.NewInt(0).Set(delegate.Amount)
// Use the latest undelegated token first as it has the longest remaining locking time.
i := len(delegation.Entries) - 1
i := len(delegation.Undelegations) - 1
for ; i >= 0; i-- {
if delegation.Entries[i].Amount.Cmp(undelegateAmount) <= 0 {
undelegateAmount.Sub(undelegateAmount, delegation.Entries[i].Amount)
if delegation.Undelegations[i].Amount.Cmp(undelegateAmount) <= 0 {
undelegateAmount.Sub(undelegateAmount, delegation.Undelegations[i].Amount)
} else {
delegation.Entries[i].Amount.Sub(delegation.Entries[i].Amount, undelegateAmount)
delegation.Undelegations[i].Amount.Sub(delegation.Undelegations[i].Amount, undelegateAmount)
break
}
}
delegation.Entries = delegation.Entries[:i+1]
delegation.Undelegations = delegation.Undelegations[:i+1]
delegation.Amount.Add(delegation.Amount, delegate.Amount)
err := stateDB.UpdateStakingInfo(wrapper.Validator.Address, wrapper)
@ -476,7 +472,7 @@ func (st *StateTransition) applyUndelegateTx(undelegate *staking.Undelegate) err
stateDB := st.state
delegatorExist := false
for i := range wrapper.Delegations {
delegation := wrapper.Delegations[i]
delegation := &wrapper.Delegations[i]
if bytes.Equal(delegation.DelegatorAddress.Bytes(), undelegate.DelegatorAddress.Bytes()) {
delegatorExist = true
@ -515,7 +511,7 @@ func (st *StateTransition) applyCollectRewards(collectRewards *staking.CollectRe
// TODO: add the index of the validator-delegation position in the ReadValidatorListByDelegator record to avoid looping
for j := range wrapper.Delegations {
delegation := wrapper.Delegations[j]
delegation := &wrapper.Delegations[j]
if bytes.Equal(delegation.DelegatorAddress.Bytes(), collectRewards.DelegatorAddress.Bytes()) {
if delegation.Reward.Cmp(big.NewInt(0)) > 0 {
totalRewards.Add(totalRewards, delegation.Reward)

@ -1,6 +1,7 @@
package hmy
import (
"bytes"
"context"
"errors"
"math/big"
@ -303,13 +304,53 @@ func (b *APIBackend) GetAllValidatorAddresses() []common.Address {
// GetValidatorInformation returns the information of validator
func (b *APIBackend) GetValidatorInformation(addr common.Address) *staking.Validator {
val, _ := b.hmy.BlockChain().ValidatorInformation(addr)
return val
val, _ := b.hmy.BlockChain().ReadValidatorData(addr)
return &val.Validator
}
// GetDelegatorsInformation returns up to date information of delegators of a given validator address
func (b *APIBackend) GetDelegatorsInformation(addr common.Address) []*staking.Delegation {
return b.hmy.BlockChain().DelegatorsInformation(addr)
// GetDelegationsByValidator returns all delegation information of a validator
func (b *APIBackend) GetDelegationsByValidator(validator common.Address) []*staking.Delegation {
wrapper, err := b.hmy.BlockChain().ReadValidatorData(validator)
if err != nil || wrapper == nil {
return nil
}
delegations := []*staking.Delegation{}
for i := range wrapper.Delegations {
delegations = append(delegations, &wrapper.Delegations[i])
}
return delegations
}
// GetDelegationsByDelegator returns all delegation information of a delegator
func (b *APIBackend) GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation) {
delegations := []*staking.Delegation{}
addresses, err := b.hmy.BlockChain().ReadValidatorListByDelegator(delegator)
if err != nil {
return nil, nil
}
for i := range addresses {
wrapper, err := b.hmy.BlockChain().ReadValidatorData(addresses[i])
if err != nil || wrapper == nil {
return nil, nil
}
// TODO: add the index of the validator-delegation position in the ReadValidatorListByDelegator record to avoid looping
found := -1
for j := range wrapper.Delegations {
delegation := wrapper.Delegations[j]
if bytes.Equal(delegation.DelegatorAddress.Bytes(), delegator.Bytes()) {
found = j
break
}
}
if found != -1 {
delegations = append(delegations, &wrapper.Delegations[found])
} else {
delegations = append(delegations, nil)
}
}
return addresses, delegations
}
// GetValidatorStakingWithDelegation returns the amount of staking after applying all delegated stakes

@ -76,7 +76,8 @@ type Backend interface {
GetActiveValidatorAddresses() []common.Address
GetAllValidatorAddresses() []common.Address
GetValidatorInformation(addr common.Address) *staking.Validator
GetDelegatorsInformation(addr common.Address) []*staking.Delegation
GetDelegationsByValidator(validator common.Address) []*staking.Delegation
GetDelegationsByDelegator(delegator common.Address) ([]common.Address, []*staking.Delegation)
GetValidatorStakingWithDelegation(addr common.Address) *big.Int
}

@ -315,20 +315,6 @@ func (s *PublicBlockChainAPI) GetValidatorStakingWithDelegation(ctx context.Cont
return hexutil.Uint64(s.b.GetValidatorStakingWithDelegation(internal_common.ParseAddr(address)).Uint64())
}
// GetDelegatorsInformation returns list of delegators for a validator address.
func (s *PublicBlockChainAPI) GetDelegatorsInformation(ctx context.Context, address string) ([]map[string]interface{}, error) {
delegators := s.b.GetDelegatorsInformation(internal_common.ParseAddr(address))
delegatorsFields := make([]map[string]interface{}, 0)
for _, delegator := range delegators {
fields := map[string]interface{}{
"delegator": delegator.DelegatorAddress.String(),
"amount": hexutil.Uint64(delegator.Amount.Uint64()),
}
delegatorsFields = append(delegatorsFields, fields)
}
return delegatorsFields, nil
}
// GetShardingStructure returns an array of sharding structures.
func (s *PublicBlockChainAPI) GetShardingStructure(ctx context.Context) ([]map[string]interface{}, error) {
// Get header and number of shards.
@ -521,3 +507,48 @@ func (s *PublicBlockChainAPI) GetValidatorInfo(ctx context.Context, address comm
}
return newRPCValidator(validator), nil
}
// GetDelegationsByDelegator returns information about a validator.
func (s *PublicBlockChainAPI) GetDelegationsByDelegator(ctx context.Context, address common.Address) ([]*RPCDelegation, error) {
validators, delegations := s.b.GetDelegationsByDelegator(address)
result := []*RPCDelegation{}
for i := range delegations {
delegation := delegations[i]
undelegations := []RPCUndelegation{}
for j := range delegation.Undelegations {
undelegations = append(undelegations, RPCUndelegation{
delegation.Undelegations[j].Amount,
delegation.Undelegations[j].Epoch,
})
}
result = append(result, &RPCDelegation{
validators[i],
address,
delegation.Amount,
delegation.Reward,
undelegations,
})
}
return result, nil
}
// GetDelegationsByValidator returns list of delegations for a validator address.
func (s *PublicBlockChainAPI) GetDelegationsByValidator(ctx context.Context, address string) ([]*RPCDelegation, error) {
validatorAddress := internal_common.ParseAddr(address)
delegations := s.b.GetDelegationsByValidator(validatorAddress)
result := make([]*RPCDelegation, 0)
for _, delegation := range delegations {
result = append(result, &RPCDelegation{
validatorAddress,
delegation.DelegatorAddress,
delegation.Amount,
delegation.Reward,
nil,
})
}
return result, nil
}

@ -71,13 +71,28 @@ type RPCValidator struct {
Stake *big.Int `json:"stake" yaml:"stake"`
UnbondingHeight *big.Int `json:"unbonding_height"`
MinSelfDelegation *big.Int `json:"min_self_delegation"`
MaxTotalDelegation *big.Int `json:"min_self_delegation"`
MaxTotalDelegation *big.Int `json:"max_total_delegation"`
Active bool `json:"active"`
Commission types2.Commission `json:"commission"`
Description types2.Description `json:"description"`
CreationHeight *big.Int `json:"creation_height"`
}
// RPCDelegation represents a particular delegation to a validator
type RPCDelegation struct {
ValidatorAddress common.Address `json:"validator_address" yaml:"validator_address"`
DelegatorAddress common.Address `json:"delegator_address" yaml:"delegator_address"`
Amount *big.Int `json:"amount" yaml:"amount"`
Reward *big.Int `json:"reward" yaml:"reward"`
Undelegations []RPCUndelegation `json:"Undelegations" yaml:"Undelegations"`
}
// RPCUndelegation represents one undelegation entry
type RPCUndelegation struct {
Amount *big.Int
Epoch *big.Int
}
func newHeaderInformation(header *block.Header) *HeaderInformation {
if header == nil {
return nil

@ -39,7 +39,7 @@ type Reader interface {
// StakingCandidatesReader ..
type StakingCandidatesReader interface {
ValidatorInformation(addr common.Address) (*staking.Validator, error)
ReadValidatorData(addr common.Address) (*staking.ValidatorWrapper, error)
ValidatorStakingWithDelegation(addr common.Address) *big.Int
ValidatorCandidates() []common.Address
}
@ -122,7 +122,7 @@ func eposStakedCommittee(
// TODO benchmark difference if went with data structure that sorts on insert
for i := range candidates {
// TODO Should be using .ValidatorStakingWithDelegation, not implemented yet
validator, err := stakerReader.ValidatorInformation(candidates[i])
validator, err := stakerReader.ReadValidatorData(candidates[i])
if err != nil {
return nil, err
}

@ -22,14 +22,14 @@ const (
// owned by one delegator, and is associated with the voting power of one
// validator.
type Delegation struct {
DelegatorAddress common.Address `json:"delegator_address" yaml:"delegator_address"`
Amount *big.Int `json:"amount" yaml:"amount"`
Reward *big.Int `json:"reward" yaml:"reward"`
Entries []*UndelegationEntry `json:"entries" yaml:"entries"`
DelegatorAddress common.Address `json:"delegator_address" yaml:"delegator_address"`
Amount *big.Int `json:"amount" yaml:"amount"`
Reward *big.Int `json:"reward" yaml:"reward"`
Undelegations []*Undelegation `json:"undelegations" yaml:"undelegations"`
}
// UndelegationEntry represents one undelegation entry
type UndelegationEntry struct {
// Undelegation represents one undelegation entry
type Undelegation struct {
Amount *big.Int
Epoch *big.Int
}
@ -54,7 +54,7 @@ func (d *Delegation) Undelegate(epoch *big.Int, amt *big.Int) error {
d.Amount.Sub(d.Amount, amt)
exist := false
for _, entry := range d.Entries {
for _, entry := range d.Undelegations {
if entry.Epoch.Cmp(epoch) == 0 {
exist = true
entry.Amount.Add(entry.Amount, amt)
@ -63,13 +63,13 @@ func (d *Delegation) Undelegate(epoch *big.Int, amt *big.Int) error {
}
if !exist {
item := UndelegationEntry{amt, epoch}
d.Entries = append(d.Entries, &item)
item := Undelegation{amt, epoch}
d.Undelegations = append(d.Undelegations, &item)
// Always sort the undelegate by epoch in increasing order
sort.SliceStable(
d.Entries,
func(i, j int) bool { return d.Entries[i].Epoch.Cmp(d.Entries[j].Epoch) < 0 },
d.Undelegations,
func(i, j int) bool { return d.Undelegations[i].Epoch.Cmp(d.Undelegations[j].Epoch) < 0 },
)
}
@ -79,7 +79,7 @@ func (d *Delegation) Undelegate(epoch *big.Int, amt *big.Int) error {
// TotalInUndelegation - return the total amount of token in undelegation (locking period)
func (d *Delegation) TotalInUndelegation() *big.Int {
total := big.NewInt(0)
for _, entry := range d.Entries {
for _, entry := range d.Undelegations {
total.Add(total, entry.Amount)
}
return total
@ -88,14 +88,14 @@ func (d *Delegation) TotalInUndelegation() *big.Int {
// DeleteEntry - delete an entry from the undelegation
// Opimize it
func (d *Delegation) DeleteEntry(epoch *big.Int) {
entries := []*UndelegationEntry{}
for i, entry := range d.Entries {
entries := []*Undelegation{}
for i, entry := range d.Undelegations {
if entry.Epoch.Cmp(epoch) == 0 {
entries = append(d.Entries[:i], d.Entries[i+1:]...)
entries = append(d.Undelegations[:i], d.Undelegations[i+1:]...)
}
}
if entries != nil {
d.Entries = entries
d.Undelegations = entries
}
}
@ -103,15 +103,15 @@ func (d *Delegation) DeleteEntry(epoch *big.Int) {
func (d *Delegation) RemoveUnlockedUndelegations(curEpoch *big.Int) *big.Int {
totalWithdraw := big.NewInt(0)
count := 0
for j := range d.Entries {
if big.NewInt(0).Sub(curEpoch, d.Entries[j].Epoch).Int64() > LockPeriodInEpoch { // need to wait at least 14 epochs to withdraw;
totalWithdraw.Add(totalWithdraw, d.Entries[j].Amount)
for j := range d.Undelegations {
if big.NewInt(0).Sub(curEpoch, d.Undelegations[j].Epoch).Int64() > LockPeriodInEpoch { // need to wait at least 14 epochs to withdraw;
totalWithdraw.Add(totalWithdraw, d.Undelegations[j].Amount)
count++
} else {
break
}
}
d.Entries = d.Entries[count:]
d.Undelegations = d.Undelegations[count:]
return totalWithdraw
}

@ -60,8 +60,8 @@ type CreateValidator struct {
// EditValidator - type for edit existing validator
type EditValidator struct {
ValidatorAddress common.Address `json:"validator_address" yaml:"validator_address" rlp:"nil"`
Description *Description `json:"description" yaml:"description"`
CommissionRate *numeric.Dec `json:"commission_rate" yaml:"commission_rate" rlp:"nil" rlp:"nil"`
Description *Description `json:"description" yaml:"description" rlp:"nil"`
CommissionRate *numeric.Dec `json:"commission_rate" yaml:"commission_rate" rlp:"nil"`
MinSelfDelegation *big.Int `json:"min_self_delegation" yaml:"min_self_delegation" rlp:"nil"`
MaxTotalDelegation *big.Int `json:"max_total_delegation" yaml:"max_total_delegation" rlp:"nil"`
SlotKeyToRemove *shard.BlsPublicKey `json:"slot_key_to_remove" yaml:"slot_key_to_remove" rlp:"nil"`

@ -53,7 +53,7 @@ type Validator struct {
// validator's self declared minimum self delegation
MinSelfDelegation *big.Int `json:"min_self_delegation" yaml:"min_self_delegation"`
// maximum total delgation allowed
MaxTotalDelegation *big.Int `json:"min_self_delegation" yaml:"min_self_delegation"`
MaxTotalDelegation *big.Int `json:"max_total_delegation" yaml:"max_total_delegation"`
// Is the validator active in the validating process or not
Active bool `json:"active" yaml:"active"`
// commission parameters
@ -243,11 +243,11 @@ func UpdateValidatorFromEditMsg(validator *Validator, edit *EditValidator) error
validator.Rate = *edit.CommissionRate
}
if edit.MinSelfDelegation != nil {
if edit.MinSelfDelegation != nil && edit.MinSelfDelegation.Cmp(big.NewInt(0)) != 0 {
validator.MinSelfDelegation = edit.MinSelfDelegation
}
if edit.MaxTotalDelegation != nil {
if edit.MaxTotalDelegation != nil && edit.MaxTotalDelegation.Cmp(big.NewInt(0)) != 0 {
validator.MaxTotalDelegation = edit.MaxTotalDelegation
}
@ -287,11 +287,12 @@ func (v *Validator) String() string {
SlotPubKeys: %s
Stake: %s
Unbonding Height: %v
Minimum SelfDelegation: %v
Minimum Self Delegation: %v
Maximum Total Delegation: %v
Description: %v
Commission: %v`,
common2.MustAddressToBech32(v.Address), printSlotPubKeys(v.SlotPubKeys),
v.Stake, v.UnbondingHeight,
v.MinSelfDelegation, v.Description, v.Commission,
v.MinSelfDelegation, v.MaxTotalDelegation, v.Description, v.Commission,
)
}

Loading…
Cancel
Save