LastTotalPower tracks the total amounts of bonded tokens recorded during the previous end block.
Store entries prefixed with "Last" must remain unchanged until EndBlock.
Params is a module-wide configuration structure that stores system parameters
and defines overall functioning of the staking module.
Copy
// Params defines the parameters for the staking module.
message Params {
option (gogoproto.equal) = true;
option (gogoproto.goproto_stringer) = false;
// unbonding_time is the time duration of unbonding.
google.protobuf.Duration unbonding_time = 1 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
// max_validators is the maximum number of validators.
uint32 max_validators = 2;
// max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio).
uint32 max_entries = 3;
// historical_entries is the number of historical entries to persist.
uint32 historical_entries = 4;
// bond_denom defines the bondable coin denomination.
string bond_denom = 5;
// min_commission_rate is the chain-wide minimum commission rate that a validator can charge their delegators
string min_commission_rate = 6 [
(gogoproto.moretags) = "yaml:\"min_commission_rate\"",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}
Unbonded: The validator is not in the active set. They cannot sign blocks and do not earn
rewards. They can receive delegations.
Bonded: Once the validator receives sufficient bonded tokens they automtically join the
active set during EndBlock and their status is updated to Bonded.
They are signing blocks and receiving rewards. They can receive further delegations.
They can be slashed for misbehavior. Delegators to this validator who unbond their delegation
must wait the duration of the UnbondingTime, a chain-specific param, during which time
they are still slashable for offences of the source validator if those offences were committed
during the period of time that the tokens were bonded.
Unbonding: When a validator leaves the active set, either by choice or due to slashing, jailing or
tombstoning, an unbonding of all their delegations begins. All delegations must then wait the UnbondingTime
before their tokens are moved to their accounts from the BondedPool.
Validators objects should be primarily stored and accessed by the
OperatorAddr, an SDK validator address for the operator of the validator. Two
additional indices are maintained per validator object in order to fulfill
required lookups for slashing and validator-set updates. A third special index
(LastValidatorPower) is also maintained which however remains constant
throughout each block, unlike the first two indices which mirror the validator
records within a block.
Validators is the primary index - it ensures that each operator can have only one
associated validator, where the public key of that validator can change in the
future. Delegators can refer to the immutable operator of the validator, without
concern for the changing public key.
ValidatorByConsAddr is an additional index that enables lookups for slashing.
When Tendermint reports evidence, it provides the validator address, so this
map is needed to find the operator. Note that the ConsAddr corresponds to the
address which can be derived from the validator's ConsPubKey.
ValidatorsByPower is an additional index that provides a sorted list of
potential validators to quickly determine the current active set. Here
ConsensusPower is validator.Tokens/10^6 by default. Note that all validators
where Jailed is true are not stored within this index.
LastValidatorsPower is a special index that provides a historical list of the
last-block's bonded validators. This index remains constant during a block but
is updated during the validator set update process which takes place in EndBlock.
Each validator's state is stored in a Validator struct:
Copy
// Validator defines a validator, together with the total amount of the
// Validator's bond shares and their exchange rate to coins. Slashing results in
// a decrease in the exchange rate, allowing correct calculation of future
// undelegations without iterating over delegators. When coins are delegated to
// this validator, the validator is credited with a delegation whose number of
// bond shares is based on the amount of coins delegated divided by the current
// exchange rate. Voting power can be calculated as total bonded shares
// multiplied by exchange rate.
message Validator {
option (gogoproto.equal) = false;
option (gogoproto.goproto_stringer) = false;
option (gogoproto.goproto_getters) = false;
// operator_address defines the address of the validator's operator; bech encoded in JSON.
string operator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// consensus_pubkey is the consensus public key of the validator, as a Protobuf Any.
google.protobuf.Any consensus_pubkey = 2 [(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey"];
// jailed defined whether the validator has been jailed from bonded status or not.
bool jailed = 3;
// status is the validator status (bonded/unbonding/unbonded).
BondStatus status = 4;
// tokens define the delegated tokens (incl. self-delegation).
string tokens = 5 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
// delegator_shares defines total shares issued to a validator's delegators.
string delegator_shares = 6 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// description defines the description terms for the validator.
Description description = 7 [(gogoproto.nullable) = false];
// unbonding_height defines, if unbonding, the height at which this validator has begun unbonding.
int64 unbonding_height = 8;
// unbonding_time defines, if unbonding, the min time for the validator to complete unbonding.
google.protobuf.Timestamp unbonding_time = 9 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
// commission defines the commission parameters.
Commission commission = 10 [(gogoproto.nullable) = false];
// min_self_delegation is the validator's self declared minimum self delegation.
//
// Since: cosmos-sdk 0.46
string min_self_delegation = 11 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
}
Copy
// CommissionRates defines the initial commission rates to be used for creating
// a validator.
message CommissionRates {
option (gogoproto.equal) = true;
option (gogoproto.goproto_stringer) = false;
// rate is the commission rate charged to delegators, as a fraction.
string rate = 1 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// max_rate defines the maximum commission rate which validator can ever charge, as a fraction.
string max_rate = 2 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
// max_change_rate defines the maximum daily increase of the validator commission, as a fraction.
string max_change_rate = 3 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}
// Commission defines commission parameters for a given validator.
message Commission {
option (gogoproto.equal) = true;
option (gogoproto.goproto_stringer) = false;
// commission_rates defines the initial commission rates to be used for creating a validator.
CommissionRates commission_rates = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
// update_time is the last time the commission rate was changed.
google.protobuf.Timestamp update_time = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
}
// Description defines a validator description.
message Description {
option (gogoproto.equal) = true;
option (gogoproto.goproto_stringer) = false;
// moniker defines a human-readable name for the validator.
string moniker = 1;
// identity defines an optional identity signature (ex. UPort or Keybase).
string identity = 2;
// website defines an optional website link.
string website = 3;
// security_contact defines an optional email for security contact.
string security_contact = 4;
// details define other optional details.
string details = 5;
}
Delegations are identified by combining DelegatorAddr (the address of the delegator)
with the ValidatorAddr Delegators are indexed in the store as follows:
Stake holders may delegate coins to validators; under this circumstance their
funds are held in a Delegation data structure. It is owned by one
delegator, and is associated with the shares for one validator. The sender of
the transaction is the owner of the bond.
Copy
// Delegation represents the bond with tokens held by an account. It is
// owned by one delegator, and is associated with the voting power of one
// validator.
message Delegation {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;
// delegator_address is the bech32-encoded address of the delegator.
string delegator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// validator_address is the bech32-encoded address of the validator.
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// shares define the delegation shares received.
string shares = 3 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}
When one Delegates tokens to a Validator they are issued a number of delegator shares based on a
dynamic exchange rate, calculated as follows from the total number of tokens delegated to the
validator and the number of shares issued so far:
Shares per Token = validator.TotalShares() / validator.Tokens()
Only the number of shares received is stored on the DelegationEntry. When a delegator then
Undelegates, the token amount they receive is calculated from the number of shares they currently
hold and the inverse exchange rate:
Tokens per Share = validator.Tokens() / validatorShares()
These Shares are simply an accounting mechanism. They are not a fungible asset. The reason for
this mechanism is to simplify the accounting around slashing. Rather than iteratively slashing the
tokens of every delegation entry, instead the Validators total bonded tokens can be slashed,
effectively reducing the value of each issued delegator share.
Shares in a Delegation can be unbonded, but they must for some time exist as
an UnbondingDelegation, where shares can be reduced if Byzantine behavior is
detected.
The first map here is used in queries, to lookup all unbonding delegations for
a given delegator, while the second map is used in slashing, to lookup all
unbonding delegations associated with a given validator that need to be
slashed.
A UnbondingDelegation object is created every time an unbonding is initiated.
Copy
// UnbondingDelegation stores all of a single delegator's unbonding bonds
// for a single validator in an time-ordered list.
message UnbondingDelegation {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;
// delegator_address is the bech32-encoded address of the delegator.
string delegator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// validator_address is the bech32-encoded address of the validator.
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// entries are the unbonding delegation entries.
repeated UnbondingDelegationEntry entries = 3 [(gogoproto.nullable) = false]; // unbonding delegation entries
}
The bonded tokens worth of a Delegation may be instantly redelegated from a
source validator to a different validator (destination validator). However when
this occurs they must be tracked in a Redelegation object, whereby their
shares can be slashed if their tokens have contributed to a Byzantine fault
committed by the source validator.
The first map here is used for queries, to lookup all redelegations for a given
delegator. The second map is used for slashing based on the ValidatorSrcAddr,
while the third map is for slashing based on the ValidatorDstAddr.
A redelegation object is created every time a redelegation occurs. To prevent
"redelegation hopping" redelegations may not occur under the situation that:
the (re)delegator already has another immature redelegation in progress
with a destination to a validator (let's call it Validator X)
and, the (re)delegator is attempting to create a new redelegation
where the source validator for this new redelegation is Validator X.
Copy
// RedelegationEntry defines a redelegation object with relevant metadata.
message RedelegationEntry {
option (gogoproto.equal) = true;
option (gogoproto.goproto_stringer) = false;
// creation_height defines the height which the redelegation took place.
int64 creation_height = 1;
// completion_time defines the unix time for redelegation completion.
google.protobuf.Timestamp completion_time = 2 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
// initial_balance defines the initial balance when redelegation started.
string initial_balance = 3 [
(cosmos_proto.scalar) = "cosmos.Int",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.nullable) = false
];
// shares_dst is the amount of destination-validator shares created by redelegation.
string shares_dst = 4 [
(cosmos_proto.scalar) = "cosmos.Dec",
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.nullable) = false
];
}
// Redelegation contains the list of a particular delegator's redelegating bonds
// from a particular source validator to a particular destination validator.
message Redelegation {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;
// delegator_address is the bech32-encoded address of the delegator.
string delegator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// validator_src_address is the validator redelegation source operator address.
string validator_src_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// validator_dst_address is the validator redelegation destination operator address.
string validator_dst_address = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
// entries are the redelegation entries.
repeated RedelegationEntry entries = 4 [(gogoproto.nullable) = false]; // redelegation entries
}
All queues objects are sorted by timestamp. The time used within any queue is
first rounded to the nearest nanosecond then sorted. The sortable time format
used is a slight modification of the RFC3339Nano and uses the format string
"2006-01-02T15:04:05.000000000". Notably this format:
right pads all zeros
drops the time zone info (uses UTC)
In all cases, the stored timestamp represents the maturation time of the queue
element.
Copy
// DVPair is struct that just has a delegator-validator pair with no other data.
// It is intended to be used as a marshalable pointer. For example, a DVPair can
// be used to construct the key to getting an UnbondingDelegation from state.
message DVPair {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;
string delegator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string validator_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
}
Copy
// DVVTriplet is struct that just has a delegator-validator-validator triplet
// with no other data. It is intended to be used as a marshalable pointer. For
// example, a DVVTriplet can be used to construct the key to getting a
// Redelegation from state.
message DVVTriplet {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
option (gogoproto.goproto_stringer) = false;
string delegator_address = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string validator_src_address = 2 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string validator_dst_address = 3 [(cosmos_proto.scalar) = "cosmos.AddressString"];
The stored object as each key is an array of validator operator addresses from
which the validator object can be accessed. Typically it is expected that only
a single validator record will be associated with a given timestamp however it is possible
that multiple validators exist in the queue at the same location.
HistoricalInfo objects are stored and pruned at each block such that the staking keeper persists
the n most recent historical info defined by staking module parameter: HistoricalEntries.
Copy
// HistoricalInfo contains header and validator information for a given block.
// It is stored as part of staking module's state, which persists the `n` most
// recent HistoricalInfo
// (`n` is set by the staking module's `historical_entries` parameter).
message HistoricalInfo {
tendermint.types.Header header = 1 [(gogoproto.nullable) = false];
repeated Validator valset = 2 [(gogoproto.nullable) = false];
}
At each BeginBlock, the staking keeper will persist the current Header and the Validators that committed
the current block in a HistoricalInfo object. The Validators are sorted on their address to ensure that
they are in a determisnistic order.
The oldest HistoricalEntries will be pruned to ensure that there only exist the parameter-defined number of
historical entries.