From 18aa9a990986a562962bd4b0850c057059cb0f17 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Thu, 17 May 2018 13:32:13 -0400 Subject: [PATCH 01/27] stake/fees spec updates --- .../FeeDistributionModel.xlsx} | Bin docs/spec/fee_distribution/overview.md | 194 ++++++++++++++++++ 2 files changed, 194 insertions(+) rename docs/spec/{staking/AbsoluteFeeDistrModel.xlsx => fee_distribution/FeeDistributionModel.xlsx} (100%) create mode 100644 docs/spec/fee_distribution/overview.md diff --git a/docs/spec/staking/AbsoluteFeeDistrModel.xlsx b/docs/spec/fee_distribution/FeeDistributionModel.xlsx similarity index 100% rename from docs/spec/staking/AbsoluteFeeDistrModel.xlsx rename to docs/spec/fee_distribution/FeeDistributionModel.xlsx diff --git a/docs/spec/fee_distribution/overview.md b/docs/spec/fee_distribution/overview.md new file mode 100644 index 000000000..2ed0fe050 --- /dev/null +++ b/docs/spec/fee_distribution/overview.md @@ -0,0 +1,194 @@ +# Fee Distribution + +## Overview + +Fees are pooled separately and withdrawn lazily, at any time. They are not +bonded, and can be paid in multiple tokens. An adjustment factor is maintained +for each validator and delegator to determine the true proportion of fees in +the pool they are entitled too. Adjustment factors are updated every time a +validator or delegator's voting power changes. Validators and delegators must +withdraw all fees they are entitled too before they can bond or unbond Atoms. + +## Affect on Staking + +Because fees are optimized to note + +Commission on Atom Provisions and having atoms autobonded are mutually exclusive (we can’t have both). The reason +for this is that + +if there are atoms commissions and autobonding, the portion of +atoms the fee distribution calculation would become very large as the atom +portion for each delegator would change each block making a withdrawal of fees +for a delegator require a calculation for every single block since the last +withdrawal. Conclusion we can only have atom commission and unbonded atoms +provisions, or bonded atom provisions and no atom commission + +## Fee Calculations + +Collected fees are pooled globally and divided out passively to validators and +delegators. Each validator has the opportunity to charge commission to the +delegators on the fees collected on behalf of the delegators by the validators. +Fees are paid directly into a global fee pool. Due to the nature of of passive +accounting whenever changes to parameters which affect the rate of fee +distribution occurs, withdrawal of fees must also occur. + + - when withdrawing one must withdrawal the maximum amount they are entitled + too, leaving nothing in the pool, + - when bonding, unbonding, or re-delegating tokens to an existing account a + full withdrawal of the fees must occur (as the rules for lazy accounting + change), + - when a candidate chooses to change the commission on fees, all accumulated + commission fees must be simultaneously withdrawn. + +When the validator is the proposer of the round, that validator (and their +delegators) receives between 1% and 5% of fee rewards, the reserve tax is then +charged, then the remainder is distributed socially by voting power to all +validators including the proposer validator. The amount of proposer reward is +calculated from pre-commits Tendermint messages. All provision rewards are +added to a provision reward pool which validator holds individually. Here note +that `BondedShares` represents the sum of all voting power saved in the +`GlobalState` (denoted `gs`). + +``` +proposerReward = feesCollected * (0.01 + 0.04 + * sumOfVotingPowerOfPrecommitValidators / gs.BondedShares) +candidate.ProposerRewardPool += proposerReward + +reserveTaxed = feesCollected * params.ReserveTax +gs.ReservePool += reserveTaxed + +distributedReward = feesCollected - proposerReward - reserveTaxed +gs.FeePool += distributedReward +gs.SumFeesReceived += distributedReward +gs.RecentFee = distributedReward +``` + +The entitlement to the fee pool held by the each validator can be accounted for +lazily. First we must account for a candidate's `count` and `adjustment`. The +`count` represents a lazy accounting of what that candidates entitlement to the +fee pool would be if there `VotingPower` was to never change and they were to +never withdraw fees. + +``` +candidate.count = candidate.VotingPower * BlockHeight +``` + +Similarly the GlobalState count can be passively calculated whenever needed, +where `BondedShares` is the updated sum of voting powers from all validators. + +``` +gs.count = gs.BondedShares * BlockHeight +``` + +The `adjustment` term accounts for changes in voting power and withdrawals of +fees. The adjustment factor must be persisted with the candidate and modified +whenever fees are withdrawn from the candidate or the voting power of the +candidate changes. When the voting power of the candidate changes the +`Adjustment` factor is increased/decreased by the cumulative difference in the +voting power if the voting power has been the new voting power as opposed to +the old voting power for the entire duration of the blockchain up the previous +block. Each time there is an adjustment change the GlobalState (denoted `gs`) +`Adjustment` must also be updated. + +``` +simplePool = candidate.count / gs.count * gs.SumFeesReceived +projectedPool = candidate.PrevPower * (height-1) + / (gs.PrevPower * (height-1)) * gs.PrevFeesReceived + + candidate.Power / gs.Power * gs.RecentFee + +AdjustmentChange = simplePool - projectedPool +candidate.AdjustmentRewardPool += AdjustmentChange +gs.Adjustment += AdjustmentChange +``` + +Every instance that the voting power changes, information about the state of +the validator set during the change must be recorded as a `powerChange` for +other validators to run through. Before any validator modifies its voting power +it must first run through the above calculation to determine the change in +their `caandidate.AdjustmentRewardPool` for all historical changes in the set +of `powerChange` which they have not yet synced to. The set of all +`powerChange` may be trimmed from its oldest members once all validators have +synced past the height of the oldest `powerChange`. This trim procedure will +occur on an epoch basis. + +```golang +type powerChange struct { + height int64 // block height at change + power rational.Rat // total power at change + prevpower rational.Rat // total power at previous height-1 + feesin coins.Coin // fees in at block height + prevFeePool coins.Coin // total fees in at previous block height +} +``` + +Note that the adjustment factor may result as negative if the voting power of a +different candidate has decreased. + +``` +candidate.AdjustmentRewardPool += withdrawn +gs.Adjustment += withdrawn +``` + +Now the entitled fee pool of each candidate can be lazily accounted for at +any given block: + +``` +candidate.feePool = candidate.simplePool - candidate.Adjustment +``` + +So far we have covered two sources fees which can be withdrawn from: Fees from +proposer rewards (`candidate.ProposerRewardPool`), and fees from the fee pool +(`candidate.feePool`). However we should note that all fees from fee pool are +subject to commission rate from the owner of the candidate. These next +calculations outline the math behind withdrawing fee rewards as either a +delegator to a candidate providing commission, or as the owner of a candidate +who is receiving commission. + +### Calculations For Delegators and Candidates + +The same mechanism described to calculate the fees which an entire validator is +entitled to is be applied to delegator level to determine the entitled fees for +each delegator and the candidates entitled commission from `gs.FeesPool` and +`candidate.ProposerRewardPool`. + +The calculations are identical with a few modifications to the parameters: + - Delegator's entitlement to `gs.FeePool`: + - entitled party voting power should be taken as the effective voting power + after commission is retrieved, + `bond.Shares/candidate.TotalDelegatorShares * candidate.VotingPower * (1 - candidate.Commission)` + - Delegator's entitlement to `candidate.ProposerFeePool` + - global power in this context is actually shares + `candidate.TotalDelegatorShares` + - entitled party voting power should be taken as the effective shares after + commission is retrieved, `bond.Shares * (1 - candidate.Commission)` + - Candidate's commission entitlement to `gs.FeePool` + - entitled party voting power should be taken as the effective voting power + of commission portion of total voting power, + `candidate.VotingPower * candidate.Commission` + - Candidate's commission entitlement to `candidate.ProposerFeePool` + - global power in this context is actually shares + `candidate.TotalDelegatorShares` + - entitled party voting power should be taken as the of commission portion + of total delegators shares, + `candidate.TotalDelegatorShares * candidate.Commission` + +For more implementation ideas see spreadsheet `spec/AbsoluteFeeDistrModel.xlsx` + +As mentioned earlier, every time the voting power of a delegator bond is +changing either by unbonding or further bonding, all fees must be +simultaneously withdrawn. Similarly if the validator changes the commission +rate, all commission on fees must be simultaneously withdrawn. + +### Other general notes on fees accounting + +- When a delegator chooses to re-delegate shares, fees continue to accumulate + until the re-delegation queue reaches maturity. At the block which the queue + reaches maturity and shares are re-delegated all available fees are + simultaneously withdrawn. +- Whenever a totally new validator is added to the validator set, the `accum` + of the entire candidate must be 0, meaning that the initial value for + `candidate.Adjustment` must be set to the value of `canidate.Count` for the + height which the candidate is added on the validator set. +- The feePool of a new delegator bond will be 0 for the height at which the bond + was added. This is achieved by setting `DelegatorBond.FeeWithdrawalHeight` to + the height which the bond was added. From c66ded76467dbcf48a19cd8b60f313c1aec514f4 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Fri, 18 May 2018 14:26:32 -0400 Subject: [PATCH 02/27] staking overview.md revisions, moving files --- .../old/spec.md => old/stakingSpec1.md} | 0 .../old/spec2.md => old/stakingSpec2.md} | 0 .../fee_distribution_model.xlsx} | Bin .../{fee_distribution => rewards}/overview.md | 106 +++++--- docs/spec/slashing/transactions.md | 19 ++ docs/spec/staking/README.md | 36 ++- docs/spec/staking/overview.md | 239 +++++++++--------- docs/spec/staking/state.md | 66 ++--- docs/spec/staking/transactions.md | 118 ++++----- docs/spec/staking/valset-changes.md | 50 ++-- 10 files changed, 346 insertions(+), 288 deletions(-) rename docs/{spec/staking/old/spec.md => old/stakingSpec1.md} (100%) rename docs/{spec/staking/old/spec2.md => old/stakingSpec2.md} (100%) rename docs/spec/{fee_distribution/FeeDistributionModel.xlsx => rewards/fee_distribution_model.xlsx} (100%) rename docs/spec/{fee_distribution => rewards}/overview.md (68%) create mode 100644 docs/spec/slashing/transactions.md diff --git a/docs/spec/staking/old/spec.md b/docs/old/stakingSpec1.md similarity index 100% rename from docs/spec/staking/old/spec.md rename to docs/old/stakingSpec1.md diff --git a/docs/spec/staking/old/spec2.md b/docs/old/stakingSpec2.md similarity index 100% rename from docs/spec/staking/old/spec2.md rename to docs/old/stakingSpec2.md diff --git a/docs/spec/fee_distribution/FeeDistributionModel.xlsx b/docs/spec/rewards/fee_distribution_model.xlsx similarity index 100% rename from docs/spec/fee_distribution/FeeDistributionModel.xlsx rename to docs/spec/rewards/fee_distribution_model.xlsx diff --git a/docs/spec/fee_distribution/overview.md b/docs/spec/rewards/overview.md similarity index 68% rename from docs/spec/fee_distribution/overview.md rename to docs/spec/rewards/overview.md index 2ed0fe050..a8ffac398 100644 --- a/docs/spec/fee_distribution/overview.md +++ b/docs/spec/rewards/overview.md @@ -37,7 +37,7 @@ distribution occurs, withdrawal of fees must also occur. - when bonding, unbonding, or re-delegating tokens to an existing account a full withdrawal of the fees must occur (as the rules for lazy accounting change), - - when a candidate chooses to change the commission on fees, all accumulated + - when a validator chooses to change the commission on fees, all accumulated commission fees must be simultaneously withdrawn. When the validator is the proposer of the round, that validator (and their @@ -52,7 +52,7 @@ that `BondedShares` represents the sum of all voting power saved in the ``` proposerReward = feesCollected * (0.01 + 0.04 * sumOfVotingPowerOfPrecommitValidators / gs.BondedShares) -candidate.ProposerRewardPool += proposerReward +validator.ProposerRewardPool += proposerReward reserveTaxed = feesCollected * params.ReserveTax gs.ReservePool += reserveTaxed @@ -64,13 +64,13 @@ gs.RecentFee = distributedReward ``` The entitlement to the fee pool held by the each validator can be accounted for -lazily. First we must account for a candidate's `count` and `adjustment`. The -`count` represents a lazy accounting of what that candidates entitlement to the +lazily. First we must account for a validator's `count` and `adjustment`. The +`count` represents a lazy accounting of what that validators entitlement to the fee pool would be if there `VotingPower` was to never change and they were to never withdraw fees. ``` -candidate.count = candidate.VotingPower * BlockHeight +validator.count = validator.VotingPower * BlockHeight ``` Similarly the GlobalState count can be passively calculated whenever needed, @@ -81,9 +81,9 @@ gs.count = gs.BondedShares * BlockHeight ``` The `adjustment` term accounts for changes in voting power and withdrawals of -fees. The adjustment factor must be persisted with the candidate and modified -whenever fees are withdrawn from the candidate or the voting power of the -candidate changes. When the voting power of the candidate changes the +fees. The adjustment factor must be persisted with the validator and modified +whenever fees are withdrawn from the validator or the voting power of the +validator changes. When the voting power of the validator changes the `Adjustment` factor is increased/decreased by the cumulative difference in the voting power if the voting power has been the new voting power as opposed to the old voting power for the entire duration of the blockchain up the previous @@ -91,13 +91,13 @@ block. Each time there is an adjustment change the GlobalState (denoted `gs`) `Adjustment` must also be updated. ``` -simplePool = candidate.count / gs.count * gs.SumFeesReceived -projectedPool = candidate.PrevPower * (height-1) +simplePool = validator.count / gs.count * gs.SumFeesReceived +projectedPool = validator.PrevPower * (height-1) / (gs.PrevPower * (height-1)) * gs.PrevFeesReceived - + candidate.Power / gs.Power * gs.RecentFee + + validator.Power / gs.Power * gs.RecentFee AdjustmentChange = simplePool - projectedPool -candidate.AdjustmentRewardPool += AdjustmentChange +validator.AdjustmentRewardPool += AdjustmentChange gs.Adjustment += AdjustmentChange ``` @@ -122,55 +122,55 @@ type powerChange struct { ``` Note that the adjustment factor may result as negative if the voting power of a -different candidate has decreased. +different validator has decreased. ``` -candidate.AdjustmentRewardPool += withdrawn +validator.AdjustmentRewardPool += withdrawn gs.Adjustment += withdrawn ``` -Now the entitled fee pool of each candidate can be lazily accounted for at +Now the entitled fee pool of each validator can be lazily accounted for at any given block: ``` -candidate.feePool = candidate.simplePool - candidate.Adjustment +validator.feePool = validator.simplePool - validator.Adjustment ``` So far we have covered two sources fees which can be withdrawn from: Fees from -proposer rewards (`candidate.ProposerRewardPool`), and fees from the fee pool -(`candidate.feePool`). However we should note that all fees from fee pool are -subject to commission rate from the owner of the candidate. These next +proposer rewards (`validator.ProposerRewardPool`), and fees from the fee pool +(`validator.feePool`). However we should note that all fees from fee pool are +subject to commission rate from the owner of the validator. These next calculations outline the math behind withdrawing fee rewards as either a -delegator to a candidate providing commission, or as the owner of a candidate +delegator to a validator providing commission, or as the owner of a validator who is receiving commission. -### Calculations For Delegators and Candidates +### Calculations For Delegators and Validators The same mechanism described to calculate the fees which an entire validator is entitled to is be applied to delegator level to determine the entitled fees for -each delegator and the candidates entitled commission from `gs.FeesPool` and -`candidate.ProposerRewardPool`. +each delegator and the validators entitled commission from `gs.FeesPool` and +`validator.ProposerRewardPool`. The calculations are identical with a few modifications to the parameters: - Delegator's entitlement to `gs.FeePool`: - entitled party voting power should be taken as the effective voting power after commission is retrieved, - `bond.Shares/candidate.TotalDelegatorShares * candidate.VotingPower * (1 - candidate.Commission)` - - Delegator's entitlement to `candidate.ProposerFeePool` + `bond.Shares/validator.TotalDelegatorShares * validator.VotingPower * (1 - validator.Commission)` + - Delegator's entitlement to `validator.ProposerFeePool` - global power in this context is actually shares - `candidate.TotalDelegatorShares` + `validator.TotalDelegatorShares` - entitled party voting power should be taken as the effective shares after - commission is retrieved, `bond.Shares * (1 - candidate.Commission)` - - Candidate's commission entitlement to `gs.FeePool` + commission is retrieved, `bond.Shares * (1 - validator.Commission)` + - Validator's commission entitlement to `gs.FeePool` - entitled party voting power should be taken as the effective voting power of commission portion of total voting power, - `candidate.VotingPower * candidate.Commission` - - Candidate's commission entitlement to `candidate.ProposerFeePool` + `validator.VotingPower * validator.Commission` + - Validator's commission entitlement to `validator.ProposerFeePool` - global power in this context is actually shares - `candidate.TotalDelegatorShares` + `validator.TotalDelegatorShares` - entitled party voting power should be taken as the of commission portion of total delegators shares, - `candidate.TotalDelegatorShares * candidate.Commission` + `validator.TotalDelegatorShares * validator.Commission` For more implementation ideas see spreadsheet `spec/AbsoluteFeeDistrModel.xlsx` @@ -186,9 +186,45 @@ rate, all commission on fees must be simultaneously withdrawn. reaches maturity and shares are re-delegated all available fees are simultaneously withdrawn. - Whenever a totally new validator is added to the validator set, the `accum` - of the entire candidate must be 0, meaning that the initial value for - `candidate.Adjustment` must be set to the value of `canidate.Count` for the - height which the candidate is added on the validator set. + of the entire validator must be 0, meaning that the initial value for + `validator.Adjustment` must be set to the value of `canidate.Count` for the + height which the validator is added on the validator set. - The feePool of a new delegator bond will be 0 for the height at which the bond was added. This is achieved by setting `DelegatorBond.FeeWithdrawalHeight` to the height which the bond was added. + +### Atom provisions + +Validator provisions are minted on an hourly basis (the first block of a new +hour). The annual target of between 7% and 20%. The long-term target ratio of +bonded tokens to unbonded tokens is 67%. + +The target annual inflation rate is recalculated for each provisions cycle. The +inflation is also subject to a rate change (positive or negative) depending on +the distance from the desired ratio (67%). The maximum rate change possible is +defined to be 13% per year, however the annual inflation is capped as between +7% and 20%. + +```go +inflationRateChange(0) = 0 +Inflation(0) = 0.07 + +bondedRatio = Pool.BondedTokens / Pool.TotalSupplyTokens +AnnualInflationRateChange = (1 - bondedRatio / 0.67) * 0.13 + +annualInflation += AnnualInflationRateChange + +if annualInflation > 0.20 then Inflation = 0.20 +if annualInflation < 0.07 then Inflation = 0.07 + +provisionTokensHourly = Pool.TotalSupplyTokens * Inflation / (365.25*24) +``` + +Because the validators hold a relative bonded share (`GlobalStakeShares`), when +more bonded tokens are added proportionally to all validators, the only term +which needs to be updated is the `GlobalState.BondedPool`. So for each +provisions cycle: + +```go +Pool.BondedPool += provisionTokensHourly +``` diff --git a/docs/spec/slashing/transactions.md b/docs/spec/slashing/transactions.md new file mode 100644 index 000000000..cdf495e4d --- /dev/null +++ b/docs/spec/slashing/transactions.md @@ -0,0 +1,19 @@ + +### TxProveLive + +If a validator was automatically unbonded due to liveness issues and wishes to +assert it is still online, it can send `TxProveLive`: + +```golang +type TxProveLive struct { + PubKey crypto.PubKey +} +``` + +All delegators in the temporary unbonding pool which have not +transacted to move will be bonded back to the now-live validator and begin to +once again collect provisions and rewards. + +``` +TODO: pseudo-code +``` diff --git a/docs/spec/staking/README.md b/docs/spec/staking/README.md index 82604e2de..30dbf1dd3 100644 --- a/docs/spec/staking/README.md +++ b/docs/spec/staking/README.md @@ -2,30 +2,38 @@ ## Abstract -This paper specifies the Staking module of the Cosmos-SDK, which was first described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) in June 2016. +This paper specifies the Staking module of the Cosmos-SDK, which was first +described in the [Cosmos Whitepaper](https://cosmos.network/about/whitepaper) +in June 2016. -The module enables Cosmos-SDK based blockchain to support an advanced Proof-of-Stake system. In this system, holders of the native staking token of the chain can become candidate validators and can delegate tokens to candidate validators, ultimately determining the effective validator set for the system. +The module enables Cosmos-SDK based blockchain to support an advanced +Proof-of-Stake system. In this system, holders of the native staking token of +the chain can become validators and can delegate tokens to validator +validators, ultimately determining the effective validator set for the system. -This module will be used in the Cosmos Hub, the first Hub in the Cosmos network. +This module will be used in the Cosmos Hub, the first Hub in the Cosmos +network. ## Contents -The following specification uses *Atom* as the native staking token. The module can be adapted to any Proof-Of-Stake blockchain by replacing *Atom* with the native staking token of the chain. +The following specification uses *Atom* as the native staking token. The module +can be adapted to any Proof-Of-Stake blockchain by replacing *Atom* with the +native staking token of the chain. 1. **[Design overview](overview.md)** 2. **Implementation** 1. **[State](state.md)** - 1. Global State - 2. Validator Candidates - 3. Delegator Bonds - 4. Unbond and Rebond Queue + 1. Params + 1. Pool + 2. Validators + 3. Delegations 2. **[Transactions](transactions.md)** - 1. Declare Candidacy - 2. Edit Candidacy - 3. Delegate - 4. Unbond - 5. Redelegate - 6. ProveLive + 1. Create-Validator + 2. Edit-Validator + 3. Repeal-Revocation + 4. Delegate + 5. Unbond + 6. Redelegate 3. **[Validator Set Changes](valset-changes.md)** 1. Validator set updates 2. Slashing diff --git a/docs/spec/staking/overview.md b/docs/spec/staking/overview.md index a202fbc11..053d59d7a 100644 --- a/docs/spec/staking/overview.md +++ b/docs/spec/staking/overview.md @@ -2,100 +2,125 @@ ## Overview -The Cosmos Hub is a Tendermint-based Proof of Stake blockchain system that -serves as a backbone of the Cosmos ecosystem. It is operated and secured by an -open and globally decentralized set of validators. Tendermint consensus is a -Byzantine fault-tolerant distributed protocol that involves all validators in -the process of exchanging protocol messages in the production of each block. To -avoid Nothing-at-Stake problem, a validator in Tendermint needs to lock up -coins in a bond deposit. Tendermint protocol messages are signed by the -validator's private key, and this is a basis for Tendermint strict -accountability that allows punishing misbehaving validators by slashing -(burning) their bonded Atoms. On the other hand, validators are rewarded for +The Cosmos Hub is a Tendermint-based Delegated Proof of Stake (DPos) blockchain +system that serves as a backbone of the Cosmos ecosystem. It is operated and +secured by an open and globally decentralized set of validators. Tendermint +consensus is a Byzantine fault-tolerant distributed protocol that involves all +validators in the process of exchanging protocol messages in the production of +each block. To avoid Nothing-at-Stake problem, a validator in Tendermint needs +to lock up coins in a bond deposit. Tendermint protocol messages are signed by +the validator's private key, and this is a basis for Tendermint strict +accountability that allows punishing misbehaving validators by slashing +(burning) their bonded Atoms. On the other hand, validators are rewarded for their service of securing blockchain network by the inflationary provisions and -transactions fees. This incentives correct behavior of the validators and +transactions fees. This incentives correct behavior of the validators and provides the economic security of the network. -The native token of the Cosmos Hub is called Atom; becoming a validator of the +The native token of the Cosmos Hub is called Atom; becoming a validator of the Cosmos Hub requires holding Atoms. However, not all Atom holders are validators of the Cosmos Hub. More precisely, there is a selection process that determines -the validator set as a subset of all validator candidates (Atom holders that -wants to become a validator). The other option for Atom holder is to delegate -their atoms to validators, i.e., being a delegator. A delegator is an Atom -holder that has bonded its Atoms by delegating it to a validator (or validator -candidate). By bonding Atoms to secure the network (and taking a risk of being -slashed in case of misbehaviour), a user is rewarded with inflationary -provisions and transaction fees proportional to the amount of its bonded Atoms. -The Cosmos Hub is designed to efficiently facilitate a small numbers of -validators (hundreds), and large numbers of delegators (tens of thousands). -More precisely, it is the role of the Staking module of the Cosmos Hub to -support various staking functionality including validator set selection, -delegating, bonding and withdrawing Atoms, and the distribution of inflationary -provisions and transaction fees. +the validator set as a subset of all validators (Atom holders that +wants to become a validator). The other option for Atom holder is to delegate +their atoms to validators, i.e., being a delegator. A delegator is an Atom +holder that has bonded its Atoms by delegating it to a validator. By bonding +Atoms to secure the network (and taking a risk of being slashed in case of +misbehaviour), a user is rewarded with inflationary provisions and transaction +fees proportional to the amount of its bonded Atoms. The Cosmos Hub is +designed to efficiently facilitate a small numbers of validators (hundreds), +and large numbers of delegators (tens of thousands). More precisely, it is the +role of the Staking module of the Cosmos Hub to support various staking +functionality including validator set selection, delegating, bonding and +withdrawing Atoms, and the distribution of inflationary provisions and +transaction fees. ## Basic Terms and Definitions -* Cosmsos Hub - a Tendermint-based Proof of Stake blockchain system +* Cosmsos Hub - a Tendermint-based Delegated Proof of Stake (DPos) + blockchain system * Atom - native token of the Cosmsos Hub * Atom holder - an entity that holds some amount of Atoms -* Candidate - an Atom holder that is actively involved in the Tendermint - blockchain protocol (running Tendermint Full Node (TODO: add link to Full - Node definition) and is competing with other candidates to be elected as a - validator (TODO: add link to Validator definition)) -* Validator - a candidate that is currently selected among a set of candidates - to be able to sign protocol messages in the Tendermint consensus protocol -* Delegator - an Atom holder that has bonded some of its Atoms by delegating - them to a validator (or a candidate) -* Bonding Atoms - a process of locking Atoms in a bond deposit (putting Atoms - under protocol control). Atoms are always bonded through a validator (or - candidate) process. Bonded atoms can be slashed (burned) in case a validator - process misbehaves (does not behave according to the protocol specification). - Atom holders can regain access to their bonded Atoms if they have not been - slashed by waiting an Unbonding period. -* Unbonding period - a period of time after which Atom holder gains access to - its bonded Atoms (they can be withdrawn to a user account) or they can be - re-delegated. -* Inflationary provisions - inflation is the process of increasing the Atom supply. - Atoms are periodically created on the Cosmos Hub and issued to bonded Atom holders. - The goal of inflation is to incentize most of the Atoms in existence to be bonded. +* Pool - Global object within the Cosmos Hub which accounts global state + including the total amount of bonded, unbonding, and unbonded atoms +* Validator Share - Share which a validator holds to represent its portion of + bonded, unbonding or unbonded atoms in the pool +* Delegation Share - Shares which a delegation bond holds to represent its + portion of bonded, unbonding or unbonded shares in a validator +* Bond Atoms - a process of locking Atoms in a delegation share which holds them + under protocol control. +* Slash Atoms - the process of burning atoms in the pool and assoiated + validator shares of a misbehaving validator, (not behaving according to the + protocol specification). This process devalues the worth of delegation shares + of the given validator +* Unbond Shares - Process of retrieving atoms from shares. If the shares are + bonded the shares must first remain in an inbetween unbonding state for the + duration of the unbonding period +* Redelegating Shares - Process of redelegating atoms from one validator to + another. This process is instantanious, the redelegated delegation is + slashible to the old validator for all blocks before the redelegation and to + the new validator for all new blocks. +* Validator - entity with atoms which is either actively validating the Tendermint + protocol (bonded validator) or vying to validate . +* Bonded Validator - a validator whose atoms are currently bonded and liable to + be slashed. These validators are to be able to sign protocol messages in the + Tendermint consensus protocol. There are limited number of bonded validators + at Cosmos Hub genesis there is a maximum of 100 bonded validators. Only Bonded + Validators receive atom provisions and fee rewards. +* Delegator - an Atom holder that has bonded Atoms to a validator +* Unbonding period - time required in the unbonding state when unbonding + shares. Time slashable to old validator after a redelegation. Time for which + validators can be slashed after an infraction +* Atom provisions - The process of increasing the Atom supply. Atoms are + periodically created on the Cosmos Hub and issued to bonded Atom holders. + The goal of inflation is to incentize most of the Atoms in existence to be + bonded. Atoms are distributed unbonded and using the fee_distribution mechanism * Transaction fees - transaction fee is a fee that is included in a Cosmsos Hub - transaction. The fees are collected by the current validator set and - distributed among validators and delegators in proportion to their bonded - Atom share. + transaction. The fees are collected by the current validator set and + distributed among validators and delegators in proportion to their bonded + Atom share * Commission fee - a fee taken from the transaction fees by a validator for - their service + their service ## The pool and the share At the core of the Staking module is the concept of a pool which denotes a -collection of Atoms contributed by different Atom holders. There are two global -pools in the Staking module: the bonded pool and unbonding pool. Bonded Atoms -are part of the global bonded pool. If a candidate or delegator wants to unbond -its Atoms, those Atoms are moved to the the unbonding pool for the duration of -the unbonding period. In the Staking module, a pool is a logical concept, i.e., -there is no pool data structure that would be responsible for managing pool -resources. Instead, it is managed in a distributed way. More precisely, at the -global level, for each pool, we track only the total amount of bonded or unbonded -Atoms and the current amount of issued shares. A share is a unit of Atom distribution -and the value of the share (share-to-atom exchange rate) changes during -system execution. The share-to-atom exchange rate can be computed as: +collection of Atoms contributed by different Atom holders. There are three +pools in the Staking module: the bonded, unbonding, and unbonded pool. Bonded +Atoms are part of the global bonded pool. If a validator or delegator wants to +unbond its Shares, these Shares are moved to the the unbonding pool for the +duration of the unbonding period. From here normally Atoms will be moved +directly into the delegators wallet, however under the situation thatn an +entire validator gets unbonded, the Atoms of the delegations will remain with +the validator and moved to the unbonded pool. For each pool, the total amount +of bonded, unbonding, or unbonded Atoms are tracked as well as the current +amount of issued pool-shares, the specific holdings of these shares by +validators are tracked in protocol by the validator object. + +A share is a unit of Atom distribution and the value of the share +(share-to-atom exchange rate) can change during system execution. The +share-to-atom exchange rate can be computed as: `share-to-atom-exchange-rate = size of the pool / ammount of issued shares` -Then for each validator candidate (in a per candidate data structure) we keep track of -the amount of shares the candidate owns in a pool. At any point in time, -the exact amount of Atoms a candidate has in the pool can be computed as the -number of shares it owns multiplied with the current share-to-atom exchange rate: +Then for each validator (in a per validator data structure) the protocol keeps +track of the amount of shares the validator owns in a pool. At any point in +time, the exact amount of Atoms a validator has in the pool can be computed as +the number of shares it owns multiplied with the current share-to-atom exchange +rate: -`candidate-coins = candidate.Shares * share-to-atom-exchange-rate` +`validator-coins = validator.Shares * share-to-atom-exchange-rate` -The benefit of such accounting of the pool resources is the fact that a -modification to the pool from bonding/unbonding/slashing/provisioning of -Atoms affects only global data (size of the pool and the number of shares) and -not the related validator/candidate data structure, i.e., the data structure of -other validators do not need to be modified. This has the advantage that -modifying global data is much cheaper computationally than modifying data of -every validator. Let's explain this further with several small examples: +The benefit of such accounting of the pool resources is the fact that a +modification to the pool from bonding/unbonding/slashing of Atoms affects only +global data (size of the pool and the number of shares) and not the related +validator data structure, i.e., the data structure of other validators do not +need to be modified. This has the advantage that modifying global data is much +cheaper computationally than modifying data of every validator. Let's explain +this further with several small examples: + +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXX TODO make way less verbose lets use bullet points to describe the example +XXX Also need to update to not include bonded atom provisions all atoms are +XXX redistributed with the fee pool now We consider initially 4 validators p1, p2, p3 and p4, and that each validator has bonded 10 Atoms to the bonded pool. Furthermore, let's assume that we have @@ -140,33 +165,38 @@ delegators (we will explain this in section X). #### Delegator shares -A candidate is, depending on it's status, contributing Atoms to either the -bonded or unbonding pool, and in return gets some amount of (global) pool -shares. Note that not all those Atoms (and respective shares) are owned by the -candidate as some Atoms could be delegated to a candidate. The mechanism for -distribution of Atoms (and shares) between a candidate and it's delegators is -based on a notion of delegator shares. More precisely, every candidate is -issuing (local) delegator shares (`Candidate.IssuedDelegatorShares`) that -represents some portion of global shares managed by the candidate -(`Candidate.GlobalStakeShares`). The principle behind managing delegator shares -is the same as described in [Section](#The pool and the share). We now +A validator is, depending on its status, contributing Atoms to either the bond, +unbonding or unbonded pool - the validator in turn holds some amount of pool +shares. Not all of a validators Atoms (and respective shares) are owned by the +validator, some may be owned by delegators to that validator. The mechanism for +distribution of Atoms (and shares) between a validator and its delegators is +based on a notion of delegator shares. More precisely, every validator is +issuing (local) delegator shares (`Validator.IssuedDelegatorShares`) that +represents some portion of global shares managed by the validator +(`Validator.GlobalStakeShares`). The principle behind managing delegator shares +is the same as described in [Section](#The pool and the share). We now illustrate it with an example. +XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +XXX TODO make way less verbose lets use bullet points to describe the example +XXX Also need to update to not include bonded atom provisions all atoms are +XXX redistributed with the fee pool now + Let's consider 4 validators p1, p2, p3 and p4, and assume that each validator has bonded 10 Atoms to the bonded pool. Furthermore, let's assume that we have issued initially 40 global shares, i.e., that `share-to-atom-exchange-rate = 1 atom per share`. So we will set `GlobalState.BondedPool = 40` and `GlobalState.BondedShares = 40` and in the -Candidate data structure of each validator `Candidate.GlobalStakeShares = 10`. +Validator data structure of each validator `Validator.GlobalStakeShares = 10`. Furthermore, each validator issued 10 delegator shares which are initially -owned by itself, i.e., `Candidate.IssuedDelegatorShares = 10`, where +owned by itself, i.e., `Validator.IssuedDelegatorShares = 10`, where `delegator-share-to-global-share-ex-rate = 1 global share per delegator share`. Now lets assume that a delegator d1 delegates 5 atoms to a validator p1 and consider what are the updates we need to make to the data structures. First, `GlobalState.BondedPool = 45` and `GlobalState.BondedShares = 45`. Then, for -validator p1 we have `Candidate.GlobalStakeShares = 15`, but we also need to +validator p1 we have `Validator.GlobalStakeShares = 15`, but we also need to issue also additional delegator shares, i.e., -`Candidate.IssuedDelegatorShares = 15` as the delegator d1 now owns 5 delegator +`Validator.IssuedDelegatorShares = 15` as the delegator d1 now owns 5 delegator shares of validator p1, where each delegator share is worth 1 global shares, i.e, 1 Atom. Lets see now what happens after 5 new Atoms are created due to inflation. In that case, we only need to update `GlobalState.BondedPool` which @@ -177,38 +207,3 @@ Therefore, a delegator d1 now owns: `delegatorCoins = 5 (delegator shares) * 1 (delegator-share-to-global-share-ex-rate) * 50/45 (share-to-atom-ex-rate) = 5.55 Atoms` -### Inflation provisions - -Validator provisions are minted on an hourly basis (the first block of a new -hour). The annual target of between 7% and 20%. The long-term target ratio of -bonded tokens to unbonded tokens is 67%. - -The target annual inflation rate is recalculated for each provisions cycle. The -inflation is also subject to a rate change (positive or negative) depending on -the distance from the desired ratio (67%). The maximum rate change possible is -defined to be 13% per year, however the annual inflation is capped as between -7% and 20%. - -```go -inflationRateChange(0) = 0 -GlobalState.Inflation(0) = 0.07 - -bondedRatio = GlobalState.BondedPool / GlobalState.TotalSupply -AnnualInflationRateChange = (1 - bondedRatio / 0.67) * 0.13 - -annualInflation += AnnualInflationRateChange - -if annualInflation > 0.20 then GlobalState.Inflation = 0.20 -if annualInflation < 0.07 then GlobalState.Inflation = 0.07 - -provisionTokensHourly = GlobalState.TotalSupply * GlobalState.Inflation / (365.25*24) -``` - -Because the validators hold a relative bonded share (`GlobalStakeShares`), when -more bonded tokens are added proportionally to all validators, the only term -which needs to be updated is the `GlobalState.BondedPool`. So for each -provisions cycle: - -```go -GlobalState.BondedPool += provisionTokensHourly -``` diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 2bcf13dea..28e4faaf2 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -4,10 +4,10 @@ The staking module persists the following information to the store: * `GlobalState`, a struct describing the global pools, inflation, and fees -* `ValidatorCandidates: => `, a map of all candidates (including current validators) in the store, +* `ValidatorValidators: => `, a map of all validators (including current validators) in the store, indexed by their public key and shares in the global pool. -* `DelegatorBonds: < delegator-address | candidate-pubkey > => `. a map of all delegations by a delegator to a candidate, -indexed by delegator address and candidate pubkey. +* `DelegatorBonds: < delegator-address | validator-pubkey > => `. a map of all delegations by a delegator to a validator, +indexed by delegator address and validator pubkey. public key * `UnbondQueue`, the queue of unbonding delegations * `RedelegateQueue`, the queue of re-delegations @@ -25,7 +25,7 @@ type GlobalState struct { TotalSupply int64 // total supply of Atoms BondedPool int64 // reserve of bonded tokens BondedShares rational.Rat // sum of all shares distributed for the BondedPool - UnbondedPool int64 // reserve of unbonding tokens held with candidates + UnbondedPool int64 // reserve of unbonding tokens held with validators UnbondedShares rational.Rat // sum of all shares distributed for the UnbondedPool InflationLastTime int64 // timestamp of last processing of inflation Inflation rational.Rat // current annual inflation rate @@ -57,22 +57,22 @@ type Params struct { } ``` -### Candidate +### Validator -The `Candidate` holds the current state and some historical -actions of validators or candidate-validators. +The `Validator` holds the current state and some historical +actions of validators. ``` go -type CandidateStatus byte +type ValidatorStatus byte const ( - Bonded CandidateStatus = 0x01 - Unbonded CandidateStatus = 0x02 - Revoked CandidateStatus = 0x03 + Bonded ValidatorStatus = 0x01 + Unbonded ValidatorStatus = 0x02 + Revoked ValidatorStatus = 0x03 ) -type Candidate struct { - Status CandidateStatus +type Validator struct { + Status ValidatorStatus ConsensusPubKey crypto.PubKey GovernancePubKey crypto.PubKey Owner crypto.Address @@ -98,30 +98,30 @@ type Description struct { } ``` -Candidate parameters are described: -* Status: it can be Bonded (active validator), Unbonding (validator candidate) +Validator parameters are described: +* Status: it can be Bonded (active validator), Unbonding (validator) or Revoked -* ConsensusPubKey: candidate public key that is used strictly for participating in +* ConsensusPubKey: validator public key that is used strictly for participating in consensus * GovernancePubKey: public key used by the validator for governance voting * Owner: Address that is allowed to unbond coins. * GlobalStakeShares: Represents shares of `GlobalState.BondedPool` if - `Candidate.Status` is `Bonded`; or shares of `GlobalState.Unbondingt Pool` + `Validator.Status` is `Bonded`; or shares of `GlobalState.Unbondingt Pool` otherwise -* IssuedDelegatorShares: Sum of all shares a candidate issued to delegators - (which includes the candidate's self-bond); a delegator share represents - their stake in the Candidate's `GlobalStakeShares` +* IssuedDelegatorShares: Sum of all shares a validator issued to delegators + (which includes the validator's self-bond); a delegator share represents + their stake in the Validator's `GlobalStakeShares` * RedelegatingShares: The portion of `IssuedDelegatorShares` which are currently re-delegating to a new validator * VotingPower: Proportional to the amount of bonded tokens which the validator - has if `Candidate.Status` is `Bonded`; otherwise it is equal to `0` + has if `Validator.Status` is `Bonded`; otherwise it is equal to `0` * Commission: The commission rate of fees charged to any delegators -* CommissionMax: The maximum commission rate this candidate can charge each +* CommissionMax: The maximum commission rate this validator can charge each day from the date `GlobalState.DateLastCommissionReset` -* CommissionChangeRate: The maximum daily increase of the candidate commission +* CommissionChangeRate: The maximum daily increase of the validator commission * CommissionChangeToday: Counter for the amount of change to commission rate which has occurred today, reset on the first block of each day (UTC time) -* ProposerRewardPool: reward pool for extra fees collected when this candidate +* ProposerRewardPool: reward pool for extra fees collected when this validator is the proposer of a block * Adjustment factor used to passively calculate each validators entitled fees from `GlobalState.FeePool` @@ -135,14 +135,14 @@ Candidate parameters are described: ### DelegatorBond -Atom holders may delegate coins to candidates; under this circumstance their +Atom holders may delegate coins to validators; under this circumstance their funds are held in a `DelegatorBond` data structure. It is owned by one -delegator, and is associated with the shares for one candidate. The sender of +delegator, and is associated with the shares for one validator. The sender of the transaction is the owner of the bond. ``` go type DelegatorBond struct { - Candidate crypto.PubKey + Validator crypto.PubKey Shares rational.Rat AdjustmentFeePool coin.Coins AdjustmentRewardPool coin.Coins @@ -150,12 +150,12 @@ type DelegatorBond struct { ``` Description: -* Candidate: the public key of the validator candidate: bonding too -* Shares: the number of delegator shares received from the validator candidate +* Validator: the public key of the validator: bonding too +* Shares: the number of delegator shares received from the validator * AdjustmentFeePool: Adjustment factor used to passively calculate each bonds entitled fees from `GlobalState.FeePool` * AdjustmentRewardPool: Adjustment factor used to passively calculate each - bonds entitled fees from `Candidate.ProposerRewardPool` + bonds entitled fees from `Validator.ProposerRewardPool` ### QueueElem @@ -165,7 +165,7 @@ data structure. All queue elements share a common structure: ```golang type QueueElem struct { - Candidate crypto.PubKey + Validator crypto.PubKey InitTime int64 // when the element was added to the queue } ``` @@ -185,7 +185,7 @@ type QueueElemUnbondDelegation struct { QueueElem Payout Address // account to pay out to Tokens coin.Coins // the value in Atoms of the amount of delegator shares which are unbonding - StartSlashRatio rational.Rat // candidate slash ratio + StartSlashRatio rational.Rat // validator slash ratio } ``` @@ -198,7 +198,7 @@ type QueueElemReDelegate struct { QueueElem Payout Address // account to pay out to Shares rational.Rat // amount of shares which are unbonding - NewCandidate crypto.PubKey // validator to bond to after unbond + NewValidator crypto.PubKey // validator to bond to after unbond } ``` diff --git a/docs/spec/staking/transactions.md b/docs/spec/staking/transactions.md index 52f324b0f..c80ddc7a9 100644 --- a/docs/spec/staking/transactions.md +++ b/docs/spec/staking/transactions.md @@ -18,8 +18,8 @@ reference to the queue of unbond delegations, `reDelegationQueue` is the reference for the queue of redelegations. We use `tx` to denote a reference to a transaction that is being processed, and `sender` to denote the address of the sender of the transaction. We use function -`loadCandidate(store, PubKey)` to obtain a Candidate structure from the store, -and `saveCandidate(store, candidate)` to save it. Similarly, we use +`loadValidator(store, PubKey)` to obtain a Validator structure from the store, +and `saveValidator(store, validator)` to save it. Similarly, we use `loadDelegatorBond(store, sender, PubKey)` to load a delegator bond with the key (sender and PubKey) from the store, and `saveDelegatorBond(store, sender, bond)` to save it. @@ -42,23 +42,23 @@ type TxDeclareCandidacy struct { } declareCandidacy(tx TxDeclareCandidacy): - candidate = loadCandidate(store, tx.PubKey) - if candidate != nil return // candidate with that public key already exists + validator = loadValidator(store, tx.PubKey) + if validator != nil return // validator with that public key already exists - candidate = NewCandidate(tx.PubKey) - candidate.Status = Unbonded - candidate.Owner = sender - init candidate VotingPower, GlobalStakeShares, IssuedDelegatorShares, RedelegatingShares and Adjustment to rational.Zero + validator = NewValidator(tx.PubKey) + validator.Status = Unbonded + validator.Owner = sender + init validator VotingPower, GlobalStakeShares, IssuedDelegatorShares, RedelegatingShares and Adjustment to rational.Zero init commision related fields based on the values from tx - candidate.ProposerRewardPool = Coin(0) - candidate.Description = tx.Description + validator.ProposerRewardPool = Coin(0) + validator.Description = tx.Description - saveCandidate(store, candidate) + saveValidator(store, validator) txDelegate = TxDelegate(tx.PubKey, tx.Amount) - return delegateWithCandidate(txDelegate, candidate) + return delegateWithValidator(txDelegate, validator) -// see delegateWithCandidate function in [TxDelegate](TxDelegate) +// see delegateWithValidator function in [TxDelegate](TxDelegate) ``` ### TxEditCandidacy @@ -75,14 +75,14 @@ type TxEditCandidacy struct { } editCandidacy(tx TxEditCandidacy): - candidate = loadCandidate(store, tx.PubKey) - if candidate == nil or candidate.Status == Revoked return + validator = loadValidator(store, tx.PubKey) + if validator == nil or validator.Status == Revoked return - if tx.GovernancePubKey != nil candidate.GovernancePubKey = tx.GovernancePubKey - if tx.Commission >= 0 candidate.Commission = tx.Commission - if tx.Description != nil candidate.Description = tx.Description + if tx.GovernancePubKey != nil validator.GovernancePubKey = tx.GovernancePubKey + if tx.Commission >= 0 validator.Commission = tx.Commission + if tx.Description != nil validator.Description = tx.Description - saveCandidate(store, candidate) + saveValidator(store, validator) return ``` @@ -90,7 +90,7 @@ editCandidacy(tx TxEditCandidacy): Delegator bonds are created using the `TxDelegate` transaction. Within this transaction the delegator provides an amount of coins, and in return receives -some amount of candidate's delegator shares that are assigned to +some amount of validator's delegator shares that are assigned to `DelegatorBond.Shares`. ```golang @@ -100,14 +100,14 @@ type TxDelegate struct { } delegate(tx TxDelegate): - candidate = loadCandidate(store, tx.PubKey) - if candidate == nil return - return delegateWithCandidate(tx, candidate) + validator = loadValidator(store, tx.PubKey) + if validator == nil return + return delegateWithValidator(tx, validator) -delegateWithCandidate(tx TxDelegate, candidate Candidate): - if candidate.Status == Revoked return +delegateWithValidator(tx TxDelegate, validator Validator): + if validator.Status == Revoked return - if candidate.Status == Bonded + if validator.Status == Bonded poolAccount = params.HoldBonded else poolAccount = params.HoldUnbonded @@ -118,16 +118,16 @@ delegateWithCandidate(tx TxDelegate, candidate Candidate): bond = loadDelegatorBond(store, sender, tx.PubKey) if bond == nil then bond = DelegatorBond(tx.PubKey, rational.Zero, Coin(0), Coin(0)) - issuedDelegatorShares = addTokens(tx.Amount, candidate) + issuedDelegatorShares = addTokens(tx.Amount, validator) bond.Shares += issuedDelegatorShares - saveCandidate(store, candidate) + saveValidator(store, validator) saveDelegatorBond(store, sender, bond) saveGlobalState(store, gs) return -addTokens(amount coin.Coin, candidate Candidate): - if candidate.Status == Bonded +addTokens(amount coin.Coin, validator Validator): + if validator.Status == Bonded gs.BondedPool += amount issuedShares = amount / exchangeRate(gs.BondedShares, gs.BondedPool) gs.BondedShares += issuedShares @@ -136,15 +136,15 @@ addTokens(amount coin.Coin, candidate Candidate): issuedShares = amount / exchangeRate(gs.UnbondedShares, gs.UnbondedPool) gs.UnbondedShares += issuedShares - candidate.GlobalStakeShares += issuedShares + validator.GlobalStakeShares += issuedShares - if candidate.IssuedDelegatorShares.IsZero() + if validator.IssuedDelegatorShares.IsZero() exRate = rational.One else - exRate = candidate.GlobalStakeShares / candidate.IssuedDelegatorShares + exRate = validator.GlobalStakeShares / validator.IssuedDelegatorShares issuedDelegatorShares = issuedShares / exRate - candidate.IssuedDelegatorShares += issuedDelegatorShares + validator.IssuedDelegatorShares += issuedDelegatorShares return issuedDelegatorShares exchangeRate(shares rational.Rat, tokenAmount int64): @@ -170,20 +170,20 @@ unbond(tx TxUnbond): bond.Shares -= tx.Shares - candidate = loadCandidate(store, tx.PubKey) + validator = loadValidator(store, tx.PubKey) revokeCandidacy = false if bond.Shares.IsZero() - if sender == candidate.Owner and candidate.Status != Revoked then revokeCandidacy = true then removeDelegatorBond(store, sender, bond) + if sender == validator.Owner and validator.Status != Revoked then revokeCandidacy = true then removeDelegatorBond(store, sender, bond) else saveDelegatorBond(store, sender, bond) - if candidate.Status == Bonded + if validator.Status == Bonded poolAccount = params.HoldBonded else poolAccount = params.HoldUnbonded - returnedCoins = removeShares(candidate, shares) + returnedCoins = removeShares(validator, shares) unbondDelegationElem = QueueElemUnbondDelegation(tx.PubKey, currentHeight(), sender, returnedCoins, startSlashRatio) unbondDelegationQueue.add(unbondDelegationElem) @@ -191,21 +191,21 @@ unbond(tx TxUnbond): transfer(poolAccount, unbondingPoolAddress, returnCoins) if revokeCandidacy - if candidate.Status == Bonded then bondedToUnbondedPool(candidate) - candidate.Status = Revoked + if validator.Status == Bonded then bondedToUnbondedPool(validator) + validator.Status = Revoked - if candidate.IssuedDelegatorShares.IsZero() - removeCandidate(store, tx.PubKey) + if validator.IssuedDelegatorShares.IsZero() + removeValidator(store, tx.PubKey) else - saveCandidate(store, candidate) + saveValidator(store, validator) saveGlobalState(store, gs) return -removeShares(candidate Candidate, shares rational.Rat): - globalPoolSharesToRemove = delegatorShareExRate(candidate) * shares +removeShares(validator Validator, shares rational.Rat): + globalPoolSharesToRemove = delegatorShareExRate(validator) * shares - if candidate.Status == Bonded + if validator.Status == Bonded gs.BondedShares -= globalPoolSharesToRemove removedTokens = exchangeRate(gs.BondedShares, gs.BondedPool) * globalPoolSharesToRemove gs.BondedPool -= removedTokens @@ -214,25 +214,25 @@ removeShares(candidate Candidate, shares rational.Rat): removedTokens = exchangeRate(gs.UnbondedShares, gs.UnbondedPool) * globalPoolSharesToRemove gs.UnbondedPool -= removedTokens - candidate.GlobalStakeShares -= removedTokens - candidate.IssuedDelegatorShares -= shares + validator.GlobalStakeShares -= removedTokens + validator.IssuedDelegatorShares -= shares return returnedCoins -delegatorShareExRate(candidate Candidate): - if candidate.IssuedDelegatorShares.IsZero() then return rational.One - return candidate.GlobalStakeShares / candidate.IssuedDelegatorShares +delegatorShareExRate(validator Validator): + if validator.IssuedDelegatorShares.IsZero() then return rational.One + return validator.GlobalStakeShares / validator.IssuedDelegatorShares -bondedToUnbondedPool(candidate Candidate): - removedTokens = exchangeRate(gs.BondedShares, gs.BondedPool) * candidate.GlobalStakeShares - gs.BondedShares -= candidate.GlobalStakeShares +bondedToUnbondedPool(validator Validator): + removedTokens = exchangeRate(gs.BondedShares, gs.BondedPool) * validator.GlobalStakeShares + gs.BondedShares -= validator.GlobalStakeShares gs.BondedPool -= removedTokens gs.UnbondedPool += removedTokens issuedShares = removedTokens / exchangeRate(gs.UnbondedShares, gs.UnbondedPool) gs.UnbondedShares += issuedShares - candidate.GlobalStakeShares = issuedShares - candidate.Status = Unbonded + validator.GlobalStakeShares = issuedShares + validator.Status = Unbonded return transfer(address of the bonded pool, address of the unbonded pool, removedTokens) ``` @@ -254,10 +254,10 @@ redelegate(tx TxRedelegate): if bond == nil then return if bond.Shares < tx.Shares return - candidate = loadCandidate(store, tx.PubKeyFrom) - if candidate == nil return + validator = loadValidator(store, tx.PubKeyFrom) + if validator == nil return - candidate.RedelegatingShares += tx.Shares + validator.RedelegatingShares += tx.Shares reDelegationElem = QueueElemReDelegate(tx.PubKeyFrom, currentHeight(), sender, tx.Shares, tx.PubKeyTo) redelegationQueue.add(reDelegationElem) return diff --git a/docs/spec/staking/valset-changes.md b/docs/spec/staking/valset-changes.md index bc52b8998..0547b171f 100644 --- a/docs/spec/staking/valset-changes.md +++ b/docs/spec/staking/valset-changes.md @@ -36,10 +36,10 @@ tick(ctx Context): if time > reDelegationQueue.head().InitTime + UnbondingPeriod for each element elem in the unbondDelegationQueue where time > elem.InitTime + UnbondingPeriod do - candidate = getCandidate(store, elem.PubKey) - returnedCoins = removeShares(candidate, elem.Shares) - candidate.RedelegatingShares -= elem.Shares - delegateWithCandidate(TxDelegate(elem.NewCandidate, returnedCoins), candidate) + validator = getValidator(store, elem.PubKey) + returnedCoins = removeShares(validator, elem.Shares) + validator.RedelegatingShares -= elem.Shares + delegateWithValidator(TxDelegate(elem.NewValidator, returnedCoins), validator) reDelegationQueue.remove(elem) return UpdateValidatorSet() @@ -61,42 +61,42 @@ nextInflation(hrsPerYr rational.Rat): return inflation UpdateValidatorSet(): - candidates = loadCandidates(store) + validators = loadValidators(store) - v1 = candidates.Validators() - v2 = updateVotingPower(candidates).Validators() + v1 = validators.Validators() + v2 = updateVotingPower(validators).Validators() change = v1.validatorsUpdated(v2) // determine all updated validators between two validator sets return change -updateVotingPower(candidates Candidates): - foreach candidate in candidates do - candidate.VotingPower = (candidate.IssuedDelegatorShares - candidate.RedelegatingShares) * delegatorShareExRate(candidate) +updateVotingPower(validators Validators): + foreach validator in validators do + validator.VotingPower = (validator.IssuedDelegatorShares - validator.RedelegatingShares) * delegatorShareExRate(validator) - candidates.Sort() + validators.Sort() - foreach candidate in candidates do - if candidate is not in the first params.MaxVals - candidate.VotingPower = rational.Zero - if candidate.Status == Bonded then bondedToUnbondedPool(candidate Candidate) + foreach validator in validators do + if validator is not in the first params.MaxVals + validator.VotingPower = rational.Zero + if validator.Status == Bonded then bondedToUnbondedPool(validator Validator) - else if candidate.Status == UnBonded then unbondedToBondedPool(candidate) + else if validator.Status == UnBonded then unbondedToBondedPool(validator) - saveCandidate(store, c) + saveValidator(store, c) - return candidates + return validators -unbondedToBondedPool(candidate Candidate): - removedTokens = exchangeRate(gs.UnbondedShares, gs.UnbondedPool) * candidate.GlobalStakeShares - gs.UnbondedShares -= candidate.GlobalStakeShares +unbondedToBondedPool(validator Validator): + removedTokens = exchangeRate(gs.UnbondedShares, gs.UnbondedPool) * validator.GlobalStakeShares + gs.UnbondedShares -= validator.GlobalStakeShares gs.UnbondedPool -= removedTokens gs.BondedPool += removedTokens issuedShares = removedTokens / exchangeRate(gs.BondedShares, gs.BondedPool) gs.BondedShares += issuedShares - candidate.GlobalStakeShares = issuedShares - candidate.Status = Bonded + validator.GlobalStakeShares = issuedShares + validator.Status = Bonded return transfer(address of the unbonded pool, address of the bonded pool, removedTokens) ``` @@ -151,7 +151,7 @@ LastCommit and +2/3 (see [TODO](https://github.com/cosmos/cosmos-sdk/issues/967) Validators are penalized for failing to be included in the LastCommit for some number of blocks by being automatically unbonded. -The following information is stored with each validator candidate, and is only non-zero if the candidate becomes an active validator: +The following information is stored with each validator, and is only non-zero if the validator becomes an active validator: ```go type ValidatorSigningInfo struct { @@ -161,7 +161,7 @@ type ValidatorSigningInfo struct { ``` Where: -* `StartHeight` is set to the height that the candidate became an active validator (with non-zero voting power). +* `StartHeight` is set to the height that the validator became an active validator (with non-zero voting power). * `SignedBlocksBitArray` is a bit-array of size `SIGNED_BLOCKS_WINDOW` that records, for each of the last `SIGNED_BLOCKS_WINDOW` blocks, whether or not this validator was included in the LastCommit. It uses a `0` if the validator was included, and a `1` if it was not. Note it is initialized with all 0s. From 55cc086f55fa0bffd03b8df72aacfd2a6d3cbb11 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Fri, 18 May 2018 15:08:12 -0400 Subject: [PATCH 03/27] docs reorganization --- docs/{old => _attic}/basecoin/basics.rst | 0 docs/{old => _attic}/basecoin/extensions.rst | 0 docs/{old => _attic}/glossary.rst | 0 docs/{old => _attic}/ibc.rst | 0 docs/{old => _attic}/keys.md | 0 docs/{old => _attic}/replay-protection.rst | 0 docs/{old => _attic}/staking/key-management.rst | 0 docs/{old => _attic}/staking/local-testnet.rst | 0 docs/{old => _attic}/staking/public-testnet.rst | 0 docs/{old => _attic}/stakingSpec1.md | 0 docs/{old => _attic}/stakingSpec2.md | 0 docs/{ => guides}/Makefile | 0 docs/{ => guides}/conf.py | 0 docs/{ => guides}/guide.md | 0 docs/{ => guides}/index.rst | 0 docs/{ => guides}/make.bat | 0 docs/{ => guides}/sdk/apps.md | 0 docs/{ => guides}/sdk/install.rst | 0 docs/{ => guides}/sdk/key-management.rst | 0 docs/{ => guides}/sdk/lcd-rest-api.yaml | 0 docs/{ => guides}/sdk/overview.rst | 0 docs/{ => guides}/staking/intro.rst | 0 docs/{spec => guides}/staking/overview.md | 2 ++ docs/{ => guides}/staking/testnet.rst | 0 .../fee_distribution_model.xlsx | Bin docs/spec/{rewards => provisioning}/overview.md | 0 docs/spec/staking/state.md | 6 ++---- 27 files changed, 4 insertions(+), 4 deletions(-) rename docs/{old => _attic}/basecoin/basics.rst (100%) rename docs/{old => _attic}/basecoin/extensions.rst (100%) rename docs/{old => _attic}/glossary.rst (100%) rename docs/{old => _attic}/ibc.rst (100%) rename docs/{old => _attic}/keys.md (100%) rename docs/{old => _attic}/replay-protection.rst (100%) rename docs/{old => _attic}/staking/key-management.rst (100%) rename docs/{old => _attic}/staking/local-testnet.rst (100%) rename docs/{old => _attic}/staking/public-testnet.rst (100%) rename docs/{old => _attic}/stakingSpec1.md (100%) rename docs/{old => _attic}/stakingSpec2.md (100%) rename docs/{ => guides}/Makefile (100%) rename docs/{ => guides}/conf.py (100%) rename docs/{ => guides}/guide.md (100%) rename docs/{ => guides}/index.rst (100%) rename docs/{ => guides}/make.bat (100%) rename docs/{ => guides}/sdk/apps.md (100%) rename docs/{ => guides}/sdk/install.rst (100%) rename docs/{ => guides}/sdk/key-management.rst (100%) rename docs/{ => guides}/sdk/lcd-rest-api.yaml (100%) rename docs/{ => guides}/sdk/overview.rst (100%) rename docs/{ => guides}/staking/intro.rst (100%) rename docs/{spec => guides}/staking/overview.md (99%) rename docs/{ => guides}/staking/testnet.rst (100%) rename docs/spec/{rewards => provisioning}/fee_distribution_model.xlsx (100%) rename docs/spec/{rewards => provisioning}/overview.md (100%) diff --git a/docs/old/basecoin/basics.rst b/docs/_attic/basecoin/basics.rst similarity index 100% rename from docs/old/basecoin/basics.rst rename to docs/_attic/basecoin/basics.rst diff --git a/docs/old/basecoin/extensions.rst b/docs/_attic/basecoin/extensions.rst similarity index 100% rename from docs/old/basecoin/extensions.rst rename to docs/_attic/basecoin/extensions.rst diff --git a/docs/old/glossary.rst b/docs/_attic/glossary.rst similarity index 100% rename from docs/old/glossary.rst rename to docs/_attic/glossary.rst diff --git a/docs/old/ibc.rst b/docs/_attic/ibc.rst similarity index 100% rename from docs/old/ibc.rst rename to docs/_attic/ibc.rst diff --git a/docs/old/keys.md b/docs/_attic/keys.md similarity index 100% rename from docs/old/keys.md rename to docs/_attic/keys.md diff --git a/docs/old/replay-protection.rst b/docs/_attic/replay-protection.rst similarity index 100% rename from docs/old/replay-protection.rst rename to docs/_attic/replay-protection.rst diff --git a/docs/old/staking/key-management.rst b/docs/_attic/staking/key-management.rst similarity index 100% rename from docs/old/staking/key-management.rst rename to docs/_attic/staking/key-management.rst diff --git a/docs/old/staking/local-testnet.rst b/docs/_attic/staking/local-testnet.rst similarity index 100% rename from docs/old/staking/local-testnet.rst rename to docs/_attic/staking/local-testnet.rst diff --git a/docs/old/staking/public-testnet.rst b/docs/_attic/staking/public-testnet.rst similarity index 100% rename from docs/old/staking/public-testnet.rst rename to docs/_attic/staking/public-testnet.rst diff --git a/docs/old/stakingSpec1.md b/docs/_attic/stakingSpec1.md similarity index 100% rename from docs/old/stakingSpec1.md rename to docs/_attic/stakingSpec1.md diff --git a/docs/old/stakingSpec2.md b/docs/_attic/stakingSpec2.md similarity index 100% rename from docs/old/stakingSpec2.md rename to docs/_attic/stakingSpec2.md diff --git a/docs/Makefile b/docs/guides/Makefile similarity index 100% rename from docs/Makefile rename to docs/guides/Makefile diff --git a/docs/conf.py b/docs/guides/conf.py similarity index 100% rename from docs/conf.py rename to docs/guides/conf.py diff --git a/docs/guide.md b/docs/guides/guide.md similarity index 100% rename from docs/guide.md rename to docs/guides/guide.md diff --git a/docs/index.rst b/docs/guides/index.rst similarity index 100% rename from docs/index.rst rename to docs/guides/index.rst diff --git a/docs/make.bat b/docs/guides/make.bat similarity index 100% rename from docs/make.bat rename to docs/guides/make.bat diff --git a/docs/sdk/apps.md b/docs/guides/sdk/apps.md similarity index 100% rename from docs/sdk/apps.md rename to docs/guides/sdk/apps.md diff --git a/docs/sdk/install.rst b/docs/guides/sdk/install.rst similarity index 100% rename from docs/sdk/install.rst rename to docs/guides/sdk/install.rst diff --git a/docs/sdk/key-management.rst b/docs/guides/sdk/key-management.rst similarity index 100% rename from docs/sdk/key-management.rst rename to docs/guides/sdk/key-management.rst diff --git a/docs/sdk/lcd-rest-api.yaml b/docs/guides/sdk/lcd-rest-api.yaml similarity index 100% rename from docs/sdk/lcd-rest-api.yaml rename to docs/guides/sdk/lcd-rest-api.yaml diff --git a/docs/sdk/overview.rst b/docs/guides/sdk/overview.rst similarity index 100% rename from docs/sdk/overview.rst rename to docs/guides/sdk/overview.rst diff --git a/docs/staking/intro.rst b/docs/guides/staking/intro.rst similarity index 100% rename from docs/staking/intro.rst rename to docs/guides/staking/intro.rst diff --git a/docs/spec/staking/overview.md b/docs/guides/staking/overview.md similarity index 99% rename from docs/spec/staking/overview.md rename to docs/guides/staking/overview.md index 053d59d7a..9867dc95e 100644 --- a/docs/spec/staking/overview.md +++ b/docs/guides/staking/overview.md @@ -1,3 +1,5 @@ +//TODO update .rst + # Staking Module ## Overview diff --git a/docs/staking/testnet.rst b/docs/guides/staking/testnet.rst similarity index 100% rename from docs/staking/testnet.rst rename to docs/guides/staking/testnet.rst diff --git a/docs/spec/rewards/fee_distribution_model.xlsx b/docs/spec/provisioning/fee_distribution_model.xlsx similarity index 100% rename from docs/spec/rewards/fee_distribution_model.xlsx rename to docs/spec/provisioning/fee_distribution_model.xlsx diff --git a/docs/spec/rewards/overview.md b/docs/spec/provisioning/overview.md similarity index 100% rename from docs/spec/rewards/overview.md rename to docs/spec/provisioning/overview.md diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 28e4faaf2..397ce1c1c 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -2,15 +2,13 @@ ## State The staking module persists the following information to the store: -* `GlobalState`, a struct describing the global pools, inflation, and - fees +* `Params`, a struct describing the global pools, inflation, and fees +* `Pool`, a struct describing the global pools, inflation, and fees * `ValidatorValidators: => `, a map of all validators (including current validators) in the store, indexed by their public key and shares in the global pool. * `DelegatorBonds: < delegator-address | validator-pubkey > => `. a map of all delegations by a delegator to a validator, indexed by delegator address and validator pubkey. public key -* `UnbondQueue`, the queue of unbonding delegations -* `RedelegateQueue`, the queue of re-delegations ### Global State From 35956c1c789ef8ca0bce766fb25e41f0d4f9e9a8 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 23 May 2018 15:42:37 -0400 Subject: [PATCH 04/27] staking spec state revisions --- docs/spec/slashing/state.md | 13 ++ docs/spec/staking/state.md | 240 ++++++++++++++---------------------- x/stake/delegation.go | 1 - x/stake/shares.go | 3 - x/stake/validator.go | 8 +- 5 files changed, 110 insertions(+), 155 deletions(-) create mode 100644 docs/spec/slashing/state.md diff --git a/docs/spec/slashing/state.md b/docs/spec/slashing/state.md new file mode 100644 index 000000000..0711b01aa --- /dev/null +++ b/docs/spec/slashing/state.md @@ -0,0 +1,13 @@ + + +Validator + +* Adjustment factor used to passively calculate each validators entitled fees + from `GlobalState.FeePool` + +Delegation Shares + +* AdjustmentFeePool: Adjustment factor used to passively calculate each bonds + entitled fees from `GlobalState.FeePool` +* AdjustmentRewardPool: Adjustment factor used to passively calculate each + bonds entitled fees from `Validator.ProposerRewardPool` diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 397ce1c1c..c5601d044 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -10,193 +10,139 @@ indexed by their public key and shares in the global pool. indexed by delegator address and validator pubkey. public key -### Global State +### Pool -The GlobalState contains information about the total amount of Atoms, the -global bonded/unbonded position, the Atom inflation rate, and the fees. +The pool is a space for all dynamic global state of the Cosmos Hub. It tracks +information about the total amounts of Atoms in all states, representative +validator shares for stake in the global pools, moving Atom inflation +information, etc. -`Params` is global data structure that stores system parameters and defines overall functioning of the -module. +```golang +type Pool struct { + LooseUnbondedTokens int64 // tokens not associated with any validator + UnbondedTokens int64 // reserve of unbonded tokens held with validators + UnbondingTokens int64 // tokens moving from bonded to unbonded pool + BondedTokens int64 // reserve of bonded tokens + UnbondedShares sdk.Rat // sum of all shares distributed for the Unbonded Pool + UnbondingShares sdk.Rat // shares moving from Bonded to Unbonded Pool + BondedShares sdk.Rat // sum of all shares distributed for the Bonded Pool + InflationLastTime int64 // block which the last inflation was processed // TODO make time + Inflation sdk.Rat // current annual inflation rate -``` go -type GlobalState struct { - TotalSupply int64 // total supply of Atoms - BondedPool int64 // reserve of bonded tokens - BondedShares rational.Rat // sum of all shares distributed for the BondedPool - UnbondedPool int64 // reserve of unbonding tokens held with validators - UnbondedShares rational.Rat // sum of all shares distributed for the UnbondedPool - InflationLastTime int64 // timestamp of last processing of inflation - Inflation rational.Rat // current annual inflation rate - DateLastCommissionReset int64 // unix timestamp for last commission accounting reset - FeePool coin.Coins // fee pool for all the fee shares which have already been distributed - ReservePool coin.Coins // pool of reserve taxes collected on all fees for governance use - Adjustment rational.Rat // Adjustment factor for calculating global fee accum + DateLastCommissionReset int64 // unix timestamp for last commission accounting reset (daily) } +type PoolShares struct { + Status sdk.BondStatus // either: unbonded, unbonding, or bonded + Amount sdk.Rat // total shares of type ShareKind +} +``` + +### Params + +Params is global data structure that stores system parameters and defines +overall functioning of the stake module. + +```golang type Params struct { - HoldBonded Address // account where all bonded coins are held - HoldUnbonding Address // account where all delegated but unbonding coins are held + InflationRateChange sdk.Rat // maximum annual change in inflation rate + InflationMax sdk.Rat // maximum inflation rate + InflationMin sdk.Rat // minimum inflation rate + GoalBonded sdk.Rat // Goal of percent bonded atoms - InflationRateChange rational.Rational // maximum annual change in inflation rate - InflationMax rational.Rational // maximum inflation rate - InflationMin rational.Rational // minimum inflation rate - GoalBonded rational.Rational // Goal of percent bonded atoms - ReserveTax rational.Rational // Tax collected on all fees - - MaxVals uint16 // maximum number of validators - AllowedBondDenom string // bondable coin denomination - - // gas costs for txs - GasDeclareCandidacy int64 - GasEditCandidacy int64 - GasDelegate int64 - GasRedelegate int64 - GasUnbond int64 + MaxValidators uint16 // maximum number of validators + BondDenom string // bondable coin denomination } ``` ### Validator -The `Validator` holds the current state and some historical -actions of validators. - -``` go -type ValidatorStatus byte - -const ( - Bonded ValidatorStatus = 0x01 - Unbonded ValidatorStatus = 0x02 - Revoked ValidatorStatus = 0x03 -) +The `Validator` holds the current state and some historical actions of the +validator. +```golang type Validator struct { - Status ValidatorStatus - ConsensusPubKey crypto.PubKey - GovernancePubKey crypto.PubKey - Owner crypto.Address - GlobalStakeShares rational.Rat - IssuedDelegatorShares rational.Rat - RedelegatingShares rational.Rat - VotingPower rational.Rat - Commission rational.Rat - CommissionMax rational.Rat - CommissionChangeRate rational.Rat - CommissionChangeToday rational.Rat - ProposerRewardPool coin.Coins - Adjustment rational.Rat - Description Description + Owner sdk.Address // sender of BondTx - UnbondTx returns here + ConsensusPubKey crypto.PubKey // Tendermint consensus pubkey of validator + Revoked bool // has the validator been revoked? + + PoolShares PoolShares // total shares for tokens held in the pool + DelegatorShares sdk.Rat // total shares issued to a validator's delegators + + Description Description // description terms for the validator + BondHeight int64 // earliest height as a bonded validator + BondIntraTxCounter int16 // block-local tx index of validator change + ProposerRewardPool sdk.Coins // reward pool collected from being the proposer + + Commission sdk.Rat // the commission rate of fees charged to any delegators + CommissionMax sdk.Rat // maximum commission rate which this validator can ever charge + CommissionChangeRate sdk.Rat // maximum daily increase of the validator commission + CommissionChangeToday sdk.Rat // commission rate change today, reset each day (UTC time) + + PrevPoolShares PoolShares // total shares of a global hold pools } type Description struct { - Name string - DateBonded string - Identity string - Website string - Details string + Moniker string // name + Identity string // optional identity signature (ex. UPort or Keybase) + Website string // optional website link + Details string // optional details } ``` -Validator parameters are described: -* Status: it can be Bonded (active validator), Unbonding (validator) - or Revoked -* ConsensusPubKey: validator public key that is used strictly for participating in - consensus -* GovernancePubKey: public key used by the validator for governance voting -* Owner: Address that is allowed to unbond coins. -* GlobalStakeShares: Represents shares of `GlobalState.BondedPool` if - `Validator.Status` is `Bonded`; or shares of `GlobalState.Unbondingt Pool` - otherwise -* IssuedDelegatorShares: Sum of all shares a validator issued to delegators - (which includes the validator's self-bond); a delegator share represents - their stake in the Validator's `GlobalStakeShares` * RedelegatingShares: The portion of `IssuedDelegatorShares` which are currently re-delegating to a new validator -* VotingPower: Proportional to the amount of bonded tokens which the validator - has if `Validator.Status` is `Bonded`; otherwise it is equal to `0` -* Commission: The commission rate of fees charged to any delegators -* CommissionMax: The maximum commission rate this validator can charge each - day from the date `GlobalState.DateLastCommissionReset` -* CommissionChangeRate: The maximum daily increase of the validator commission -* CommissionChangeToday: Counter for the amount of change to commission rate - which has occurred today, reset on the first block of each day (UTC time) -* ProposerRewardPool: reward pool for extra fees collected when this validator - is the proposer of a block -* Adjustment factor used to passively calculate each validators entitled fees - from `GlobalState.FeePool` -* Description - * Name: moniker - * DateBonded: date determined which the validator was bonded - * Identity: optional field to provide a signature which verifies the - validators identity (ex. UPort or Keybase) - * Website: optional website link - * Details: optional details -### DelegatorBond +### Delegation Atom holders may delegate coins to validators; under this circumstance their -funds are held in a `DelegatorBond` data structure. It is owned by one +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. -``` go -type DelegatorBond struct { - Validator crypto.PubKey - Shares rational.Rat - AdjustmentFeePool coin.Coins - AdjustmentRewardPool coin.Coins -} -``` - -Description: -* Validator: the public key of the validator: bonding too -* Shares: the number of delegator shares received from the validator -* AdjustmentFeePool: Adjustment factor used to passively calculate each bonds - entitled fees from `GlobalState.FeePool` -* AdjustmentRewardPool: Adjustment factor used to passively calculate each - bonds entitled fees from `Validator.ProposerRewardPool` - - -### QueueElem - -The Unbonding and re-delegation process is implemented using the ordered queue -data structure. All queue elements share a common structure: - ```golang -type QueueElem struct { - Validator crypto.PubKey - InitTime int64 // when the element was added to the queue +type Delegation struct { + DelegatorAddr sdk.Address // delegation owner address + ValidatorAddr sdk.Address // validator owner address + Shares sdk.Rat // delegation shares recieved + Height int64 // last height bond updated } ``` -The queue is ordered so the next element to unbond/re-delegate is at the head. -Every tick the head of the queue is checked and if the unbonding period has -passed since `InitTime`, the final settlement of the unbonding is started or -re-delegation is executed, and the element is popped from the queue. Each -`QueueElem` is persisted in the store until it is popped from the queue. +### UnbondingDelegation -### QueueElemUnbondDelegation - -QueueElemUnbondDelegation structure is used in the unbonding queue. +A UnbondingDelegation object is created every time an unbonding is initiated. +It must be completed with a second transaction provided by the delegation owner +after the unbonding period has passed. ```golang -type QueueElemUnbondDelegation struct { - QueueElem - Payout Address // account to pay out to - Tokens coin.Coins // the value in Atoms of the amount of delegator shares which are unbonding - StartSlashRatio rational.Rat // validator slash ratio +type UnbondingDelegation struct { + DelegationKey []byte // key of the delegation + InitTime int64 // unix time at unbonding initation + InitHeight int64 // block height at unbonding initation + ExpectedTokens sdk.Coins // the value in Atoms of the amount of shares which are unbonding + StartSlashRatio sdk.Rat // validator slash ratio at unbonding initiation } ``` -### QueueElemReDelegate +### Redelegation -QueueElemReDelegate structure is used in the re-delegation queue. +A redelegation object is created every time a redelegation occurs. It must be +completed with a second transaction provided by the delegation owner after the +unbonding period has passed. The destination delegation of a redelegation may +not itself undergo a new redelegation until the original redelegation has been +completed. + + - index: delegation address + - index 2: source validator owner address + - index 3: destination validator owner address ```golang -type QueueElemReDelegate struct { - QueueElem - Payout Address // account to pay out to - Shares rational.Rat // amount of shares which are unbonding - NewValidator crypto.PubKey // validator to bond to after unbond +type Redelegation struct { + SourceDelegation []byte // source delegation key + DestinationDelegation []byte // destination delegation key + InitTime int64 // unix time at redelegation + InitHeight int64 // block height at redelegation + Shares sdk.Rat // amount of shares redelegating } ``` - diff --git a/x/stake/delegation.go b/x/stake/delegation.go index dedac03e5..ae08e0867 100644 --- a/x/stake/delegation.go +++ b/x/stake/delegation.go @@ -10,7 +10,6 @@ import ( // 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 // pubKey. -// TODO better way of managing space type Delegation struct { DelegatorAddr sdk.Address `json:"delegator_addr"` ValidatorAddr sdk.Address `json:"validator_addr"` diff --git a/x/stake/shares.go b/x/stake/shares.go index d5fe93844..48781e2c6 100644 --- a/x/stake/shares.go +++ b/x/stake/shares.go @@ -4,9 +4,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// kind of shares -type PoolShareKind byte - // pool shares held by a validator type PoolShares struct { Status sdk.BondStatus `json:"status"` diff --git a/x/stake/validator.go b/x/stake/validator.go index a0b484d71..97e1a827e 100644 --- a/x/stake/validator.go +++ b/x/stake/validator.go @@ -82,10 +82,10 @@ func (v Validator) equal(c2 Validator) bool { // Description - description fields for a validator type Description struct { - Moniker string `json:"moniker"` - Identity string `json:"identity"` - Website string `json:"website"` - Details string `json:"details"` + Moniker string `json:"moniker"` // name + Identity string `json:"identity"` // optional identity signature (ex. UPort or Keybase) + Website string `json:"website"` // optional website link + Details string `json:"details"` // optional details } func NewDescription(moniker, identity, website, details string) Description { From b8cf5f347ea55820b42c4a48d98e071ab2bbc38c Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Fri, 25 May 2018 18:52:34 -0400 Subject: [PATCH 05/27] transaction stake updates --- docs/spec/slashing/valset-changes.md | 88 ++++++++ docs/spec/staking/state.md | 41 ++-- docs/spec/staking/transactions.md | 323 ++++++++++----------------- docs/spec/staking/valset-changes.md | 99 +------- 4 files changed, 232 insertions(+), 319 deletions(-) create mode 100644 docs/spec/slashing/valset-changes.md diff --git a/docs/spec/slashing/valset-changes.md b/docs/spec/slashing/valset-changes.md new file mode 100644 index 000000000..c403e5d4c --- /dev/null +++ b/docs/spec/slashing/valset-changes.md @@ -0,0 +1,88 @@ +# Validator Set Changes + +## Slashing + +Messges which may compromise the safety of the underlying consensus protocol ("equivocations") +result in some amount of the offending validator's shares being removed ("slashed"). + +Currently, such messages include only the following: + +- prevotes by the same validator for more than one BlockID at the same + Height and Round +- precommits by the same validator for more than one BlockID at the same + Height and Round + +We call any such pair of conflicting votes `Evidence`. Full nodes in the network prioritize the +detection and gossipping of `Evidence` so that it may be rapidly included in blocks and the offending +validators punished. + +For some `evidence` to be valid, it must satisfy: + +`evidence.Timestamp >= block.Timestamp - MAX_EVIDENCE_AGE` + +where `evidence.Timestamp` is the timestamp in the block at height +`evidence.Height` and `block.Timestamp` is the current block timestamp. + +If valid evidence is included in a block, the offending validator loses +a constant `SLASH_PROPORTION` of their current stake at the beginning of the block: + +``` +oldShares = validator.shares +validator.shares = oldShares * (1 - SLASH_PROPORTION) +``` + +This ensures that offending validators are punished the same amount whether they +act as a single validator with X stake or as N validators with collectively X +stake. + + +## Automatic Unbonding + +Every block includes a set of precommits by the validators for the previous block, +known as the LastCommit. A LastCommit is valid so long as it contains precommits from +2/3 of voting power. + +Proposers are incentivized to include precommits from all +validators in the LastCommit by receiving additional fees +proportional to the difference between the voting power included in the +LastCommit and +2/3 (see [TODO](https://github.com/cosmos/cosmos-sdk/issues/967)). + +Validators are penalized for failing to be included in the LastCommit for some +number of blocks by being automatically unbonded. + +The following information is stored with each validator, and is only non-zero if the validator becomes an active validator: + +```go +type ValidatorSigningInfo struct { + StartHeight int64 + SignedBlocksBitArray BitArray +} +``` + +Where: +* `StartHeight` is set to the height that the validator became an active validator (with non-zero voting power). +* `SignedBlocksBitArray` is a bit-array of size `SIGNED_BLOCKS_WINDOW` that records, for each of the last `SIGNED_BLOCKS_WINDOW` blocks, +whether or not this validator was included in the LastCommit. It uses a `0` if the validator was included, and a `1` if it was not. +Note it is initialized with all 0s. + +At the beginning of each block, we update the signing info for each validator and check if they should be automatically unbonded: + +``` +h = block.Height +index = h % SIGNED_BLOCKS_WINDOW + +for val in block.Validators: + signInfo = val.SignInfo + if val in block.LastCommit: + signInfo.SignedBlocksBitArray.Set(index, 0) + else + signInfo.SignedBlocksBitArray.Set(index, 1) + + // validator must be active for at least SIGNED_BLOCKS_WINDOW + // before they can be automatically unbonded for failing to be + // included in 50% of the recent LastCommits + minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW + minSigned = SIGNED_BLOCKS_WINDOW / 2 + blocksSigned = signInfo.SignedBlocksBitArray.Sum() + if h > minHeight AND blocksSigned < minSigned: + unbond the validator +``` diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index c5601d044..fa235479f 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -1,16 +1,7 @@ - ## State -The staking module persists the following information to the store: -* `Params`, a struct describing the global pools, inflation, and fees -* `Pool`, a struct describing the global pools, inflation, and fees -* `ValidatorValidators: => `, a map of all validators (including current validators) in the store, -indexed by their public key and shares in the global pool. -* `DelegatorBonds: < delegator-address | validator-pubkey > => `. a map of all delegations by a delegator to a validator, -indexed by delegator address and validator pubkey. - public key - ### Pool + - index: n/a single-record The pool is a space for all dynamic global state of the Cosmos Hub. It tracks information about the total amounts of Atoms in all states, representative @@ -39,6 +30,7 @@ type PoolShares struct { ``` ### Params + - index: n/a single-record Params is global data structure that stores system parameters and defines overall functioning of the stake module. @@ -56,6 +48,11 @@ type Params struct { ``` ### Validator + - index 1: validator owner address + - index 2: validator Tendermint PubKey + - index 3: bonded validators only + - index 4: voting power + - index 5: Tendermint updates The `Validator` holds the current state and some historical actions of the validator. @@ -90,10 +87,8 @@ type Description struct { } ``` -* RedelegatingShares: The portion of `IssuedDelegatorShares` which are - currently re-delegating to a new validator - ### Delegation + - index: delegation address Atom holders may delegate coins to validators; under this circumstance their funds are held in a `Delegation` data structure. It is owned by one @@ -110,10 +105,12 @@ type Delegation struct { ``` ### UnbondingDelegation + - index: delegation address A UnbondingDelegation object is created every time an unbonding is initiated. -It must be completed with a second transaction provided by the delegation owner +The unbond must be completed with a second transaction provided by the delegation owner after the unbonding period has passed. + ```golang type UnbondingDelegation struct { @@ -126,17 +123,17 @@ type UnbondingDelegation struct { ``` ### Redelegation - -A redelegation object is created every time a redelegation occurs. It must be -completed with a second transaction provided by the delegation owner after the -unbonding period has passed. The destination delegation of a redelegation may -not itself undergo a new redelegation until the original redelegation has been -completed. - - - index: delegation address + - index 1: delegation address - index 2: source validator owner address - index 3: destination validator owner address +A redelegation object is created every time a redelegation occurs. The +redelegation must be completed with a second transaction provided by the +delegation owner after the unbonding period has passed. The destination +delegation of a redelegation may not itself undergo a new redelegation until +the original redelegation has been completed. + + ```golang type Redelegation struct { SourceDelegation []byte // source delegation key diff --git a/docs/spec/staking/transactions.md b/docs/spec/staking/transactions.md index c80ddc7a9..685883dd1 100644 --- a/docs/spec/staking/transactions.md +++ b/docs/spec/staking/transactions.md @@ -1,67 +1,58 @@ ### Transaction Overview +In this section we describe the processing of the transactions and the +corresponding updates to the state. + Available Transactions: -* TxDeclareCandidacy -* TxEditCandidacy -* TxDelegate -* TxUnbond -* TxRedelegate -* TxProveLive + - TxCreateValidator + - TxEditValidator + - TxDelegation + - TxRedelegation + - TxUnbond -## Transaction processing - -In this section we describe the processing of the transactions and the -corresponding updates to the global state. In the following text we will use -`gs` to refer to the `GlobalState` data structure, `unbondDelegationQueue` is a -reference to the queue of unbond delegations, `reDelegationQueue` is the -reference for the queue of redelegations. We use `tx` to denote a -reference to a transaction that is being processed, and `sender` to denote the -address of the sender of the transaction. We use function -`loadValidator(store, PubKey)` to obtain a Validator structure from the store, -and `saveValidator(store, validator)` to save it. Similarly, we use -`loadDelegatorBond(store, sender, PubKey)` to load a delegator bond with the -key (sender and PubKey) from the store, and -`saveDelegatorBond(store, sender, bond)` to save it. -`removeDelegatorBond(store, sender, bond)` is used to remove the bond from the -store. +Other notes: + - `tx` denotes a reference to the transaction being processed + - `sender` denotes the address of the sender of the transaction + - `getXxx`, `setXxx`, and `removeXxx` functions are used to retrieve and + modify objects from the store + - `sdk.Rat` refers to a rational numeric type specified by the sdk. -### TxDeclareCandidacy +### TxCreateValidator -A validator candidacy is declared using the `TxDeclareCandidacy` transaction. +A validator is created using the `TxCreateValidator` transaction. ```golang -type TxDeclareCandidacy struct { +type TxCreateValidator struct { + OwnerAddr sdk.Address ConsensusPubKey crypto.PubKey - Amount coin.Coin GovernancePubKey crypto.PubKey - Commission rational.Rat - CommissionMax int64 - CommissionMaxChange int64 + SelfDelegation coin.Coin + Description Description + Commission sdk.Rat + CommissionMax sdk.Rat + CommissionMaxChange sdk.Rat } + -declareCandidacy(tx TxDeclareCandidacy): - validator = loadValidator(store, tx.PubKey) - if validator != nil return // validator with that public key already exists +createValidator(tx TxCreateValidator): + validator = getValidator(tx.OwnerAddr) + if validator != nil return // only one validator per address - validator = NewValidator(tx.PubKey) - validator.Status = Unbonded - validator.Owner = sender - init validator VotingPower, GlobalStakeShares, IssuedDelegatorShares, RedelegatingShares and Adjustment to rational.Zero - init commision related fields based on the values from tx - validator.ProposerRewardPool = Coin(0) - validator.Description = tx.Description + validator = NewValidator(OwnerAddr, ConsensusPubKey, GovernancePubKey, Description) + init validator poolShares, delegatorShares set to 0 //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + init validator commision fields from tx + validator.PoolShares = 0 - saveValidator(store, validator) + setValidator(validator) - txDelegate = TxDelegate(tx.PubKey, tx.Amount) - return delegateWithValidator(txDelegate, validator) - -// see delegateWithValidator function in [TxDelegate](TxDelegate) + txDelegate = TxDelegate(tx.OwnerAddr, tx.OwnerAddr, tx.SelfDelegation) + delegate(txDelegate, validator) // see delegate function in [TxDelegate](TxDelegate) + return ``` -### TxEditCandidacy +### TxEditValidator If either the `Description` (excluding `DateBonded` which is constant), `Commission`, or the `GovernancePubKey` need to be updated, the @@ -70,87 +61,51 @@ If either the `Description` (excluding `DateBonded` which is constant), ```golang type TxEditCandidacy struct { GovernancePubKey crypto.PubKey - Commission int64 + Commission sdk.Rat Description Description } editCandidacy(tx TxEditCandidacy): - validator = loadValidator(store, tx.PubKey) - if validator == nil or validator.Status == Revoked return + validator = getValidator(tx.ValidatorAddr) + if tx.Commission > CommissionMax || tx.Commission < 0 return halt tx + if rateChange(tx.Commission) > CommissionMaxChange return halt tx + validator.Commission = tx.Commission + if tx.GovernancePubKey != nil validator.GovernancePubKey = tx.GovernancePubKey - if tx.Commission >= 0 validator.Commission = tx.Commission if tx.Description != nil validator.Description = tx.Description - saveValidator(store, validator) + setValidator(store, validator) return ``` -### TxDelegate +### TxDelegation -Delegator bonds are created using the `TxDelegate` transaction. Within this -transaction the delegator provides an amount of coins, and in return receives -some amount of validator's delegator shares that are assigned to -`DelegatorBond.Shares`. +Within this transaction the delegator provides coins, and in return receives +some amount of their validator's delegator-shares that are assigned to +`Delegation.Shares`. ```golang type TxDelegate struct { - PubKey crypto.PubKey - Amount coin.Coin + DelegatorAddr sdk.Address + ValidatorAddr sdk.Address + Amount sdk.Coin } delegate(tx TxDelegate): - validator = loadValidator(store, tx.PubKey) - if validator == nil return - return delegateWithValidator(tx, validator) - -delegateWithValidator(tx TxDelegate, validator Validator): + pool = getPool() if validator.Status == Revoked return - if validator.Status == Bonded - poolAccount = params.HoldBonded - else - poolAccount = params.HoldUnbonded + delegation = getDelegatorBond(DelegatorAddr, ValidatorAddr) + if delegation == nil then delegation = NewDelegation(DelegatorAddr, ValidatorAddr) - err = transfer(sender, poolAccount, tx.Amount) - if err != nil return - - bond = loadDelegatorBond(store, sender, tx.PubKey) - if bond == nil then bond = DelegatorBond(tx.PubKey, rational.Zero, Coin(0), Coin(0)) - - issuedDelegatorShares = addTokens(tx.Amount, validator) - bond.Shares += issuedDelegatorShares - - saveValidator(store, validator) - saveDelegatorBond(store, sender, bond) - saveGlobalState(store, gs) - return - -addTokens(amount coin.Coin, validator Validator): - if validator.Status == Bonded - gs.BondedPool += amount - issuedShares = amount / exchangeRate(gs.BondedShares, gs.BondedPool) - gs.BondedShares += issuedShares - else - gs.UnbondedPool += amount - issuedShares = amount / exchangeRate(gs.UnbondedShares, gs.UnbondedPool) - gs.UnbondedShares += issuedShares - - validator.GlobalStakeShares += issuedShares + validator, pool, issuedDelegatorShares = validator.addTokensFromDel(tx.Amount, pool) + delegation.Shares += issuedDelegatorShares - if validator.IssuedDelegatorShares.IsZero() - exRate = rational.One - else - exRate = validator.GlobalStakeShares / validator.IssuedDelegatorShares - - issuedDelegatorShares = issuedShares / exRate - validator.IssuedDelegatorShares += issuedDelegatorShares - return issuedDelegatorShares - -exchangeRate(shares rational.Rat, tokenAmount int64): - if shares.IsZero() then return rational.One - return tokenAmount / shares - + setDelegation(delegation) + updateValidator(validator) + setPool(pool) + return ``` ### TxUnbond @@ -159,125 +114,89 @@ Delegator unbonding is defined with the following transaction: ```golang type TxUnbond struct { - PubKey crypto.PubKey - Shares rational.Rat + DelegatorAddr sdk.Address + ValidatorAddr sdk.Address + Shares string } unbond(tx TxUnbond): - bond = loadDelegatorBond(store, sender, tx.PubKey) - if bond == nil return - if bond.Shares < tx.Shares return - - bond.Shares -= tx.Shares - - validator = loadValidator(store, tx.PubKey) - - revokeCandidacy = false - if bond.Shares.IsZero() - if sender == validator.Owner and validator.Status != Revoked then revokeCandidacy = true then removeDelegatorBond(store, sender, bond) - else - saveDelegatorBond(store, sender, bond) - - if validator.Status == Bonded - poolAccount = params.HoldBonded - else - poolAccount = params.HoldUnbonded - - returnedCoins = removeShares(validator, shares) - - unbondDelegationElem = QueueElemUnbondDelegation(tx.PubKey, currentHeight(), sender, returnedCoins, startSlashRatio) - unbondDelegationQueue.add(unbondDelegationElem) - - transfer(poolAccount, unbondingPoolAddress, returnCoins) + delegation, found = getDelegatorBond(store, sender, tx.PubKey) + if !found == nil return - if revokeCandidacy - if validator.Status == Bonded then bondedToUnbondedPool(validator) - validator.Status = Revoked - - if validator.IssuedDelegatorShares.IsZero() - removeValidator(store, tx.PubKey) + if msg.Shares == "MAX" { + if !bond.Shares.GT(sdk.ZeroRat()) { + return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result() else - saveValidator(store, validator) + var err sdk.Error + delShares, err = sdk.NewRatFromDecimal(msg.Shares) + if err != nil + return err + if bond.Shares.LT(delShares) + return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result() - saveGlobalState(store, gs) - return + validator, found := k.GetValidator(ctx, msg.ValidatorAddr) + if !found { + return err -removeShares(validator Validator, shares rational.Rat): - globalPoolSharesToRemove = delegatorShareExRate(validator) * shares + if msg.Shares == "MAX" + delShares = bond.Shares - if validator.Status == Bonded - gs.BondedShares -= globalPoolSharesToRemove - removedTokens = exchangeRate(gs.BondedShares, gs.BondedPool) * globalPoolSharesToRemove - gs.BondedPool -= removedTokens - else - gs.UnbondedShares -= globalPoolSharesToRemove - removedTokens = exchangeRate(gs.UnbondedShares, gs.UnbondedPool) * globalPoolSharesToRemove - gs.UnbondedPool -= removedTokens - - validator.GlobalStakeShares -= removedTokens - validator.IssuedDelegatorShares -= shares - return returnedCoins - -delegatorShareExRate(validator Validator): - if validator.IssuedDelegatorShares.IsZero() then return rational.One - return validator.GlobalStakeShares / validator.IssuedDelegatorShares - -bondedToUnbondedPool(validator Validator): - removedTokens = exchangeRate(gs.BondedShares, gs.BondedPool) * validator.GlobalStakeShares - gs.BondedShares -= validator.GlobalStakeShares - gs.BondedPool -= removedTokens - - gs.UnbondedPool += removedTokens - issuedShares = removedTokens / exchangeRate(gs.UnbondedShares, gs.UnbondedPool) - gs.UnbondedShares += issuedShares + bond.Shares -= delShares - validator.GlobalStakeShares = issuedShares - validator.Status = Unbonded + unbondingDelegation = NewUnbondingDelegation(sender, delShares, currentHeight/Time, startSlashRatio) + setUnbondingDelegation(unbondingDelegation) - return transfer(address of the bonded pool, address of the unbonded pool, removedTokens) + revokeCandidacy := false + if bond.Shares.IsZero() { + + if bond.DelegatorAddr == validator.Owner && validator.Revoked == false + revokeCandidacy = true + + k.removeDelegation(ctx, bond) + else + bond.Height = currentBlockHeight + setDelegation(bond) + + pool := k.GetPool(ctx) + validator, pool, returnAmount := validator.removeDelShares(pool, delShares) + k.setPool(ctx, pool) + AddCoins(ctx, bond.DelegatorAddr, returnAmount) + + if revokeCandidacy + validator.Revoked = true + + validator = updateValidator(ctx, validator) + + if validator.DelegatorShares == 0 { + removeValidator(ctx, validator.Owner) + + return ``` -### TxRedelegate +### TxRedelegation -The re-delegation command allows delegators to switch validators while still -receiving equal reward to as if they had never unbonded. +The redelegation command allows delegators to instantly switch validators. ```golang type TxRedelegate struct { - PubKeyFrom crypto.PubKey - PubKeyTo crypto.PubKey - Shares rational.Rat + DelegatorAddr Address + ValidatorFrom Validator + ValidatorTo Validator + Shares sdk.Rat } redelegate(tx TxRedelegate): - bond = loadDelegatorBond(store, sender, tx.PubKey) - if bond == nil then return + pool = getPool() + delegation = getDelegatorBond(tx.DelegatorAddr, tx.ValidatorFrom.Owner) + if delegation == nil then return - if bond.Shares < tx.Shares return - validator = loadValidator(store, tx.PubKeyFrom) - if validator == nil return + if delegation.Shares < tx.Shares return + delegation.shares -= Tx.Shares + validator, pool, createdCoins = validator.RemoveShares(pool, tx.Shares) + setPool(pool) - validator.RedelegatingShares += tx.Shares - reDelegationElem = QueueElemReDelegate(tx.PubKeyFrom, currentHeight(), sender, tx.Shares, tx.PubKeyTo) - redelegationQueue.add(reDelegationElem) + redelegation = newRedelegation(validatorFrom, validatorTo, Shares, createdCoins) + setRedelegation(redelegation) return ``` -### TxProveLive - -If a validator was automatically unbonded due to liveness issues and wishes to -assert it is still online, it can send `TxProveLive`: - -```golang -type TxProveLive struct { - PubKey crypto.PubKey -} -``` - -All delegators in the temporary unbonding pool which have not -transacted to move will be bonded back to the now-live validator and begin to -once again collect provisions and rewards. - -``` -TODO: pseudo-code -``` diff --git a/docs/spec/staking/valset-changes.md b/docs/spec/staking/valset-changes.md index 0547b171f..92efa293b 100644 --- a/docs/spec/staking/valset-changes.md +++ b/docs/spec/staking/valset-changes.md @@ -1,13 +1,9 @@ # Validator Set Changes -The validator set may be updated by state transitions that run at the beginning and -end of every block. This can happen one of three ways: - -- voting power of a validator changes due to bonding and unbonding -- voting power of validator is "slashed" due to conflicting signed messages -- validator is automatically unbonded due to inactivity - -## Voting Power Changes +The Tendermint validator set may be updated by state transitions that run at +the beginning and end of every block. The Tendermint validator set may be +changed by validators either being revoked due to inactivity/unexpected +behaviour (covered in slashing) or changed in validator power. At the end of every block, we run the following: @@ -101,90 +97,3 @@ unbondedToBondedPool(validator Validator): return transfer(address of the unbonded pool, address of the bonded pool, removedTokens) ``` - -## Slashing - -Messges which may compromise the safety of the underlying consensus protocol ("equivocations") -result in some amount of the offending validator's shares being removed ("slashed"). - -Currently, such messages include only the following: - -- prevotes by the same validator for more than one BlockID at the same - Height and Round -- precommits by the same validator for more than one BlockID at the same - Height and Round - -We call any such pair of conflicting votes `Evidence`. Full nodes in the network prioritize the -detection and gossipping of `Evidence` so that it may be rapidly included in blocks and the offending -validators punished. - -For some `evidence` to be valid, it must satisfy: - -`evidence.Timestamp >= block.Timestamp - MAX_EVIDENCE_AGE` - -where `evidence.Timestamp` is the timestamp in the block at height -`evidence.Height` and `block.Timestamp` is the current block timestamp. - -If valid evidence is included in a block, the offending validator loses -a constant `SLASH_PROPORTION` of their current stake at the beginning of the block: - -``` -oldShares = validator.shares -validator.shares = oldShares * (1 - SLASH_PROPORTION) -``` - -This ensures that offending validators are punished the same amount whether they -act as a single validator with X stake or as N validators with collectively X -stake. - - -## Automatic Unbonding - -Every block includes a set of precommits by the validators for the previous block, -known as the LastCommit. A LastCommit is valid so long as it contains precommits from +2/3 of voting power. - -Proposers are incentivized to include precommits from all -validators in the LastCommit by receiving additional fees -proportional to the difference between the voting power included in the -LastCommit and +2/3 (see [TODO](https://github.com/cosmos/cosmos-sdk/issues/967)). - -Validators are penalized for failing to be included in the LastCommit for some -number of blocks by being automatically unbonded. - -The following information is stored with each validator, and is only non-zero if the validator becomes an active validator: - -```go -type ValidatorSigningInfo struct { - StartHeight int64 - SignedBlocksBitArray BitArray -} -``` - -Where: -* `StartHeight` is set to the height that the validator became an active validator (with non-zero voting power). -* `SignedBlocksBitArray` is a bit-array of size `SIGNED_BLOCKS_WINDOW` that records, for each of the last `SIGNED_BLOCKS_WINDOW` blocks, -whether or not this validator was included in the LastCommit. It uses a `0` if the validator was included, and a `1` if it was not. -Note it is initialized with all 0s. - -At the beginning of each block, we update the signing info for each validator and check if they should be automatically unbonded: - -``` -h = block.Height -index = h % SIGNED_BLOCKS_WINDOW - -for val in block.Validators: - signInfo = val.SignInfo - if val in block.LastCommit: - signInfo.SignedBlocksBitArray.Set(index, 0) - else - signInfo.SignedBlocksBitArray.Set(index, 1) - - // validator must be active for at least SIGNED_BLOCKS_WINDOW - // before they can be automatically unbonded for failing to be - // included in 50% of the recent LastCommits - minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW - minSigned = SIGNED_BLOCKS_WINDOW / 2 - blocksSigned = signInfo.SignedBlocksBitArray.Sum() - if h > minHeight AND blocksSigned < minSigned: - unbond the validator -``` From e39ba70c08d004ca3c2e68159a294a20d7f2bb6a Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 29 May 2018 14:50:35 -0400 Subject: [PATCH 06/27] complete staking spec update --- docs/spec/staking/end_block.md | 61 ++++++++++++++++++ docs/spec/staking/state.md | 4 +- docs/spec/staking/transactions.md | 97 +++++++++++++++++++++++++++- docs/spec/staking/valset-changes.md | 99 ----------------------------- x/stake/keeper.go | 14 ++-- 5 files changed, 164 insertions(+), 111 deletions(-) create mode 100644 docs/spec/staking/end_block.md delete mode 100644 docs/spec/staking/valset-changes.md diff --git a/docs/spec/staking/end_block.md b/docs/spec/staking/end_block.md new file mode 100644 index 000000000..e7c4e1e82 --- /dev/null +++ b/docs/spec/staking/end_block.md @@ -0,0 +1,61 @@ +# End-Block + +Two staking activities are intended to be processed in the application endblock. + - inform tendermint of validator set changes + - process and set atom inflation + +# Validator Set Changes + +The Tendermint validator set may be updated by state transitions that run at +the end of every block. The Tendermint validator set may be changed by +validators either being revoked due to inactivity/unexpected behaviour (covered +in slashing) or changed in validator power. Determining which validator set +changes must be made occures during staking transactions (and slashing +transactions) - during end-block the already accounted changes are applied and +the changes cleared + +```golang +EndBlock() ValidatorSetChanges + vsc = GetTendermintUpdates() + ClearTendermintUpdates() + return vsc +``` + +# Inflation + +The atom inflation rate is changed once per hour based on the current and +historic bond ratio + +```golang +processProvisions(): + hrsPerYr = 8766 // as defined by a julian year of 365.25 days + + time = BFTTime() + if time > pool.InflationLastTime + ProvisionTimeout + pool.InflationLastTime = time + pool.Inflation = nextInflation(hrsPerYr).Round(1000000000) + + provisions = pool.Inflation * (pool.TotalSupply / hrsPerYr) + + pool.LooseUnbondedTokens += provisions + feePool += LooseUnbondedTokens + + setPool(pool) + +nextInflation(hrsPerYr rational.Rat): + if pool.TotalSupply > 0 + bondedRatio = pool.BondedPool / pool.TotalSupply + else + bondedRation = 0 + + inflationRateChangePerYear = (1 - bondedRatio / params.GoalBonded) * params.InflationRateChange + inflationRateChange = inflationRateChangePerYear / hrsPerYr + + inflation = pool.Inflation + inflationRateChange + if inflation > params.InflationMax then inflation = params.InflationMax + + if inflation < params.InflationMin then inflation = params.InflationMin + + return inflation +``` + diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index fa235479f..72cda1bb4 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -52,7 +52,9 @@ type Params struct { - index 2: validator Tendermint PubKey - index 3: bonded validators only - index 4: voting power - - index 5: Tendermint updates + +Related Store which holds Validator.ABCIValidator() + - index: validator owner address The `Validator` holds the current state and some historical actions of the validator. diff --git a/docs/spec/staking/transactions.md b/docs/spec/staking/transactions.md index 685883dd1..b59077314 100644 --- a/docs/spec/staking/transactions.md +++ b/docs/spec/staking/transactions.md @@ -2,15 +2,16 @@ ### Transaction Overview In this section we describe the processing of the transactions and the -corresponding updates to the state. - -Available Transactions: +corresponding updates to the state. Transactions: - TxCreateValidator - TxEditValidator - TxDelegation - TxRedelegation - TxUnbond +Other important state changes: + - Update Validators + Other notes: - `tx` denotes a reference to the transaction being processed - `sender` denotes the address of the sender of the transaction @@ -200,3 +201,93 @@ redelegate(tx TxRedelegate): return ``` +### Update Validators + +Within many transactions the validator set must be updated based on changes in +power to a single validator. This process also updates the Tendermint-Updates +store for use in end-block when validators are either added or kicked from the +Tendermint. + +```golang +updateBondedValidators(newValidator Validator) (updatedVal Validator) + + kickCliffValidator := false + oldCliffValidatorAddr := getCliffValidator(ctx) + + // add the actual validator power sorted store + maxValidators := GetParams(ctx).MaxValidators + iterator := ReverseSubspaceIterator(ValidatorsByPowerKey) // largest to smallest + bondedValidatorsCount := 0 + var validator Validator + for { + if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) { + + if bondedValidatorsCount == int(maxValidators) { // is cliff validator + setCliffValidator(ctx, validator, GetPool(ctx)) + iterator.Close() + break + + // either retrieve the original validator from the store, + // or under the situation that this is the "new validator" just + // use the validator provided because it has not yet been updated + // in the main validator store + + ownerAddr := iterator.Value() + if bytes.Equal(ownerAddr, newValidator.Owner) { + validator = newValidator + else + validator = getValidator(ownerAddr) + + // if not previously a validator (and unrevoked), + // kick the cliff validator / bond this new validator + if validator.Status() != sdk.Bonded && !validator.Revoked { + kickCliffValidator = true + + validator = bondValidator(ctx, store, validator) + if bytes.Equal(ownerAddr, newValidator.Owner) { + updatedVal = validator + + bondedValidatorsCount++ + iterator.Next() + + // perform the actual kicks + if oldCliffValidatorAddr != nil && kickCliffValidator { + validator := getValidator(store, oldCliffValidatorAddr) + unbondValidator(ctx, store, validator) + return + +// perform all the store operations for when a validator status becomes unbonded +unbondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) + pool := GetPool(ctx) + + // set the status + validator, pool = validator.UpdateStatus(pool, sdk.Unbonded) + setPool(ctx, pool) + + // save the now unbonded validator record + setValidator(validator) + + // add to accumulated changes for tendermint + setTendermintUpdates(validator.abciValidatorZero) + + // also remove from the bonded validators index + removeValidatorsBonded(validator) +} + +// perform all the store operations for when a validator status becomes bonded +bondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) Validator + pool := GetPool(ctx) + + // set the status + validator, pool = validator.UpdateStatus(pool, sdk.Bonded) + setPool(ctx, pool) + + // save the now bonded validator record to the three referenced stores + setValidator(validator) + setValidatorsBonded(validator) + + // add to accumulated changes for tendermint + setTendermintUpdates(validator.abciValidator) + + return validator +``` diff --git a/docs/spec/staking/valset-changes.md b/docs/spec/staking/valset-changes.md deleted file mode 100644 index 92efa293b..000000000 --- a/docs/spec/staking/valset-changes.md +++ /dev/null @@ -1,99 +0,0 @@ -# Validator Set Changes - -The Tendermint validator set may be updated by state transitions that run at -the beginning and end of every block. The Tendermint validator set may be -changed by validators either being revoked due to inactivity/unexpected -behaviour (covered in slashing) or changed in validator power. - -At the end of every block, we run the following: - -(TODO remove inflation from here) - -```golang -tick(ctx Context): - hrsPerYr = 8766 // as defined by a julian year of 365.25 days - - time = ctx.Time() - if time > gs.InflationLastTime + ProvisionTimeout - gs.InflationLastTime = time - gs.Inflation = nextInflation(hrsPerYr).Round(1000000000) - - provisions = gs.Inflation * (gs.TotalSupply / hrsPerYr) - - gs.BondedPool += provisions - gs.TotalSupply += provisions - - saveGlobalState(store, gs) - - if time > unbondDelegationQueue.head().InitTime + UnbondingPeriod - for each element elem in the unbondDelegationQueue where time > elem.InitTime + UnbondingPeriod do - transfer(unbondingQueueAddress, elem.Payout, elem.Tokens) - unbondDelegationQueue.remove(elem) - - if time > reDelegationQueue.head().InitTime + UnbondingPeriod - for each element elem in the unbondDelegationQueue where time > elem.InitTime + UnbondingPeriod do - validator = getValidator(store, elem.PubKey) - returnedCoins = removeShares(validator, elem.Shares) - validator.RedelegatingShares -= elem.Shares - delegateWithValidator(TxDelegate(elem.NewValidator, returnedCoins), validator) - reDelegationQueue.remove(elem) - - return UpdateValidatorSet() - -nextInflation(hrsPerYr rational.Rat): - if gs.TotalSupply > 0 - bondedRatio = gs.BondedPool / gs.TotalSupply - else - bondedRation = 0 - - inflationRateChangePerYear = (1 - bondedRatio / params.GoalBonded) * params.InflationRateChange - inflationRateChange = inflationRateChangePerYear / hrsPerYr - - inflation = gs.Inflation + inflationRateChange - if inflation > params.InflationMax then inflation = params.InflationMax - - if inflation < params.InflationMin then inflation = params.InflationMin - - return inflation - -UpdateValidatorSet(): - validators = loadValidators(store) - - v1 = validators.Validators() - v2 = updateVotingPower(validators).Validators() - - change = v1.validatorsUpdated(v2) // determine all updated validators between two validator sets - return change - -updateVotingPower(validators Validators): - foreach validator in validators do - validator.VotingPower = (validator.IssuedDelegatorShares - validator.RedelegatingShares) * delegatorShareExRate(validator) - - validators.Sort() - - foreach validator in validators do - if validator is not in the first params.MaxVals - validator.VotingPower = rational.Zero - if validator.Status == Bonded then bondedToUnbondedPool(validator Validator) - - else if validator.Status == UnBonded then unbondedToBondedPool(validator) - - saveValidator(store, c) - - return validators - -unbondedToBondedPool(validator Validator): - removedTokens = exchangeRate(gs.UnbondedShares, gs.UnbondedPool) * validator.GlobalStakeShares - gs.UnbondedShares -= validator.GlobalStakeShares - gs.UnbondedPool -= removedTokens - - gs.BondedPool += removedTokens - issuedShares = removedTokens / exchangeRate(gs.BondedShares, gs.BondedPool) - gs.BondedShares += issuedShares - - validator.GlobalStakeShares = issuedShares - validator.Status = Bonded - - return transfer(address of the unbonded pool, address of the bonded pool, removedTokens) -``` - diff --git a/x/stake/keeper.go b/x/stake/keeper.go index 9f554c764..dfa2709d1 100644 --- a/x/stake/keeper.go +++ b/x/stake/keeper.go @@ -282,16 +282,14 @@ func (k Keeper) updateValidator(ctx sdk.Context, validator Validator) Validator return validator } -// XXX TODO build in consideration for revoked -// // Update the validator group and kick out any old validators. In addition this // function adds (or doesn't add) a validator which has updated its bonded // tokens to the validator group. -> this validator is specified through the // updatedValidatorAddr term. // // The correct subset is retrieved by iterating through an index of the -// validators sorted by power, stored using the ValidatorsByPowerKey. Simultaniously -// the current validator records are updated in store with the +// validators sorted by power, stored using the ValidatorsByPowerKey. +// Simultaneously the current validator records are updated in store with the // ValidatorsBondedKey. This store is used to determine if a validator is a // validator without needing to iterate over the subspace as we do in // GetValidators. @@ -319,10 +317,10 @@ func (k Keeper) updateBondedValidators(ctx sdk.Context, store sdk.KVStore, break } - // either retrieve the original validator from the store, - // or under the situation that this is the "new validator" just - // use the validator provided because it has not yet been updated - // in the main validator store + // either retrieve the original validator from the store, or under the + // situation that this is the "new validator" just use the validator + // provided because it has not yet been updated in the main validator + // store ownerAddr := iterator.Value() if bytes.Equal(ownerAddr, newValidator.Owner) { validator = newValidator From 25ae0269586e20b15c3789b666bd8656c1ea22f0 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 29 May 2018 16:03:13 -0400 Subject: [PATCH 07/27] spec complete unbonding, redelegation txs --- docs/spec/staking/state.md | 93 +++++++++++++++--------------- docs/spec/staking/transactions.md | 96 +++++++++++++++++++++++-------- x/stake/delegation.go | 1 - 3 files changed, 120 insertions(+), 70 deletions(-) diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 72cda1bb4..82a22dc65 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -10,22 +10,22 @@ information, etc. ```golang type Pool struct { - LooseUnbondedTokens int64 // tokens not associated with any validator - UnbondedTokens int64 // reserve of unbonded tokens held with validators - UnbondingTokens int64 // tokens moving from bonded to unbonded pool - BondedTokens int64 // reserve of bonded tokens - UnbondedShares sdk.Rat // sum of all shares distributed for the Unbonded Pool - UnbondingShares sdk.Rat // shares moving from Bonded to Unbonded Pool - BondedShares sdk.Rat // sum of all shares distributed for the Bonded Pool - InflationLastTime int64 // block which the last inflation was processed // TODO make time - Inflation sdk.Rat // current annual inflation rate + LooseUnbondedTokens int64 // tokens not associated with any validator + UnbondedTokens int64 // reserve of unbonded tokens held with validators + UnbondingTokens int64 // tokens moving from bonded to unbonded pool + BondedTokens int64 // reserve of bonded tokens + UnbondedShares sdk.Rat // sum of all shares distributed for the Unbonded Pool + UnbondingShares sdk.Rat // shares moving from Bonded to Unbonded Pool + BondedShares sdk.Rat // sum of all shares distributed for the Bonded Pool + InflationLastTime int64 // block which the last inflation was processed // TODO make time + Inflation sdk.Rat // current annual inflation rate DateLastCommissionReset int64 // unix timestamp for last commission accounting reset (daily) } type PoolShares struct { - Status sdk.BondStatus // either: unbonded, unbonding, or bonded - Amount sdk.Rat // total shares of type ShareKind + Status sdk.BondStatus // either: unbonded, unbonding, or bonded + Amount sdk.Rat // total shares of type ShareKind } ``` @@ -37,13 +37,13 @@ overall functioning of the stake module. ```golang type Params struct { - InflationRateChange sdk.Rat // maximum annual change in inflation rate - InflationMax sdk.Rat // maximum inflation rate - InflationMin sdk.Rat // minimum inflation rate - GoalBonded sdk.Rat // Goal of percent bonded atoms + InflationRateChange sdk.Rat // maximum annual change in inflation rate + InflationMax sdk.Rat // maximum inflation rate + InflationMin sdk.Rat // minimum inflation rate + GoalBonded sdk.Rat // Goal of percent bonded atoms - MaxValidators uint16 // maximum number of validators - BondDenom string // bondable coin denomination + MaxValidators uint16 // maximum number of validators + BondDenom string // bondable coin denomination } ``` @@ -65,27 +65,28 @@ type Validator struct { ConsensusPubKey crypto.PubKey // Tendermint consensus pubkey of validator Revoked bool // has the validator been revoked? - PoolShares PoolShares // total shares for tokens held in the pool - DelegatorShares sdk.Rat // total shares issued to a validator's delegators + PoolShares PoolShares // total shares for tokens held in the pool + DelegatorShares sdk.Rat // total shares issued to a validator's delegators + SlashRatio sdk.Rat // increases each time the validator is slashed - Description Description // description terms for the validator - BondHeight int64 // earliest height as a bonded validator - BondIntraTxCounter int16 // block-local tx index of validator change - ProposerRewardPool sdk.Coins // reward pool collected from being the proposer + Description Description // description terms for the validator + BondHeight int64 // earliest height as a bonded validator + BondIntraTxCounter int16 // block-local tx index of validator change + ProposerRewardPool sdk.Coins // reward pool collected from being the proposer Commission sdk.Rat // the commission rate of fees charged to any delegators CommissionMax sdk.Rat // maximum commission rate which this validator can ever charge CommissionChangeRate sdk.Rat // maximum daily increase of the validator commission CommissionChangeToday sdk.Rat // commission rate change today, reset each day (UTC time) - PrevPoolShares PoolShares // total shares of a global hold pools + PrevPoolShares PoolShares // total shares of a global hold pools } type Description struct { - Moniker string // name - Identity string // optional identity signature (ex. UPort or Keybase) - Website string // optional website link - Details string // optional details + Moniker string // name + Identity string // optional identity signature (ex. UPort or Keybase) + Website string // optional website link + Details string // optional details } ``` @@ -99,10 +100,10 @@ the transaction is the owner of the bond. ```golang type Delegation struct { - DelegatorAddr sdk.Address // delegation owner address - ValidatorAddr sdk.Address // validator owner address - Shares sdk.Rat // delegation shares recieved - Height int64 // last height bond updated + DelegatorAddr sdk.Address // delegation owner address + ValidatorAddr sdk.Address // validator owner address + Shares sdk.Rat // delegation shares recieved + Height int64 // last height bond updated } ``` @@ -110,17 +111,16 @@ type Delegation struct { - index: delegation address A UnbondingDelegation object is created every time an unbonding is initiated. -The unbond must be completed with a second transaction provided by the delegation owner -after the unbonding period has passed. - +The unbond must be completed with a second transaction provided by the +delegation owner after the unbonding period has passed. ```golang type UnbondingDelegation struct { - DelegationKey []byte // key of the delegation - InitTime int64 // unix time at unbonding initation - InitHeight int64 // block height at unbonding initation - ExpectedTokens sdk.Coins // the value in Atoms of the amount of shares which are unbonding - StartSlashRatio sdk.Rat // validator slash ratio at unbonding initiation + DelegationKey sdk.Address // key of the delegation + ExpectedTokens sdk.Coins // the value in Atoms of the amount of shares which are unbonding + StartSlashRatio sdk.Rat // validator slash ratio at unbonding initiation + CompleteTime int64 // unix time to complete redelegation + CompleteHeight int64 // block height to complete redelegation } ``` @@ -135,13 +135,14 @@ delegation owner after the unbonding period has passed. The destination delegation of a redelegation may not itself undergo a new redelegation until the original redelegation has been completed. - ```golang type Redelegation struct { - SourceDelegation []byte // source delegation key - DestinationDelegation []byte // destination delegation key - InitTime int64 // unix time at redelegation - InitHeight int64 // block height at redelegation - Shares sdk.Rat // amount of shares redelegating + SourceDelegation sdk.Address // source delegation key + DestinationDelegation sdk.Address // destination delegation key + SourceShares sdk.Rat // amount of source shares redelegating + DestinationShares sdk.Rat // amount of destination shares created at redelegation + SourceStartSlashRatio sdk.Rat // source validator slash ratio at unbonding initiation + CompleteTime int64 // unix time to complete redelegation + CompleteHeight int64 // block height to complete redelegation } ``` diff --git a/docs/spec/staking/transactions.md b/docs/spec/staking/transactions.md index b59077314..8013829ec 100644 --- a/docs/spec/staking/transactions.md +++ b/docs/spec/staking/transactions.md @@ -6,8 +6,10 @@ corresponding updates to the state. Transactions: - TxCreateValidator - TxEditValidator - TxDelegation - - TxRedelegation - - TxUnbond + - TxStartUnbonding + - TxCompleteUnbonding + - TxRedelegate + - TxCompleteRedelegation Other important state changes: - Update Validators @@ -109,43 +111,41 @@ delegate(tx TxDelegate): return ``` -### TxUnbond +### TxStartUnbonding Delegator unbonding is defined with the following transaction: ```golang -type TxUnbond struct { +type TxStartUnbonding struct { DelegatorAddr sdk.Address ValidatorAddr sdk.Address Shares string } -unbond(tx TxUnbond): +startUnbonding(tx TxStartUnbonding): delegation, found = getDelegatorBond(store, sender, tx.PubKey) if !found == nil return - if msg.Shares == "MAX" { + if tx.Shares == "MAX" { if !bond.Shares.GT(sdk.ZeroRat()) { - return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result() + return ErrNotEnoughBondShares else var err sdk.Error - delShares, err = sdk.NewRatFromDecimal(msg.Shares) + delShares, err = sdk.NewRatFromDecimal(tx.Shares) if err != nil return err if bond.Shares.LT(delShares) - return ErrNotEnoughBondShares(k.codespace, msg.Shares).Result() + return ErrNotEnoughBondShares - validator, found := k.GetValidator(ctx, msg.ValidatorAddr) + validator, found := GetValidator(tx.ValidatorAddr) if !found { return err - if msg.Shares == "MAX" + if tx.Shares == "MAX" delShares = bond.Shares bond.Shares -= delShares - unbondingDelegation = NewUnbondingDelegation(sender, delShares, currentHeight/Time, startSlashRatio) - setUnbondingDelegation(unbondingDelegation) revokeCandidacy := false if bond.Shares.IsZero() { @@ -153,30 +153,55 @@ unbond(tx TxUnbond): if bond.DelegatorAddr == validator.Owner && validator.Revoked == false revokeCandidacy = true - k.removeDelegation(ctx, bond) + removeDelegation( bond) else bond.Height = currentBlockHeight setDelegation(bond) - pool := k.GetPool(ctx) + pool := GetPool() validator, pool, returnAmount := validator.removeDelShares(pool, delShares) - k.setPool(ctx, pool) - AddCoins(ctx, bond.DelegatorAddr, returnAmount) + setPool( pool) + + unbondingDelegation = NewUnbondingDelegation(sender, returnAmount, currentHeight/Time, startSlashRatio) + setUnbondingDelegation(unbondingDelegation) if revokeCandidacy validator.Revoked = true - validator = updateValidator(ctx, validator) + validator = updateValidator(validator) if validator.DelegatorShares == 0 { - removeValidator(ctx, validator.Owner) + removeValidator(validator.Owner) return ``` +### TxCompleteUnbonding + +Complete the unbonding and transfer the coins to the delegate. Perform any +slashing that occured during the unbonding period. + +```golang +type TxUnbondingComplete struct { + DelegatorAddr sdk.Address + ValidatorAddr sdk.Address +} + +redelegationComplete(tx TxRedelegate): + unbonding = getUnbondingDelegation(tx.DelegatorAddr, tx.Validator) + if unbonding.CompleteTime >= CurrentBlockTime && unbonding.CompleteHeight >= CurrentBlockHeight + validator = GetValidator(tx.ValidatorAddr) + returnTokens = ExpectedTokens * tx.startSlashRatio/validator.SlashRatio + AddCoins(unbonding.DelegatorAddr, returnTokens) + removeUnbondingDelegation(unbonding) + return +``` + ### TxRedelegation -The redelegation command allows delegators to instantly switch validators. +The redelegation command allows delegators to instantly switch validators. Once +the unbonding period has passed, the redelegation must be completed with +txRedelegationComplete. ```golang type TxRedelegate struct { @@ -184,23 +209,48 @@ type TxRedelegate struct { ValidatorFrom Validator ValidatorTo Validator Shares sdk.Rat + CompletedTime int64 } redelegate(tx TxRedelegate): + pool = getPool() delegation = getDelegatorBond(tx.DelegatorAddr, tx.ValidatorFrom.Owner) - if delegation == nil then return + if delegation == nil + return - if delegation.Shares < tx.Shares return + if delegation.Shares < tx.Shares + return delegation.shares -= Tx.Shares validator, pool, createdCoins = validator.RemoveShares(pool, tx.Shares) setPool(pool) - redelegation = newRedelegation(validatorFrom, validatorTo, Shares, createdCoins) + redelegation = newRedelegation(tx.DelegatorAddr, tx.validatorFrom, + tx.validatorTo, tx.Shares, createdCoins, tx.CompletedTime) setRedelegation(redelegation) return ``` +### TxCompleteRedelegation + +Note that unlike TxCompleteUnbonding slashing of redelegating shares does not +take place during completion. Slashing on redelegated shares takes place +actively as a slashing occurs. + +```golang +type TxRedelegationComplete struct { + DelegatorAddr Address + ValidatorFrom Validator + ValidatorTo Validator +} + +redelegationComplete(tx TxRedelegate): + redelegation = getRedelegation(tx.DelegatorAddr, tx.validatorFrom, tx.validatorTo) + if redelegation.CompleteTime >= CurrentBlockTime && redelegation.CompleteHeight >= CurrentBlockHeight + removeRedelegation(redelegation) + return +``` + ### Update Validators Within many transactions the validator set must be updated based on changes in diff --git a/x/stake/delegation.go b/x/stake/delegation.go index ae08e0867..8b047acce 100644 --- a/x/stake/delegation.go +++ b/x/stake/delegation.go @@ -49,5 +49,4 @@ func (b Delegation) HumanReadableString() (string, error) { resp += fmt.Sprintf("Height: %d", b.Height) return resp, nil - } From d587a6f6c3607aed5ce5637f22ce166a3229c8dc Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Wed, 30 May 2018 13:30:19 -0400 Subject: [PATCH 08/27] docs: some files need to be in root & path fixes --- docs/{guides => }/Makefile | 0 docs/{guides => }/conf.py | 0 docs/{guides => }/index.rst | 6 +++--- docs/{guides => }/make.bat | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename docs/{guides => }/Makefile (100%) rename docs/{guides => }/conf.py (100%) rename docs/{guides => }/index.rst (92%) rename docs/{guides => }/make.bat (100%) diff --git a/docs/guides/Makefile b/docs/Makefile similarity index 100% rename from docs/guides/Makefile rename to docs/Makefile diff --git a/docs/guides/conf.py b/docs/conf.py similarity index 100% rename from docs/guides/conf.py rename to docs/conf.py diff --git a/docs/guides/index.rst b/docs/index.rst similarity index 92% rename from docs/guides/index.rst rename to docs/index.rst index 66e3f7cb8..77b030743 100644 --- a/docs/guides/index.rst +++ b/docs/index.rst @@ -17,8 +17,8 @@ SDK .. toctree:: :maxdepth: 1 - sdk/install.rst - sdk/key-management.rst + guides/sdk/install.rst + guides/sdk/key-management.rst .. sdk/overview.rst # needs to be updated .. old/glossary.rst # not completely up to date but has good content @@ -47,7 +47,7 @@ Staking .. toctree:: :maxdepth: 1 - staking/testnet.rst + guides/staking/testnet.rst .. staking/intro.rst .. staking/key-management.rst .. staking/local-testnet.rst diff --git a/docs/guides/make.bat b/docs/make.bat similarity index 100% rename from docs/guides/make.bat rename to docs/make.bat From 605dfbbe5454ce9b4dbdca31ffbf104964ba0993 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 30 May 2018 12:04:14 -0700 Subject: [PATCH 09/27] changelog, update spec README --- CHANGELOG.md | 6 ++++-- docs/spec/README.md | 6 ++++-- docs/spec/staking/end_block.md | 6 +++--- docs/spec/staking/transactions.md | 2 +- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22969a403..dd953b6ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,9 +41,11 @@ BUG FIXES * query sequence via account store * fixed duplicate pub_key in stake.Validator -## 0.18.0 +FEATURES +* [docs] Reorganize documentation +* [docs] Update staking spec, create WIP spec for slashing, and fees -_TBD_ +## 0.18.0 BREAKING CHANGES diff --git a/docs/spec/README.md b/docs/spec/README.md index e7507bf95..7f773e6aa 100644 --- a/docs/spec/README.md +++ b/docs/spec/README.md @@ -7,11 +7,13 @@ NOTE: the specifications are not yet complete and very much a work in progress. - [Basecoin](basecoin) - Cosmos SDK related specifications and transactions for sending tokens. -- [Staking](staking) - Proof of Stake related specifications including bonding - and delegation transactions, inflation, fees, etc. - [Governance](governance) - Governance related specifications including proposals and voting. - [IBC](ibc) - Specification of the Cosmos inter-blockchain communication (IBC) protocol. +- [Staking](staking) - Proof of Stake related specifications including bonding + and delegation transactions, inflation, etc. +- [Slashing](slashing) - Specifications of validator punishment mechanisms +- [Provisioning](provisioning) - Fee distribution, and atom provision distribution specification - [Other](other) - Other components of the Cosmos Hub, including the reserve pool, All in Bits vesting, etc. diff --git a/docs/spec/staking/end_block.md b/docs/spec/staking/end_block.md index e7c4e1e82..28e3891d1 100644 --- a/docs/spec/staking/end_block.md +++ b/docs/spec/staking/end_block.md @@ -1,7 +1,7 @@ # End-Block -Two staking activities are intended to be processed in the application endblock. - - inform tendermint of validator set changes +Two staking activities are intended to be processed in the application end-block. + - inform Tendermint of validator set changes - process and set atom inflation # Validator Set Changes @@ -10,7 +10,7 @@ The Tendermint validator set may be updated by state transitions that run at the end of every block. The Tendermint validator set may be changed by validators either being revoked due to inactivity/unexpected behaviour (covered in slashing) or changed in validator power. Determining which validator set -changes must be made occures during staking transactions (and slashing +changes must be made occurs during staking transactions (and slashing transactions) - during end-block the already accounted changes are applied and the changes cleared diff --git a/docs/spec/staking/transactions.md b/docs/spec/staking/transactions.md index 8013829ec..11edbb98b 100644 --- a/docs/spec/staking/transactions.md +++ b/docs/spec/staking/transactions.md @@ -19,7 +19,7 @@ Other notes: - `sender` denotes the address of the sender of the transaction - `getXxx`, `setXxx`, and `removeXxx` functions are used to retrieve and modify objects from the store - - `sdk.Rat` refers to a rational numeric type specified by the sdk. + - `sdk.Rat` refers to a rational numeric type specified by the SDK. ### TxCreateValidator From e78a232e8508bbbb7bd0856557d0fe85dbed44b5 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 31 May 2018 00:54:46 +0200 Subject: [PATCH 10/27] Move over slashing spec changes from #1011 --- docs/spec/slashing/valset-changes.md | 54 +++++++++++++++++----------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/docs/spec/slashing/valset-changes.md b/docs/spec/slashing/valset-changes.md index c403e5d4c..8aad213b8 100644 --- a/docs/spec/slashing/valset-changes.md +++ b/docs/spec/slashing/valset-changes.md @@ -49,40 +49,52 @@ LastCommit and +2/3 (see [TODO](https://github.com/cosmos/cosmos-sdk/issues/967) Validators are penalized for failing to be included in the LastCommit for some number of blocks by being automatically unbonded. -The following information is stored with each validator, and is only non-zero if the validator becomes an active validator: +The following information is stored with each validator candidate, and is only non-zero if the candidate becomes an active validator: ```go type ValidatorSigningInfo struct { - StartHeight int64 - SignedBlocksBitArray BitArray + StartHeight int64 + IndexOffset int64 + JailedUntil int64 + SignedBlocksCounter int64 + SignedBlocksBitArray BitArray } ``` Where: -* `StartHeight` is set to the height that the validator became an active validator (with non-zero voting power). +* `StartHeight` is set to the height that the candidate became an active validator (with non-zero voting power). +* `IndexOffset` is incremented each time the candidate was a bonded validator in a block (and may have signed a precommit or not). +* `JailedUntil` is set whenever the candidate is revoked due to downtime +* `SignedBlocksCounter` is a counter kept to avoid unnecessary array reads. `SignedBlocksBitArray.Sum() == SignedBlocksCounter` always. * `SignedBlocksBitArray` is a bit-array of size `SIGNED_BLOCKS_WINDOW` that records, for each of the last `SIGNED_BLOCKS_WINDOW` blocks, -whether or not this validator was included in the LastCommit. It uses a `0` if the validator was included, and a `1` if it was not. -Note it is initialized with all 0s. +whether or not this validator was included in the LastCommit. It uses a `1` if the validator was included, and a `0` if it was not. Note it is initialized with all 0s. At the beginning of each block, we update the signing info for each validator and check if they should be automatically unbonded: ``` -h = block.Height -index = h % SIGNED_BLOCKS_WINDOW +height := block.Height for val in block.Validators: - signInfo = val.SignInfo - if val in block.LastCommit: - signInfo.SignedBlocksBitArray.Set(index, 0) - else - signInfo.SignedBlocksBitArray.Set(index, 1) + signInfo = val.SignInfo + index := signInfo.IndexOffset % SIGNED_BLOCKS_WINDOW + signInfo.IndexOffset++ + previous = signInfo.SignedBlocksBitArray.Get(index) - // validator must be active for at least SIGNED_BLOCKS_WINDOW - // before they can be automatically unbonded for failing to be - // included in 50% of the recent LastCommits - minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW - minSigned = SIGNED_BLOCKS_WINDOW / 2 - blocksSigned = signInfo.SignedBlocksBitArray.Sum() - if h > minHeight AND blocksSigned < minSigned: - unbond the validator + // update counter if array has changed + if previous and val in block.AbsentValidators: + signInfo.SignedBlocksBitArray.Set(index, 0) + signInfo.SignedBlocksCounter-- + else if !previous and val not in block.AbsentValidators: + signInfo.SignedBlocksBitArray.Set(index, 1) + signInfo.SignedBlocksCounter++ + // else previous == val not in block.AbsentValidators, no change + + // validator must be active for at least SIGNED_BLOCKS_WINDOW + // before they can be automatically unbonded for failing to be + // included in 50% of the recent LastCommits + minHeight = signInfo.StartHeight + SIGNED_BLOCKS_WINDOW + minSigned = SIGNED_BLOCKS_WINDOW / 2 + if height > minHeight AND signInfo.SignedBlocksCounter < minSigned: + signInfo.JailedUntil = block.Time + DOWNTIME_UNBOND_DURATION + slash & unbond the validator ``` From d87dfce8e4db5c46fa349951f9de5be15c1b428e Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Thu, 31 May 2018 09:16:12 -0400 Subject: [PATCH 11/27] docs: rst => md --- docs/conf.py | 14 +++-- docs/guides/sdk/install.md | 42 +++++++++++++ docs/guides/sdk/install.rst | 48 --------------- docs/guides/sdk/key-management.md | 17 ++++++ docs/guides/sdk/key-management.rst | 18 ------ docs/guides/staking/testnet.md | 94 ++++++++++++++++++++++++++++++ docs/index.rst | 7 ++- 7 files changed, 167 insertions(+), 73 deletions(-) create mode 100644 docs/guides/sdk/install.md delete mode 100644 docs/guides/sdk/install.rst create mode 100644 docs/guides/sdk/key-management.md delete mode 100644 docs/guides/sdk/key-management.rst create mode 100644 docs/guides/staking/testnet.md diff --git a/docs/conf.py b/docs/conf.py index 73a0220fd..3f7cb19b5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,9 +38,15 @@ templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: -# -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' + +from recommonmark.parser import CommonMarkParser + +source_parsers = { + '.md': CommonMarkParser, +} + +source_suffix = ['.rst', '.md'] +#source_suffix = '.rst' # The master toctree document. master_doc = 'index' @@ -69,7 +75,7 @@ language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'old'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '_attic', 'spec'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' diff --git a/docs/guides/sdk/install.md b/docs/guides/sdk/install.md new file mode 100644 index 000000000..f34d0bd72 --- /dev/null +++ b/docs/guides/sdk/install.md @@ -0,0 +1,42 @@ +# Install + +Cosmos SDK can be installed to +`$GOPATH/src/github.com/cosmos/cosmos-sdk` like a normal Go program: + +``` +go get github.com/cosmos/cosmos-sdk +``` + +If the dependencies have been updated with breaking changes, or if +another branch is required, `dep` is used for dependency management. +Thus, assuming you've already run `go get` or otherwise cloned the repo, +the correct way to install is: + +``` +cd $GOPATH/src/github.com/cosmos/cosmos-sdk +make get_vendor_deps +make install +make install_examples +``` + +This will install `gaiad` and `gaiacli` and four example binaries: +`basecoind`, `basecli`, `democoind`, and `democli`. + +Verify that everything is OK by running: + +``` +gaiad version +``` + +you should see: + +``` +0.17.3-a5a78eb +``` + +then with: + +``` +gaiacli version +``` +you should see the same version (or a later one for both). diff --git a/docs/guides/sdk/install.rst b/docs/guides/sdk/install.rst deleted file mode 100644 index 03b219cb5..000000000 --- a/docs/guides/sdk/install.rst +++ /dev/null @@ -1,48 +0,0 @@ -Install -======= - -Cosmos SDK can be installed to -``$GOPATH/src/github.com/cosmos/cosmos-sdk`` like a normal Go program: - -:: - - go get github.com/cosmos/cosmos-sdk - -If the dependencies have been updated with breaking changes, or if -another branch is required, ``dep`` is used for dependency management. -Thus, assuming you've already run ``go get`` or otherwise cloned the -repo, the correct way to install is: - -:: - - cd $GOPATH/src/github.com/cosmos/cosmos-sdk - make get_vendor_deps - make install - make install_examples - -This will install ``gaiad`` and ``gaiacli`` and four example binaries: -``basecoind``, ``basecli``, ``democoind``, and ``democli``. - -Verify that everything is OK by running: - -:: - - gaiad version - -you should see: - -:: - - 0.15.0-rc1-9d90c6b - -then with: - -:: - - gaiacli version - -you should see: - -:: - - 0.15.0-rc1-9d90c6b diff --git a/docs/guides/sdk/key-management.md b/docs/guides/sdk/key-management.md new file mode 100644 index 000000000..a4c0f5501 --- /dev/null +++ b/docs/guides/sdk/key-management.md @@ -0,0 +1,17 @@ +# Key Management + +Here we cover many aspects of handling keys within the Cosmos SDK +framework. + +## Pseudo Code + +Generating an address for an ed25519 public key (in pseudo code): + +``` +const TypeDistinguisher = HexToBytes("1624de6220") + +// prepend the TypeDistinguisher as Bytes +SerializedBytes = TypeDistinguisher ++ PubKey.asBytes() + +Address = ripemd160(SerializedBytes) +``` diff --git a/docs/guides/sdk/key-management.rst b/docs/guides/sdk/key-management.rst deleted file mode 100644 index d2b657729..000000000 --- a/docs/guides/sdk/key-management.rst +++ /dev/null @@ -1,18 +0,0 @@ -Key Management -============== - -Here we cover many aspects of handling keys within the Cosmos SDK framework. - -Pseudo Code ------------ - -Generating an address for an ed25519 public key (in pseudo code): - -:: - - const TypeDistinguisher = HexToBytes("1624de6220") - - // prepend the TypeDistinguisher as Bytes - SerializedBytes = TypeDistinguisher ++ PubKey.asBytes() - - Address = ripemd160(SerializedBytes) diff --git a/docs/guides/staking/testnet.md b/docs/guides/staking/testnet.md new file mode 100644 index 000000000..b2bbd8f1a --- /dev/null +++ b/docs/guides/staking/testnet.md @@ -0,0 +1,94 @@ +# Testnet Setup + +**Note:** This document is incomplete and may not be up-to-date with the +state of the code. + +See the [installation guide](../sdk/install.html) for details on +installation. + +Here is a quick example to get you off your feet: + +First, generate a couple of genesis transactions to be incorporated into +the genesis file, this will create two keys with the password +`1234567890`: + +``` +gaiad init gen-tx --name=foo --home=$HOME/.gaiad1 +gaiad init gen-tx --name=bar --home=$HOME/.gaiad2 +gaiacli keys list +``` + +**Note:** If you've already run these tests you may need to overwrite +keys using the `--owk` flag When you list the keys you should see two +addresses, we'll need these later so take note. Now let's actually +create the genesis files for both nodes: + +``` +cp -a ~/.gaiad2/config/gentx/. ~/.gaiad1/config/gentx/ +cp -a ~/.gaiad1/config/gentx/. ~/.gaiad2/config/gentx/ +gaiad init --gen-txs --home=$HOME/.gaiad1 --chain-id=test-chain +gaiad init --gen-txs --home=$HOME/.gaiad2 --chain-id=test-chain +``` + +**Note:** If you've already run these tests you may need to overwrite +genesis using the `-o` flag. What we just did is copy the genesis +transactions between each of the nodes so there is a common genesis +transaction set; then we created both genesis files independently from +each home directory. Importantly both nodes have independently created +their `genesis.json` and `config.toml` files, which should be identical +between nodes. + +Great, now that we've initialized the chains, we can start both nodes in +the background: + +``` +gaiad start --home=$HOME/.gaiad1 &> gaia1.log & +NODE1_PID=$! +gaia start --home=$HOME/.gaiad2 &> gaia2.log & +NODE2_PID=$! +``` + +Note that we save the PID so we can later kill the processes. You can +peak at your logs with `tail gaia1.log`, or follow them for a bit with +`tail -f gaia1.log`. + +Nice. We can also lookup the validator set: + +``` +gaiacli validatorset +``` + +Then, we try to transfer some `steak` to another account: + +``` +gaiacli account +gaiacli account +gaiacli send --amount=10steak --to= --name=foo --chain-id=test-chain +``` + +**Note:** We need to be careful with the `chain-id` and `sequence` + +Check the balance & sequence with: + +``` +gaiacli account +``` + +To confirm for certain the new validator is active, check tendermint: + +``` +curl localhost:46657/validators +``` + +Finally, to relinquish all your power, unbond some coins. You should see +your VotingPower reduce and your account balance increase. + +``` +gaiacli unbond --chain-id= --name=test +``` + +That's it! + +**Note:** TODO demonstrate edit-candidacy **Note:** TODO demonstrate +delegation **Note:** TODO demonstrate unbond of delegation **Note:** +TODO demonstrate unbond candidate diff --git a/docs/index.rst b/docs/index.rst index 77b030743..3a2237a3c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -17,8 +17,9 @@ SDK .. toctree:: :maxdepth: 1 - guides/sdk/install.rst - guides/sdk/key-management.rst + guides/sdk/install.md + guides/sdk/key-management.md + .. sdk/overview.rst # needs to be updated .. old/glossary.rst # not completely up to date but has good content @@ -47,7 +48,7 @@ Staking .. toctree:: :maxdepth: 1 - guides/staking/testnet.rst + guides/staking/testnet.md .. staking/intro.rst .. staking/key-management.rst .. staking/local-testnet.rst From 1031e45a823b19214c0f8ea4c9118dfa77b61df9 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Thu, 31 May 2018 11:03:02 -0400 Subject: [PATCH 12/27] docs/install: add easy script --- scripts/install.sh | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 scripts/install.sh diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100644 index 000000000..e8a6a75e3 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# XXX: this script is intended to be run from +# a fresh Digital Ocean droplet with Ubuntu + +# upon its completion, you must either reset +# your terminal or run `source ~/.profile` + +# change this to a specific release or branch +BRANCH=master + +sudo apt-get update -y +sudo apt-get upgrade -y +sudo apt-get install -y make + +# get and unpack golang +curl -O https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz +tar -xvf go1.10.linux-amd64.tar.gz + +# move go binary and add to path +mv go /usr/local +echo "export PATH=\$PATH:/usr/local/go/bin" >> ~/.profile + +# create the goApps directory, set GOPATH, and put it on PATH +mkdir goApps +echo "export GOPATH=/root/goApps" >> ~/.profile +echo "export PATH=\$PATH:\$GOPATH/bin" >> ~/.profile + +source ~/.profile + +# get the code and move into repo +REPO=github.com/cosmos/cosmos-sdk +go get $REPO +cd $GOPATH/src/$REPO + +# build & install master +git checkout $BRANCH +make get_tools +make get_vendor_deps +make install +make install_examples From cd4499c15f7871f6b3554afd461275dda2a80a81 Mon Sep 17 00:00:00 2001 From: Zach Ramsay Date: Thu, 31 May 2018 11:12:03 -0400 Subject: [PATCH 13/27] docs: link to quick install script --- docs/guides/sdk/install.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/guides/sdk/install.md b/docs/guides/sdk/install.md index f34d0bd72..cf3e83161 100644 --- a/docs/guides/sdk/install.md +++ b/docs/guides/sdk/install.md @@ -1,5 +1,8 @@ # Install +The fastest and easiest way to install the Cosmos SDK binaries +is to run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install.sh) on a fresh Ubuntu instance. Read the comments / instructions carefully (i.e., reset your terminal after running the script). + Cosmos SDK can be installed to `$GOPATH/src/github.com/cosmos/cosmos-sdk` like a normal Go program: @@ -14,6 +17,7 @@ the correct way to install is: ``` cd $GOPATH/src/github.com/cosmos/cosmos-sdk +make get_tools make get_vendor_deps make install make install_examples @@ -40,3 +44,16 @@ then with: gaiacli version ``` you should see the same version (or a later one for both). + +## Update + +Get latest code (you can also `git fetch` only the version desired), +ensure the dependencies are up to date, then recompile. + +``` +cd $GOPATH/src/github.com/cosmos/cosmos-sdk +git fetch -a origin +git checkout VERSION +make get_vendor_deps +make install +``` From 40df73adc2eab233149f68a5241a8a879139bbd7 Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Mon, 4 Jun 2018 13:35:00 -0700 Subject: [PATCH 14/27] docs: Add BSD install script --- docs/guides/sdk/install.md | 2 +- scripts/install_sdk_bsd.sh | 46 +++++++++++++++++++ scripts/{install.sh => install_sdk_ubuntu.sh} | 0 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 scripts/install_sdk_bsd.sh rename scripts/{install.sh => install_sdk_ubuntu.sh} (100%) diff --git a/docs/guides/sdk/install.md b/docs/guides/sdk/install.md index cf3e83161..37faec829 100644 --- a/docs/guides/sdk/install.md +++ b/docs/guides/sdk/install.md @@ -1,7 +1,7 @@ # Install The fastest and easiest way to install the Cosmos SDK binaries -is to run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install.sh) on a fresh Ubuntu instance. Read the comments / instructions carefully (i.e., reset your terminal after running the script). +is to run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install_sdk_ubuntu.sh) on a fresh Ubuntu instance. Similarly, you can run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install_sdk_bsd.sh) on a fresh FreeBSD instance. Read the comments / instructions carefully (i.e., reset your terminal after running the script). Cosmos SDK can be installed to `$GOPATH/src/github.com/cosmos/cosmos-sdk` like a normal Go program: diff --git a/scripts/install_sdk_bsd.sh b/scripts/install_sdk_bsd.sh new file mode 100644 index 000000000..e0795ca90 --- /dev/null +++ b/scripts/install_sdk_bsd.sh @@ -0,0 +1,46 @@ +#!/usr/bin/tcsh + +# Just run tcsh install_sdk_bsd.sh +# XXX: this script is intended to be run from +# a fresh Digital Ocean droplet with FreeBSD + +# upon its completion, you must either reset +# your terminal or run `source ~/.tcshrc` + +# change this to a specific release or branch +set BRANCH=master + +sudo pkg updatels + +sudo pkg upgrade -y +sudo pkg install -y gmake +sudo pkg install -y git + +# get and unpack golang +curl -O https://storage.googleapis.com/golang/go1.10.freebsd-amd64.tar.gz +tar -xvf go1.10.freebsd-amd64.tar.gz + +# move go binary and add to path +mv go /usr/local +set path=($path /usr/local/go/bin) + + +# create the go directory, set GOPATH, and put it on PATH +mkdir go +echo "setenv GOPATH /root/go" >> ~/.tcshrc +setenv GOPATH /root/go +echo "set path=($path $GOPATH/bin)" >> ~/.tcshrc + +source ~/.tcshrc + +# get the code and move into repo +set REPO=github.com/cosmos/cosmos-sdk +go get $REPO +cd $GOPATH/src/$REPO + +# build & install master +git checkout $BRANCH +gmake get_tools +gmake get_vendor_deps +gmake install +gmake install_examples diff --git a/scripts/install.sh b/scripts/install_sdk_ubuntu.sh similarity index 100% rename from scripts/install.sh rename to scripts/install_sdk_ubuntu.sh From 508af391fe4252f99600a7ba8e9aa92d468f162a Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Mon, 4 Jun 2018 16:52:19 -0700 Subject: [PATCH 15/27] docs: Add disclaimer for install scripts --- docs/guides/sdk/install.md | 2 +- scripts/install_sdk_bsd.sh | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/guides/sdk/install.md b/docs/guides/sdk/install.md index 37faec829..bafdfc4bd 100644 --- a/docs/guides/sdk/install.md +++ b/docs/guides/sdk/install.md @@ -1,7 +1,7 @@ # Install The fastest and easiest way to install the Cosmos SDK binaries -is to run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install_sdk_ubuntu.sh) on a fresh Ubuntu instance. Similarly, you can run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install_sdk_bsd.sh) on a fresh FreeBSD instance. Read the comments / instructions carefully (i.e., reset your terminal after running the script). +is to run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install_sdk_ubuntu.sh) on a fresh Ubuntu instance. Similarly, you can run [this script](https://github.com/cosmos/cosmos-sdk/blob/develop/scripts/install_sdk_bsd.sh) on a fresh FreeBSD instance. Read the scripts before running them to ensure no untrusted connection is being made, for example we're making curl requests to download golang. Also read the comments / instructions carefully (i.e., reset your terminal after running the script). Cosmos SDK can be installed to `$GOPATH/src/github.com/cosmos/cosmos-sdk` like a normal Go program: diff --git a/scripts/install_sdk_bsd.sh b/scripts/install_sdk_bsd.sh index e0795ca90..2ee812e54 100644 --- a/scripts/install_sdk_bsd.sh +++ b/scripts/install_sdk_bsd.sh @@ -7,6 +7,11 @@ # upon its completion, you must either reset # your terminal or run `source ~/.tcshrc` +# This assumes your installing it through tcsh as root. +# Change the relevant lines from tcsh to csh if your +# installing as a different user, along with changing the +# gopath. + # change this to a specific release or branch set BRANCH=master From 23fe4fab456910953114a25814eb18b33047da41 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Mon, 4 Jun 2018 22:57:09 -0700 Subject: [PATCH 16/27] Fix typo --- scripts/install_sdk_bsd.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install_sdk_bsd.sh b/scripts/install_sdk_bsd.sh index 2ee812e54..5bff970e6 100644 --- a/scripts/install_sdk_bsd.sh +++ b/scripts/install_sdk_bsd.sh @@ -15,7 +15,7 @@ # change this to a specific release or branch set BRANCH=master -sudo pkg updatels +sudo pkg update sudo pkg upgrade -y sudo pkg install -y gmake From 02559e725a49e70434e424f29e674f3e4d9149ce Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 5 Jun 2018 09:11:03 -0700 Subject: [PATCH 17/27] cwgoes spec comments --- docs/guides/staking/overview.md | 28 ++++++++++-------- .../fee_distribution_model.xlsx | Bin .../overview.md | 17 +++++------ docs/spec/{slashing => WIP_slashing}/state.md | 0 .../transactions.md | 0 .../valset-changes.md | 0 6 files changed, 23 insertions(+), 22 deletions(-) rename docs/spec/{provisioning => WIP_provisioning}/fee_distribution_model.xlsx (100%) rename docs/spec/{provisioning => WIP_provisioning}/overview.md (95%) rename docs/spec/{slashing => WIP_slashing}/state.md (100%) rename docs/spec/{slashing => WIP_slashing}/transactions.md (100%) rename docs/spec/{slashing => WIP_slashing}/valset-changes.md (100%) diff --git a/docs/guides/staking/overview.md b/docs/guides/staking/overview.md index 9867dc95e..570a0bfc5 100644 --- a/docs/guides/staking/overview.md +++ b/docs/guides/staking/overview.md @@ -6,25 +6,27 @@ The Cosmos Hub is a Tendermint-based Delegated Proof of Stake (DPos) blockchain system that serves as a backbone of the Cosmos ecosystem. It is operated and -secured by an open and globally decentralized set of validators. Tendermint -consensus is a Byzantine fault-tolerant distributed protocol that involves all -validators in the process of exchanging protocol messages in the production of -each block. To avoid Nothing-at-Stake problem, a validator in Tendermint needs -to lock up coins in a bond deposit. Tendermint protocol messages are signed by -the validator's private key, and this is a basis for Tendermint strict -accountability that allows punishing misbehaving validators by slashing -(burning) their bonded Atoms. On the other hand, validators are rewarded for -their service of securing blockchain network by the inflationary provisions and -transactions fees. This incentives correct behavior of the validators and -provides the economic security of the network. +secured by an open and globally decentralized set of validators. Tendermint is +a Byzantine fault-tolerant distributed protocol for consensus among distrusting +parties, in this case the group of validators which produce the blocks for +cosmos. To avoid the nothing-at-stake problem, a validator in Tendermint needs +to lock up coins in a bond deposit. Each bond's atoms are illiquid, they cannot +be transferred however they can be unbonded to become liquid, this process +takes the unbonding-period which will be 3 weeks by default at Cosmos-Hub +launch. Tendermint protocol messages are signed by the validator's private key, +and this is a basis for Tendermint strict accountability that allows punishing +misbehaving validators by slashing (burning) their bonded Atoms. On the other +hand, validators are rewarded for their service of securing blockchain network +by the inflationary provisions and transactions fees. This incentivizes correct +behavior of the validators and provides the economic security of the network. -The native token of the Cosmos Hub is called Atom; becoming a validator of the +The native token of the Cosmos Hub is called the Atom; becoming a validator of the Cosmos Hub requires holding Atoms. However, not all Atom holders are validators of the Cosmos Hub. More precisely, there is a selection process that determines the validator set as a subset of all validators (Atom holders that wants to become a validator). The other option for Atom holder is to delegate their atoms to validators, i.e., being a delegator. A delegator is an Atom -holder that has bonded its Atoms by delegating it to a validator. By bonding +holder that has put its Atoms at stake by delegating it to a validator. By bonding Atoms to secure the network (and taking a risk of being slashed in case of misbehaviour), a user is rewarded with inflationary provisions and transaction fees proportional to the amount of its bonded Atoms. The Cosmos Hub is diff --git a/docs/spec/provisioning/fee_distribution_model.xlsx b/docs/spec/WIP_provisioning/fee_distribution_model.xlsx similarity index 100% rename from docs/spec/provisioning/fee_distribution_model.xlsx rename to docs/spec/WIP_provisioning/fee_distribution_model.xlsx diff --git a/docs/spec/provisioning/overview.md b/docs/spec/WIP_provisioning/overview.md similarity index 95% rename from docs/spec/provisioning/overview.md rename to docs/spec/WIP_provisioning/overview.md index a8ffac398..046223a4b 100644 --- a/docs/spec/provisioning/overview.md +++ b/docs/spec/WIP_provisioning/overview.md @@ -13,15 +13,14 @@ withdraw all fees they are entitled too before they can bond or unbond Atoms. Because fees are optimized to note -Commission on Atom Provisions and having atoms autobonded are mutually exclusive (we can’t have both). The reason -for this is that - -if there are atoms commissions and autobonding, the portion of -atoms the fee distribution calculation would become very large as the atom -portion for each delegator would change each block making a withdrawal of fees -for a delegator require a calculation for every single block since the last -withdrawal. Conclusion we can only have atom commission and unbonded atoms -provisions, or bonded atom provisions and no atom commission +Commission on Atom Provisions and having atoms autobonded are mutually +exclusive (we can’t have both). The reason for this is that if there are atoms +commissions and autobonding, the portion of atoms the fee distribution +calculation would become very large as the atom portion for each delegator +would change each block making a withdrawal of fees for a delegator require a +calculation for every single block since the last withdrawal. Conclusion we can +only have atom commission and unbonded atoms provisions, or bonded atom +provisions and no atom commission ## Fee Calculations diff --git a/docs/spec/slashing/state.md b/docs/spec/WIP_slashing/state.md similarity index 100% rename from docs/spec/slashing/state.md rename to docs/spec/WIP_slashing/state.md diff --git a/docs/spec/slashing/transactions.md b/docs/spec/WIP_slashing/transactions.md similarity index 100% rename from docs/spec/slashing/transactions.md rename to docs/spec/WIP_slashing/transactions.md diff --git a/docs/spec/slashing/valset-changes.md b/docs/spec/WIP_slashing/valset-changes.md similarity index 100% rename from docs/spec/slashing/valset-changes.md rename to docs/spec/WIP_slashing/valset-changes.md From 8acac9089413528e43dba4f48b6975b6a1e16fde Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 13 Jun 2018 01:39:08 -0700 Subject: [PATCH 18/27] gaia 6002 --- cmd/gaia/testnets/README.md | 12 +- cmd/gaia/testnets/STATUS.md | 14 + cmd/gaia/testnets/gaia-6002/genesis.json | 1459 ++++++++++++++++++++++ 3 files changed, 1479 insertions(+), 6 deletions(-) create mode 100644 cmd/gaia/testnets/gaia-6002/genesis.json diff --git a/cmd/gaia/testnets/README.md b/cmd/gaia/testnets/README.md index b34926aed..b65feeb91 100644 --- a/cmd/gaia/testnets/README.md +++ b/cmd/gaia/testnets/README.md @@ -1,4 +1,4 @@ -# Connect to the `gaia-6001` Testnet +# Connect to the `gaia-6002` Testnet Note: We are aware this documentation is sub-par. We are working to improve the tooling and the documentation to make this process as painless as @@ -23,7 +23,7 @@ Next, let's install the testnet's version of the Cosmos SDK. mkdir -p $GOPATH/src/github.com/cosmos cd $GOPATH/src/github.com/cosmos git clone https://github.com/cosmos/cosmos-sdk -cd cosmos-sdk && git checkout v0.18.0 +cd cosmos-sdk && git checkout v0.19.0 make get_tools && make get_vendor_deps && make install ``` @@ -31,7 +31,7 @@ That will install the `gaiad` and `gaiacli` binaries. Verify that everything is ``` gaiad version -0.18.0-eceb56b7 +0.19.0- ``` ### Node Setup @@ -76,7 +76,7 @@ Now it is time to upgrade the software: ``` cd $GOPATH/src/github.com/cosmos/cosmos-sdk -git fetch --all && git checkout v0.18.0 +git fetch --all && git checkout v0.19.0 make update_tools && make get_vendor_deps && make install ``` @@ -90,7 +90,7 @@ Copy the testnet's `genesis.json` file and place it in `gaiad`'s config director ``` mkdir -p $HOME/.gaiad/config -cp -a $GOPATH/src/github.com/cosmos/cosmos-sdk/cmd/gaia/testnets/gaia-6001/genesis.json $HOME/.gaiad/config/genesis.json +cp -a $GOPATH/src/github.com/cosmos/cosmos-sdk/cmd/gaia/testnets/gaia-6002/genesis.json $HOME/.gaiad/config/genesis.json ``` ### Add Seed Nodes @@ -99,7 +99,7 @@ Your node needs to know how to find peers. You'll need to add healthy seed nodes ``` # Comma separated list of seed nodes to connect to -seeds = "38aa9bec3998f12ae9088b21a2d910d19d565c27@gaia-6001.coinculture.net:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@seed.cosmos.cryptium.ch:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@35.198.166.171:46656,032fa56301de335d835057fb6ad9f7ce2242a66d@165.227.236.213:46656" +seeds = "38aa9bec3998f12ae9088b21a2d910d19d565c27@gaia-6002.coinculture.net:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@seed.cosmos.cryptium.ch:46656,80a35a46ce09cfb31ee220c8141a25e73e0b239b@35.198.166.171:46656,032fa56301de335d835057fb6ad9f7ce2242a66d@165.227.236.213:46656" ``` You can also [ask other validators](https://riot.im/app/#/room/#cosmos_validators:matrix.org) for a persistent peer and add it under the `persistent_peers` key. For more information on seeds and peers, [read this](https://github.com/tendermint/tendermint/blob/develop/docs/using-tendermint.md#peers). diff --git a/cmd/gaia/testnets/STATUS.md b/cmd/gaia/testnets/STATUS.md index 6f2140c26..9b3940ef2 100644 --- a/cmd/gaia/testnets/STATUS.md +++ b/cmd/gaia/testnets/STATUS.md @@ -1,5 +1,19 @@ # TESTNET STATUS +## *June 13, 2018, 4:30 EST* - New Testnet Gaia-6002 + +- After fixing bugs from gaia-6001, especially [issue + #1197](https://github.com/cosmos/cosmos-sdk/issues/1197), we are announcing a + new testnet, Gaia-6002 +- Gaia-6002 has the same genesis file as Gaia-6001, just with the chain-id + updated + +## *June 13, 2018, 4:30 EST* - New Release + +- Released gaia + [v0.19.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.19.0) +- Includes various bug-fixes for staking found on Gaia-6001 + ## *June 13, 2018, 2:30 EST* - Published Postmortem of Gaia-6001 failure - A bug in the design of the staking data model caused a sanity check to fail diff --git a/cmd/gaia/testnets/gaia-6002/genesis.json b/cmd/gaia/testnets/gaia-6002/genesis.json new file mode 100644 index 000000000..7f53893a8 --- /dev/null +++ b/cmd/gaia/testnets/gaia-6002/genesis.json @@ -0,0 +1,1459 @@ +{ + "genesis_time": "2018-06-16T18:29:12.38288148Z", + "chain_id": "gaia-6002", + "consensus_params": { + "block_size_params": { + "max_bytes": 22020096, + "max_txs": 100000, + "max_gas": -1 + }, + "tx_size_params": { + "max_bytes": 10240, + "max_gas": -1 + }, + "block_gossip_params": { + "block_part_size_bytes": 65536 + }, + "evidence_params": { + "max_age": 100000 + } + }, + "validators": [ + { + "pub_key": { + "type": "AC26791624DE60", + "value": "TZTQnfqOsi89SeoXVnIw+tnFJnr4X8qVC0U8AsEmFk4=" + }, + "power": 1000, + "name": "adrian" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "CDF/8aD8Lt+ikR3LyCg9c7DwWBA51NH+MUkH7tzxrfY=" + }, + "power": 1000, + "name": "zaki" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "bIvXwf0qlOy0rO0SY/h8FfsqyW/AMpGL2yUhUNOY7hs=" + }, + "power": 100, + "name": "staked" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "RMwWTZsVdkq1heicNJb2fosy9Fls4NHxAHReiJvHl+8=" + }, + "power": 1000, + "name": "polsdam" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "SW12+WpGKUCO9oT2CV0CD5kUclbXjJHV1MjerLWB7Oc=" + }, + "power": 1000, + "name": "lino" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "NjjEQKUsq8F0gWxl3BoU2Li5n7hEz9H/LX80rfMxVyE=" + }, + "power": 100, + "name": "" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "7SaH/LyM+qdz9ovD/pvqIf2q7LC7tc5v0ZJxsA2CGTw=" + }, + "power": 1000, + "name": "iris" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "pY7eLF0Ez3yq495kIjag8mD67Q131np/ssagpEvlV2A=" + }, + "power": 1000, + "name": "pbostrom" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "RpX+xkwnCNw5DpBelscz4//TiODyC9RDiyIuD6NEwx0=" + }, + "power": 1000, + "name": "aurel" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "0aNTDL49987ZNRi3FtJIi0jk93ybHuYg1FjWrfP9H2o=" + }, + "power": 1000, + "name": "bucky" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "dcmCn+RZTBdwbCa4YqSnw/Va7xQloBw6vF87ItLwdM0=" + }, + "power": 100, + "name": "cwgoes" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "TNPLvN6f6QoSLJqGHzIfbraBoSw3emr9Sk2Us94M4gM=" + }, + "power": 1000, + "name": "bdnet" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "cvGYknYP9XMUzqlXZde7lRpvAp/kZiSRYHg66krJNxQ=" + }, + "power": 1000, + "name": "melea-trust" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "kol7Gj60Fct4X8T1rHLJQ0z/b14UqqSae8h1e37rLL8=" + }, + "power": 1000, + "name": "naruemon" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "rcl+kuM69Un/a7e+fQsQrCEtT1g04tFviOeq2GygSIw=" + }, + "power": 1000, + "name": "idoor" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "S9urD5q7je21qp5vEobiURdWrtJwvqMsfZGQhb8GOBQ=" + }, + "power": 1000, + "name": "ATEAM1" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "vSr94aI+zfF3D8Cr2VtCXPpfgj7t2ck8SlZxRsfn7gk=" + }, + "power": 1000, + "name": "figmatt" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "p5ijoVwp2zmA7RkXXvPl+yqdnlaWMwoV2pYIN8bDyFs=" + }, + "power": 1000, + "name": "jla-bsd" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "KOdWpo4aQFrLxVlkyc66p7m6mBNnPLmGuO4Z4L+CI1Y=" + }, + "power": 1000, + "name": "Gold" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "VfOsmcON77auerRc9zKwOR+CvL0sj1nA45hS2WqX1xE=" + }, + "power": 1000, + "name": "nylira" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "QYONAknaJqx3OKPSKraDrOPkx6xssezYtXVS84nZvZE=" + }, + "power": 1000, + "name": "BKCM" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "PyFJmNrUres3QOkj2BjxplCxrOF+HDFGohi3tRKsToY=" + }, + "power": 1000, + "name": "Dev's Validator" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "s4ER09+WeX10euzGyK7xDW7+myQVXt3Plup8IHUE4nk=" + }, + "power": 1000, + "name": "Staking Facilities" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "OPxj9edXgufNEjYNhZKqLgmYnK4A3nGw3rxaFQrHn24=" + }, + "power": 1000, + "name": "nuevax" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "Hi2rtbdJdQtOe3Kq4OoD/xkWJbIjIsUI9qgLQ6TlhiM=" + }, + "power": 1000, + "name": "vultr.guest" + }, + { + "pub_key": { + "type": "AC26791624DE60", + "value": "ah3JtmBA7gbxSiimPsLqQlV85gpNOUBJMvnxGx8eVlo=" + }, + "power": 1000, + "name": "forebole" + } + ], + "app_hash": "", + "app_state": { + "accounts": [ + { + "address": "04F01D5AF8DD248130BBE1D0780EA219CE479A9B", + "coins": [ + { + "denom": "devToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "FD8DA5F512A59A30F8698E3CA638D384A68DF977", + "coins": [ + { + "denom": "adrianToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "109720515B4F8C0858DA3521E448262334534FFD", + "coins": [ + { + "denom": "defaultToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "10B0899E05A486AE4E5589C39587DF7E9A185872", + "coins": [ + { + "denom": "aurelToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "328FBB8EA315D070DF908982A5F91A3618001D20", + "coins": [ + { + "denom": "cwgoesToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "4062DAFB9ACD4D91051B569CD4B19A26524B314B", + "coins": [ + { + "denom": "BKCMToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "42D76AF31E36EE23CA5366FBB9CE18610CCB9820", + "coins": [ + { + "denom": "BDToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "4B5BE759EB23B0D76C6A60636BD0E3111178794E", + "coins": [ + { + "denom": "suyuToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "5A007B81A25AF34B829B79DA508A26E12180BCDB", + "coins": [ + { + "denom": "linoToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "83C2788B74E1A410A4E62F1040EAE15F4B6EA3F5", + "coins": [ + { + "denom": "stakingToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "935E04662697134905706A4CCDB822AC6FC11C2E", + "coins": [ + { + "denom": "defaultToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "935E48ED79F1006ED135553768E1D9A768747CF6", + "coins": [ + { + "denom": "buckyToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "99FFAEE5BF6040EADA2F26548C4A702619797C9F", + "coins": [ + { + "denom": "kwunyeungToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + }, + { + "address": "9D5723057702E2090405AB5D3B48C45B9ABF4377", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "zachToken", + "amount": 1000 + } + ] + }, + { + "address": "A323EC45243D600204BA3D298E3C20322D08C84C", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "nuevaxToken", + "amount": 1000 + } + ] + }, + { + "address": "A9A3DADDC8BFFAD52BA51C8F4F2E9F62709412DC", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "dokiaToken", + "amount": 1000 + } + ] + }, + { + "address": "B6834D914FE50F0C743E6A012AB20438CFADFB95", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "forboleToken", + "amount": 1000 + } + ] + }, + { + "address": "B75C2C4309475C91E8DE271BC52CBAC408365B83", + "coins": [ + { + "denom": "steak", + "amount": 100 + }, + { + "denom": "pengToken", + "amount": 1000 + } + ] + }, + { + "address": "FD30D5C983FFEDEC069C3DDFCF270E41A556A86E", + "coins": [ + { + "denom": "steak", + "amount": 900 + }, + { + "denom": "faucetToken", + "amount": 10000000 + } + ] + }, + { + "address": "C0D0CA58C50B7B02A841E1B27D9A21D939754AC7", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "goldToken", + "amount": 100 + } + ] + }, + { + "address": "C5033FCFB67D7BD7B8546389F125710462D4FB6C", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "jlaToken", + "amount": 100 + } + ] + }, + { + "address": "C6CB038C98026D2F17241A3B3166AE7E9488D9AD", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "figmattToken", + "amount": 100 + } + ] + }, + { + "address": "D0861E3F22339C507B716102FDD5CA681EDE4F8E", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "ATEAM1Token", + "amount": 100 + } + ] + }, + { + "address": "D6545CB14FCA7840A295FB0566C27E4B9D526993", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "idoorToken", + "amount": 100 + } + ] + }, + { + "address": "D841E0DACF3994E6A40126F023F6F32F98A5D89E", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "naruemonToken", + "amount": 100 + } + ] + }, + { + "address": "ECE57661F0CDCF28EED257B72F86240E57F4A612", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "zakiToken", + "amount": 1000 + } + ] + }, + { + "address": "EFE597F7A90D3F3E7599B25259449628E2C4AFAD", + "coins": [ + { + "denom": "steak", + "amount": 50 + }, + { + "denom": "trustToken", + "amount": 1000 + } + ] + }, + { + "address": "FA929191B04C5DB222AFC6F15C63EF48CCC864C5", + "coins": [ + { + "denom": "poldsamToken", + "amount": 1000 + }, + { + "denom": "steak", + "amount": 50 + } + ] + } + ], + "stake": { + "pool": { + "loose_unbonded_tokens": 2300, + "unbonded_tokens": 0, + "unbonding_tokens": 0, + "bonded_tokens": 23300, + "unbonded_shares": "0", + "unbonding_shares": "0", + "bonded_shares": "23300", + "inflation_last_time": 0, + "inflation": "9012837/100000000", + "date_last_commission_reset": 0, + "prev_bonded_shares": "0" + }, + "params": { + "inflation_rate_change": "13/100", + "inflation_max": "1/5", + "inflation_min": "7/100", + "goal_bonded": "67/100", + "max_validators": 100, + "bond_denom": "steak" + }, + "bonds": [ + { + "delegator_addr": "04F01D5AF8DD248130BBE1D0780EA219CE479A9B", + "validator_addr": "04F01D5AF8DD248130BBE1D0780EA219CE479A9B", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "109720515B4F8C0858DA3521E448262334534FFD", + "validator_addr": "109720515B4F8C0858DA3521E448262334534FFD", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "10B0899E05A486AE4E5589C39587DF7E9A185872", + "validator_addr": "10B0899E05A486AE4E5589C39587DF7E9A185872", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "328FBB8EA315D070DF908982A5F91A3618001D20", + "validator_addr": "328FBB8EA315D070DF908982A5F91A3618001D20", + "shares": "100", + "height": 0 + }, + { + "delegator_addr": "4062DAFB9ACD4D91051B569CD4B19A26524B314B", + "validator_addr": "4062DAFB9ACD4D91051B569CD4B19A26524B314B", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "42D76AF31E36EE23CA5366FBB9CE18610CCB9820", + "validator_addr": "42D76AF31E36EE23CA5366FBB9CE18610CCB9820", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "4B5BE759EB23B0D76C6A60636BD0E3111178794E", + "validator_addr": "4B5BE759EB23B0D76C6A60636BD0E3111178794E", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "5A007B81A25AF34B829B79DA508A26E12180BCDB", + "validator_addr": "5A007B81A25AF34B829B79DA508A26E12180BCDB", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "83C2788B74E1A410A4E62F1040EAE15F4B6EA3F5", + "validator_addr": "83C2788B74E1A410A4E62F1040EAE15F4B6EA3F5", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "935E04662697134905706A4CCDB822AC6FC11C2E", + "validator_addr": "935E04662697134905706A4CCDB822AC6FC11C2E", + "shares": "100", + "height": 0 + }, + { + "delegator_addr": "935E48ED79F1006ED135553768E1D9A768747CF6", + "validator_addr": "935E48ED79F1006ED135553768E1D9A768747CF6", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "9D5723057702E2090405AB5D3B48C45B9ABF4377", + "validator_addr": "9D5723057702E2090405AB5D3B48C45B9ABF4377", + "shares": "100", + "height": 0 + }, + { + "delegator_addr": "A323EC45243D600204BA3D298E3C20322D08C84C", + "validator_addr": "A323EC45243D600204BA3D298E3C20322D08C84C", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "A9A3DADDC8BFFAD52BA51C8F4F2E9F62709412DC", + "validator_addr": "A9A3DADDC8BFFAD52BA51C8F4F2E9F62709412DC", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "B6834D914FE50F0C743E6A012AB20438CFADFB95", + "validator_addr": "B6834D914FE50F0C743E6A012AB20438CFADFB95", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "B75C2C4309475C91E8DE271BC52CBAC408365B83", + "validator_addr": "B75C2C4309475C91E8DE271BC52CBAC408365B83", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "C0D0CA58C50B7B02A841E1B27D9A21D939754AC7", + "validator_addr": "C0D0CA58C50B7B02A841E1B27D9A21D939754AC7", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "C5033FCFB67D7BD7B8546389F125710462D4FB6C", + "validator_addr": "C5033FCFB67D7BD7B8546389F125710462D4FB6C", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "C6CB038C98026D2F17241A3B3166AE7E9488D9AD", + "validator_addr": "C6CB038C98026D2F17241A3B3166AE7E9488D9AD", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "D0861E3F22339C507B716102FDD5CA681EDE4F8E", + "validator_addr": "D0861E3F22339C507B716102FDD5CA681EDE4F8E", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "D6545CB14FCA7840A295FB0566C27E4B9D526993", + "validator_addr": "D6545CB14FCA7840A295FB0566C27E4B9D526993", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "D841E0DACF3994E6A40126F023F6F32F98A5D89E", + "validator_addr": "D841E0DACF3994E6A40126F023F6F32F98A5D89E", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "FD8DA5F512A59A30F8698E3CA638D384A68DF977", + "validator_addr": "FD8DA5F512A59A30F8698E3CA638D384A68DF977", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "ECE57661F0CDCF28EED257B72F86240E57F4A612", + "validator_addr": "ECE57661F0CDCF28EED257B72F86240E57F4A612", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "EFE597F7A90D3F3E7599B25259449628E2C4AFAD", + "validator_addr": "EFE597F7A90D3F3E7599B25259449628E2C4AFAD", + "shares": "1000", + "height": 0 + }, + { + "delegator_addr": "FA929191B04C5DB222AFC6F15C63EF48CCC864C5", + "validator_addr": "FA929191B04C5DB222AFC6F15C63EF48CCC864C5", + "shares": "1000", + "height": 0 + } + ], + "validators": [ + { + "owner": "04F01D5AF8DD248130BBE1D0780EA219CE479A9B", + "pub_key": { + "type": "AC26791624DE60", + "value": "PyFJmNrUres3QOkj2BjxplCxrOF+HDFGohi3tRKsToY=" + }, + "description": { + "moniker": "Dev's Validator", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "109720515B4F8C0858DA3521E448262334534FFD", + "pub_key": { + "type": "AC26791624DE60", + "value": "pY7eLF0Ez3yq495kIjag8mD67Q131np/ssagpEvlV2A=" + }, + "description": { + "moniker": "", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "10B0899E05A486AE4E5589C39587DF7E9A185872", + "pub_key": { + "type": "AC26791624DE60", + "value": "RpX+xkwnCNw5DpBelscz4//TiODyC9RDiyIuD6NEwx0=" + }, + "description": { + "moniker": "aurel", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "328FBB8EA315D070DF908982A5F91A3618001D20", + "pub_key": { + "type": "AC26791624DE60", + "value": "dcmCn+RZTBdwbCa4YqSnw/Va7xQloBw6vF87ItLwdM0=" + }, + "description": { + "moniker": "cwgoes", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "100" + }, + "delegator_shares": "100", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "4062DAFB9ACD4D91051B569CD4B19A26524B314B", + "pub_key": { + "type": "AC26791624DE60", + "value": "QYONAknaJqx3OKPSKraDrOPkx6xssezYtXVS84nZvZE=" + }, + "description": { + "moniker": "BKCM", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "42D76AF31E36EE23CA5366FBB9CE18610CCB9820", + "pub_key": { + "type": "AC26791624DE60", + "value": "TNPLvN6f6QoSLJqGHzIfbraBoSw3emr9Sk2Us94M4gM=" + }, + "description": { + "moniker": "bdnet", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "4B5BE759EB23B0D76C6A60636BD0E3111178794E", + "pub_key": { + "type": "AC26791624DE60", + "value": "7SaH/LyM+qdz9ovD/pvqIf2q7LC7tc5v0ZJxsA2CGTw=" + }, + "description": { + "moniker": "suyu", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "5A007B81A25AF34B829B79DA508A26E12180BCDB", + "pub_key": { + "type": "AC26791624DE60", + "value": "SW12+WpGKUCO9oT2CV0CD5kUclbXjJHV1MjerLWB7Oc=" + }, + "description": { + "moniker": "lino", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "83C2788B74E1A410A4E62F1040EAE15F4B6EA3F5", + "pub_key": { + "type": "AC26791624DE60", + "value": "s4ER09+WeX10euzGyK7xDW7+myQVXt3Plup8IHUE4nk=" + }, + "description": { + "moniker": "Staking Facilities", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "935E04662697134905706A4CCDB822AC6FC11C2E", + "pub_key": { + "type": "AC26791624DE60", + "value": "bIvXwf0qlOy0rO0SY/h8FfsqyW/AMpGL2yUhUNOY7hs=" + }, + "description": { + "moniker": "default", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "100" + }, + "delegator_shares": "100", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "935E48ED79F1006ED135553768E1D9A768747CF6", + "pub_key": { + "type": "AC26791624DE60", + "value": "0aNTDL49987ZNRi3FtJIi0jk93ybHuYg1FjWrfP9H2o=" + }, + "description": { + "moniker": "bucky", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "9D5723057702E2090405AB5D3B48C45B9ABF4377", + "pub_key": { + "type": "AC26791624DE60", + "value": "NjjEQKUsq8F0gWxl3BoU2Li5n7hEz9H/LX80rfMxVyE=" + }, + "description": { + "moniker": "zach", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "100" + }, + "delegator_shares": "100", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "A323EC45243D600204BA3D298E3C20322D08C84C", + "pub_key": { + "type": "AC26791624DE60", + "value": "OPxj9edXgufNEjYNhZKqLgmYnK4A3nGw3rxaFQrHn24=" + }, + "description": { + "moniker": "nuevax", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "A9A3DADDC8BFFAD52BA51C8F4F2E9F62709412DC", + "pub_key": { + "type": "AC26791624DE60", + "value": "Hi2rtbdJdQtOe3Kq4OoD/xkWJbIjIsUI9qgLQ6TlhiM=" + }, + "description": { + "moniker": "vultr.guest", + "identity": "", + "website": "https://ion.dokia.capital/", + "details": "DokiaValidator" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "B6834D914FE50F0C743E6A012AB20438CFADFB95", + "pub_key": { + "type": "AC26791624DE60", + "value": "ah3JtmBA7gbxSiimPsLqQlV85gpNOUBJMvnxGx8eVlo=" + }, + "description": { + "moniker": "forbole", + "identity": "", + "website": "https://www.forbole.com", + "details": "Recommend. Refer. Reward" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "B75C2C4309475C91E8DE271BC52CBAC408365B83", + "pub_key": { + "type": "AC26791624DE60", + "value": "VfOsmcON77auerRc9zKwOR+CvL0sj1nA45hS2WqX1xE=" + }, + "description": { + "moniker": "nylira", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "C0D0CA58C50B7B02A841E1B27D9A21D939754AC7", + "pub_key": { + "type": "AC26791624DE60", + "value": "KOdWpo4aQFrLxVlkyc66p7m6mBNnPLmGuO4Z4L+CI1Y=" + }, + "description": { + "moniker": "Gold", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "C5033FCFB67D7BD7B8546389F125710462D4FB6C", + "pub_key": { + "type": "AC26791624DE60", + "value": "p5ijoVwp2zmA7RkXXvPl+yqdnlaWMwoV2pYIN8bDyFs=" + }, + "description": { + "moniker": "jla-bsd", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "C6CB038C98026D2F17241A3B3166AE7E9488D9AD", + "pub_key": { + "type": "AC26791624DE60", + "value": "vSr94aI+zfF3D8Cr2VtCXPpfgj7t2ck8SlZxRsfn7gk=" + }, + "description": { + "moniker": "figmatt", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "D0861E3F22339C507B716102FDD5CA681EDE4F8E", + "pub_key": { + "type": "AC26791624DE60", + "value": "S9urD5q7je21qp5vEobiURdWrtJwvqMsfZGQhb8GOBQ=" + }, + "description": { + "moniker": "ATEAM1", + "identity": "", + "website": "", + "details": "ATEAM1" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "D6545CB14FCA7840A295FB0566C27E4B9D526993", + "pub_key": { + "type": "AC26791624DE60", + "value": "rcl+kuM69Un/a7e+fQsQrCEtT1g04tFviOeq2GygSIw=" + }, + "description": { + "moniker": "idoor", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "D841E0DACF3994E6A40126F023F6F32F98A5D89E", + "pub_key": { + "type": "AC26791624DE60", + "value": "kol7Gj60Fct4X8T1rHLJQ0z/b14UqqSae8h1e37rLL8=" + }, + "description": { + "moniker": "naruemon", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "FD8DA5F512A59A30F8698E3CA638D384A68DF977", + "pub_key": { + "type": "AC26791624DE60", + "value": "TZTQnfqOsi89SeoXVnIw+tnFJnr4X8qVC0U8AsEmFk4=" + }, + "description": { + "moniker": "Adrian Brink - Cryptium Labs", + "identity": "", + "website": "https://cryptium.ch", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "ECE57661F0CDCF28EED257B72F86240E57F4A612", + "pub_key": { + "type": "AC26791624DE60", + "value": "CDF/8aD8Lt+ikR3LyCg9c7DwWBA51NH+MUkH7tzxrfY=" + }, + "description": { + "moniker": "zaki", + "identity": "", + "website": "", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "EFE597F7A90D3F3E7599B25259449628E2C4AFAD", + "pub_key": { + "type": "AC26791624DE60", + "value": "cvGYknYP9XMUzqlXZde7lRpvAp/kZiSRYHg66krJNxQ=" + }, + "description": { + "moniker": "trust", + "identity": "", + "website": "http://cosmos-trust.com", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + }, + { + "owner": "FA929191B04C5DB222AFC6F15C63EF48CCC864C5", + "pub_key": { + "type": "AC26791624DE60", + "value": "RMwWTZsVdkq1heicNJb2fosy9Fls4NHxAHReiJvHl+8=" + }, + "description": { + "moniker": "proof-of-audit", + "identity": "", + "website": "https://proof-of-audit.com", + "details": "" + }, + "revoked": false, + "pool_shares": { + "status": 2, + "amount": "1000" + }, + "delegator_shares": "1000", + "bond_height": 0, + "bond_intra_tx_counter": 0, + "commision": "0/1", + "commission_max": "0/1", + "commission_change_rate": "0/1", + "commission_change_rate_today": "0/1", + "prev_bonded_shares": "0/1" + } + ] + } + } +} From 6b9f8510323b701c8bb080d383c52dc23905ca00 Mon Sep 17 00:00:00 2001 From: gamarin2 Date: Wed, 13 Jun 2018 11:11:55 +0200 Subject: [PATCH 19/27] Add update testnet guide to status --- cmd/gaia/testnets/STATUS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/gaia/testnets/STATUS.md b/cmd/gaia/testnets/STATUS.md index 9b3940ef2..1e3a67704 100644 --- a/cmd/gaia/testnets/STATUS.md +++ b/cmd/gaia/testnets/STATUS.md @@ -7,6 +7,7 @@ new testnet, Gaia-6002 - Gaia-6002 has the same genesis file as Gaia-6001, just with the chain-id updated +- Update from previous testnet [here](https://github.com/cosmos/cosmos-sdk/tree/master/cmd/gaia/testnets#upgrading-from-previous-testnet) ## *June 13, 2018, 4:30 EST* - New Release From 0fa28cac3bae82b3c63ff8203b85aa98327e3da3 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Wed, 13 Jun 2018 14:44:41 -0700 Subject: [PATCH 20/27] Merge PR #1248: Sync to tendermint develop's latest revision --- Gopkg.lock | 9 ++++----- Gopkg.toml | 4 ++-- server/tm_cmds.go | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 07a3c6d5b..701f383af 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -267,8 +267,8 @@ "server", "types" ] - revision = "ebee2fe114020aa49c70bbbae50b7079fc7e7b90" - version = "v0.11.0" + revision = "198dccf0ddfd1bb176f87657e3286a05a6ed9540" + version = "v0.12.0" [[projects]] branch = "master" @@ -347,8 +347,7 @@ "types", "version" ] - revision = "27bd1deabe4ba6a2d9b463b8f3e3f1e31b993e61" - version = "v0.20.0" + revision = "696e8c6f9e950eec15f150f314d2dd9ddf6bc601" [[projects]] branch = "develop" @@ -463,6 +462,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "31f69b235b2d8f879a215c9e8ca0919adc62d21f6830b17931a3a0efb058721f" + inputs-digest = "d02a24bcfd8bded901e1b154e19b81ff797d3921046ede19d1d11eed61e871e7" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 77de1b78d..49bbf9f79 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -54,7 +54,7 @@ [[constraint]] name = "github.com/tendermint/abci" - version = "=0.11.0" + version = "=0.12.0" [[constraint]] name = "github.com/tendermint/go-crypto" @@ -70,7 +70,7 @@ [[constraint]] name = "github.com/tendermint/tendermint" - version = "=0.20.0" + revision = "696e8c6f9e950eec15f150f314d2dd9ddf6bc601" [[override]] name = "github.com/tendermint/tmlibs" diff --git a/server/tm_cmds.go b/server/tm_cmds.go index 7dccaf531..25d417a66 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -72,7 +72,7 @@ func UnsafeResetAllCmd(ctx *Context) *cobra.Command { Short: "Reset blockchain database, priv_validator.json file, and the logger", RunE: func(cmd *cobra.Command, args []string) error { cfg := ctx.Config - tcmd.ResetAll(cfg.DBDir(), cfg.PrivValidatorFile(), ctx.Logger) + tcmd.ResetAll(cfg.DBDir(), cfg.P2P.AddrBookFile(), cfg.PrivValidatorFile(), ctx.Logger) return nil }, } From 3cbb06015e88892e65ceccbcfd5c2808c5f5d34c Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 13 Jun 2018 15:17:18 -0700 Subject: [PATCH 21/27] add gaiadebug readme --- cmd/gaia/cmd/gaiadebug/README.md | 35 ++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 cmd/gaia/cmd/gaiadebug/README.md diff --git a/cmd/gaia/cmd/gaiadebug/README.md b/cmd/gaia/cmd/gaiadebug/README.md new file mode 100644 index 000000000..c2f0b8bc0 --- /dev/null +++ b/cmd/gaia/cmd/gaiadebug/README.md @@ -0,0 +1,35 @@ +# Gaiadebug + +Simple tool for simple debugging. + +We try to accept both hex and base64 formats and provide a useful response. + +Note we often encode bytes as hex in the logs, but as base64 in the JSON. + +## Pubkeys + +The following give the same result: + +``` +gaiadebug pubkey TZTQnfqOsi89SeoXVnIw+tnFJnr4X8qVC0U8AsEmFk4= +gaiadebug pubkey 4D94D09DFA8EB22F3D49EA17567230FAD9C5267AF85FCA950B453C02C126164E +``` + +## Txs + +Pass in a hex/base64 tx and get back the full JSON + +``` +gaiadebug tx +``` + +## Hack + +This is a command with boilerplate for using Go as a scripting language to hack +on an existing Gaia state. + +Currently we have an example for the state of gaia-6001 after it +[crashed](https://github.com/cosmos/cosmos-sdk/blob/master/cmd/gaia/testnets/STATUS.md#june-13-2018-230-est---published-postmortem-of-gaia-6001-failure). +If you run `gaiadebug hack $HOME/.gaiad` on that +state, it will do a binary search on the state history to find when the state +invariant was violated. From 0180a5b48ebbae233ae0fb72e66cb77217fc0b2b Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 13 Jun 2018 15:20:54 -0700 Subject: [PATCH 22/27] testnets/status: gaia-6002 is live --- cmd/gaia/testnets/STATUS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmd/gaia/testnets/STATUS.md b/cmd/gaia/testnets/STATUS.md index 1e3a67704..0848f27ef 100644 --- a/cmd/gaia/testnets/STATUS.md +++ b/cmd/gaia/testnets/STATUS.md @@ -1,5 +1,11 @@ # TESTNET STATUS +## *June 13, 2018, 17:00 EST* - Gaia-6002 is making blocks! + +- Gaia-6002 is live and making blocks +- Absent validators have been slashed and revoked +- Currently live with 17 validators + ## *June 13, 2018, 4:30 EST* - New Testnet Gaia-6002 - After fixing bugs from gaia-6001, especially [issue From bd362ee590f35436a02b04d8a482392dbe941713 Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Wed, 13 Jun 2018 15:13:51 -0700 Subject: [PATCH 23/27] Merge PR #1090: Switch away from ephemeral ports * Switch ports 4665x to be 2655x This is done so the default ports aren't in the linux kernel's default ephemeral port range. * Missed one doc file, change dep so gaiad works * Update changelog, fix Gopkg.lock --- CHANGELOG.md | 5 ++++- baseapp/helpers.go | 2 +- client/flags.go | 4 ++-- client/lcd/root.go | 2 +- client/rpc/block.go | 2 +- client/rpc/root.go | 2 +- client/rpc/status.go | 2 +- client/rpc/validators.go | 2 +- client/tx/query.go | 2 +- client/tx/search.go | 2 +- docs/old/basecoin/basics.rst | 2 +- docs/old/basecoin/extensions.rst | 2 +- docs/old/staking/local-testnet.rst | 12 ++++++------ docs/old/staking/public-testnet.rst | 2 +- docs/sdk/lcd-rest-api.yaml | 2 +- docs/staking/intro.rst | 2 +- docs/staking/testnet.rst | 2 +- examples/kvstore/main.go | 2 +- networks/remote/ansible/status.yml | 2 +- server/init.go | 2 +- server/start.go | 2 +- x/ibc/client/cli/README.md | 4 ++-- x/ibc/client/cli/relay.go | 2 +- 23 files changed, 33 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdfd1895f..b37db2292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ *TBD* +BREAKING CHANGES +* Change default ports from 466xx to 266xx + ## 0.19.0 *June 13, 2018* @@ -24,7 +27,7 @@ IMPROVEMENTS FIXES * Fixes consensus fault on testnet - see postmortem [here](https://github.com/cosmos/cosmos-sdk/issues/1197#issuecomment-396823021) -* [x/stake] bonded inflation removed, non-bonded inflation partially implemented +* [x/stake] bonded inflation removed, non-bonded inflation partially implemented * [lcd] Switch to bech32 for addresses on all human readable inputs and outputs * [lcd] fixed tx indexing/querying * [cli] Added `--gas` flag to specify transaction gas limit diff --git a/baseapp/helpers.go b/baseapp/helpers.go index 43bd654d6..b7ac45d9a 100644 --- a/baseapp/helpers.go +++ b/baseapp/helpers.go @@ -10,7 +10,7 @@ import ( func RunForever(app abci.Application) { // Start the ABCI server - srv, err := server.NewServer("0.0.0.0:46658", "socket", app) + srv, err := server.NewServer("0.0.0.0:26658", "socket", app) if err != nil { cmn.Exit(err.Error()) } diff --git a/client/flags.go b/client/flags.go index 4991b9a77..5af588bd4 100644 --- a/client/flags.go +++ b/client/flags.go @@ -25,7 +25,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command { // TODO: make this default false when we support proofs c.Flags().Bool(FlagTrustNode, true, "Don't verify proofs for responses") c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") - c.Flags().String(FlagNode, "tcp://localhost:46657", ": to tendermint rpc interface for this chain") + c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") c.Flags().Int64(FlagHeight, 0, "block height to query, omit to get most recent provable block") } return cmds @@ -39,7 +39,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx") c.Flags().String(FlagFee, "", "Fee to pay along with transaction") c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") - c.Flags().String(FlagNode, "tcp://localhost:46657", ": to tendermint rpc interface for this chain") + c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") c.Flags().Int64(FlagGas, 200000, "gas limit to set per-transaction") } return cmds diff --git a/client/lcd/root.go b/client/lcd/root.go index 66491dfec..7d819740c 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -57,7 +57,7 @@ func ServeCommand(cdc *wire.Codec) *cobra.Command { cmd.Flags().StringP(flagListenAddr, "a", "tcp://localhost:1317", "Address for server to listen on") cmd.Flags().String(flagCORS, "", "Set to domains that can make CORS requests (* for all)") cmd.Flags().StringP(client.FlagChainID, "c", "", "ID of chain we connect to") - cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") return cmd } diff --git a/client/rpc/block.go b/client/rpc/block.go index 693298bb8..3244e8d12 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -24,7 +24,7 @@ func BlockCommand() *cobra.Command { Args: cobra.MaximumNArgs(1), RunE: printBlock, } - cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") // TODO: change this to false when we can cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") cmd.Flags().StringSlice(flagSelect, []string{"header", "tx"}, "Fields to return (header|txs|results)") diff --git a/client/rpc/root.go b/client/rpc/root.go index 63ba64a18..bb5a162a7 100644 --- a/client/rpc/root.go +++ b/client/rpc/root.go @@ -36,7 +36,7 @@ func initClientCommand() *cobra.Command { RunE: todoNotImplemented, } cmd.Flags().StringP(client.FlagChainID, "c", "", "ID of chain we connect to") - cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") cmd.Flags().String(flagGenesis, "", "Genesis file to verify header validity") cmd.Flags().String(flagCommit, "", "File with trusted and signed header") cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)") diff --git a/client/rpc/status.go b/client/rpc/status.go index 70d6628e4..96517cc19 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -18,7 +18,7 @@ func statusCommand() *cobra.Command { Short: "Query remote node for status", RunE: printNodeStatus, } - cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") return cmd } diff --git a/client/rpc/validators.go b/client/rpc/validators.go index f8835d737..d88fb7317 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -24,7 +24,7 @@ func ValidatorCommand() *cobra.Command { Args: cobra.MaximumNArgs(1), RunE: printValidators, } - cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") // TODO: change this to false when we can cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") return cmd diff --git a/client/tx/query.go b/client/tx/query.go index 7673dd38d..86439b317 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -42,7 +42,7 @@ func QueryTxCmd(cdc *wire.Codec) *cobra.Command { }, } - cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") // TODO: change this to false when we can cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") diff --git a/client/tx/search.go b/client/tx/search.go index b3ebbf34e..7c75be8f7 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -43,7 +43,7 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command { }, } - cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:46657", "Node to connect to") + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") // TODO: change this to false once proofs built in cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") diff --git a/docs/old/basecoin/basics.rst b/docs/old/basecoin/basics.rst index d3627b2b1..3b61dd6e5 100644 --- a/docs/old/basecoin/basics.rst +++ b/docs/old/basecoin/basics.rst @@ -78,7 +78,7 @@ window. Here run: :: - basecli init --node=tcp://localhost:46657 --genesis=$HOME/.basecoin/genesis.json + basecli init --node=tcp://localhost:26657 --genesis=$HOME/.basecoin/genesis.json If you provide the genesis file to basecli, it can calculate the proper chainID and validator hash. Basecli needs to get this information from diff --git a/docs/old/basecoin/extensions.rst b/docs/old/basecoin/extensions.rst index c1db864a3..6f31222de 100644 --- a/docs/old/basecoin/extensions.rst +++ b/docs/old/basecoin/extensions.rst @@ -49,7 +49,7 @@ initialize the light-client and send a transaction: :: - countercli init --node=tcp://localhost:46657 --genesis=$HOME/.counter/genesis.json + countercli init --node=tcp://localhost:26657 --genesis=$HOME/.counter/genesis.json YOU=$(countercli keys get friend | awk '{print $2}') countercli tx send --name=cool --amount=1000mycoin --to=$YOU --sequence=1 diff --git a/docs/old/staking/local-testnet.rst b/docs/old/staking/local-testnet.rst index e3f69bded..b8d30d2e2 100644 --- a/docs/old/staking/local-testnet.rst +++ b/docs/old/staking/local-testnet.rst @@ -39,18 +39,18 @@ and ports. It should look like: :: - proxy_app = "tcp://127.0.0.1:46668" + proxy_app = "tcp://127.0.0.1:26668" moniker = "anonymous" fast_sync = true db_backend = "leveldb" log_level = "state:info,*:error" [rpc] - laddr = "tcp://0.0.0.0:46667" + laddr = "tcp://0.0.0.0:26667" [p2p] - laddr = "tcp://0.0.0.0:46666" - seeds = "0.0.0.0:46656" + laddr = "tcp://0.0.0.0:26666" + seeds = "0.0.0.0:26656" Start Nodes ----------- @@ -69,14 +69,14 @@ account: :: - gaia client init --chain-id=gaia-test --node=tcp://localhost:46657 + gaia client init --chain-id=gaia-test --node=tcp://localhost:26657 gaia client query account 5D93A6059B6592833CBC8FA3DA90EE0382198985 To see what tendermint considers the validator set is, use: :: - curl localhost:46657/validators + curl localhost:26657/validators and compare the information in this file: ``~/.gaia1/priv_validator.json``. The ``address`` and ``pub_key`` fields should match. diff --git a/docs/old/staking/public-testnet.rst b/docs/old/staking/public-testnet.rst index 640163642..587c025b1 100644 --- a/docs/old/staking/public-testnet.rst +++ b/docs/old/staking/public-testnet.rst @@ -49,7 +49,7 @@ Finally, let's initialize the gaia client to interact with the testnet: :: - gaia client init --chain-id=gaia-1 --node=tcp://localhost:46657 + gaia client init --chain-id=gaia-1 --node=tcp://localhost:26657 and check our balance: diff --git a/docs/sdk/lcd-rest-api.yaml b/docs/sdk/lcd-rest-api.yaml index 3008d7f73..6a5be2394 100644 --- a/docs/sdk/lcd-rest-api.yaml +++ b/docs/sdk/lcd-rest-api.yaml @@ -41,7 +41,7 @@ paths: type: string listen_addr: type: string - example: 192.168.56.1:46656 + example: 192.168.56.1:26656 version: description: Tendermint version type: string diff --git a/docs/staking/intro.rst b/docs/staking/intro.rst index 00a68811a..3ed20852b 100644 --- a/docs/staking/intro.rst +++ b/docs/staking/intro.rst @@ -291,7 +291,7 @@ To confirm for certain the new validator is active, ask the tendermint node: :: - curl localhost:46657/validators + curl localhost:26657/validators If you now kill either node, blocks will stop streaming in, because there aren't enough validators online. Turn it back on and they will diff --git a/docs/staking/testnet.rst b/docs/staking/testnet.rst index 4fca09c4a..0e86a952d 100644 --- a/docs/staking/testnet.rst +++ b/docs/staking/testnet.rst @@ -66,7 +66,7 @@ To confirm for certain the new validator is active, check tendermint: :: - curl localhost:46657/validators + curl localhost:26657/validators Finally, to relinquish all your power, unbond some coins. You should see your VotingPower reduce and your account balance increase. diff --git a/examples/kvstore/main.go b/examples/kvstore/main.go index 856538f63..6835f5407 100644 --- a/examples/kvstore/main.go +++ b/examples/kvstore/main.go @@ -50,7 +50,7 @@ func main() { } // Start the ABCI server - srv, err := server.NewServer("0.0.0.0:46658", "socket", baseApp) + srv, err := server.NewServer("0.0.0.0:26658", "socket", baseApp) if err != nil { fmt.Println(err) os.Exit(1) diff --git a/networks/remote/ansible/status.yml b/networks/remote/ansible/status.yml index fffba41fc..d0b89d13f 100644 --- a/networks/remote/ansible/status.yml +++ b/networks/remote/ansible/status.yml @@ -9,7 +9,7 @@ - name: Gather status uri: body_format: json - url: "http://{{inventory_hostname}}:46657/status" + url: "http://{{inventory_hostname}}:26657/status" register: status - name: Print status diff --git a/server/init.go b/server/init.go index 512751bed..5e5a73fbe 100644 --- a/server/init.go +++ b/server/init.go @@ -255,7 +255,7 @@ func processGenTxs(genTxsDir string, cdc *wire.Codec, appInit AppInit) ( if len(persistentPeers) == 0 { comma = "" } - persistentPeers += fmt.Sprintf("%s%s@%s:46656", comma, genTx.NodeID, genTx.IP) + persistentPeers += fmt.Sprintf("%s%s@%s:26656", comma, genTx.NodeID, genTx.IP) } return diff --git a/server/start.go b/server/start.go index 5224ef30d..77a18fffb 100644 --- a/server/start.go +++ b/server/start.go @@ -37,7 +37,7 @@ func StartCmd(ctx *Context, appCreator AppCreator) *cobra.Command { // basic flags for abci app cmd.Flags().Bool(flagWithTendermint, true, "run abci app embedded in-process with tendermint") - cmd.Flags().String(flagAddress, "tcp://0.0.0.0:46658", "Listen address") + cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address") // AddNodeFlags adds support for all tendermint-specific command line options tcmd.AddNodeFlags(cmd) diff --git a/x/ibc/client/cli/README.md b/x/ibc/client/cli/README.md index ed9652fa3..e91fb55bf 100644 --- a/x/ibc/client/cli/README.md +++ b/x/ibc/client/cli/README.md @@ -58,7 +58,7 @@ I[04-02|14:09:14.453] Generated genesis file module=main p } > ADDR2=DC26002735D3AA9573707CFA6D77C12349E49868 > ID2=test-chain-4XHTPn -> NODE2=tcp://0.0.0.0:46657 +> NODE2=tcp://0.0.0.0:26657 > basecli keys add key2 --recover Enter a passphrase for your key: Repeat the passphrase: @@ -70,7 +70,7 @@ key2 DC26002735D3AA9573707CFA6D77C12349E49868 > basecoind start --home ~/.chain1 --address tcp://0.0.0.0:36658 --rpc.laddr tcp://0.0.0.0:36657 --p2p.laddr tcp://0.0.0.0:36656 ... -> basecoind start --home ~/.chain2 # --address tcp://0.0.0.0:46658 --rpc.laddr tcp://0.0.0.0:46657 --p2p.laddr tcp://0.0.0.0:46656 +> basecoind start --home ~/.chain2 # --address tcp://0.0.0.0:26658 --rpc.laddr tcp://0.0.0.0:26657 --p2p.laddr tcp://0.0.0.0:26656 ... ``` ## Check balance diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index 96c5b08cc..9742f83f9 100644 --- a/x/ibc/client/cli/relay.go +++ b/x/ibc/client/cli/relay.go @@ -54,7 +54,7 @@ func IBCRelayCmd(cdc *wire.Codec) *cobra.Command { } cmd.Flags().String(FlagFromChainID, "", "Chain ID for ibc node to check outgoing packets") - cmd.Flags().String(FlagFromChainNode, "tcp://localhost:46657", ": to tendermint rpc interface for this chain") + cmd.Flags().String(FlagFromChainNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") cmd.Flags().String(FlagToChainID, "", "Chain ID for ibc node to broadcast incoming packets") cmd.Flags().String(FlagToChainNode, "tcp://localhost:36657", ": to tendermint rpc interface for this chain") From ff34cbc8bc9684a8297a5f2238a36101867da7b0 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 13 Jun 2018 17:41:01 -0700 Subject: [PATCH 24/27] gaia/cli_test: remove sleeps --- cmd/gaia/cli_test/cli_test.go | 13 ++++---- tests/util.go | 56 +++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 1868452a6..3eee20c85 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "testing" - "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -50,7 +49,7 @@ func TestGaiaCLISend(t *testing.T) { assert.Equal(t, int64(50), fooAcc.GetCoins().AmountOf("steak")) executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), pass) - time.Sleep(time.Second * 2) // waiting for some blocks to pass + tests.WaitForNextHeightTM(port) barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags)) assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak")) @@ -59,7 +58,7 @@ func TestGaiaCLISend(t *testing.T) { // test autosequencing executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), pass) - time.Sleep(time.Second * 2) // waiting for some blocks to pass + tests.WaitForNextHeightTM(port) barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags)) assert.Equal(t, int64(20), barAcc.GetCoins().AmountOf("steak")) @@ -96,7 +95,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { require.NoError(t, err) executeWrite(t, fmt.Sprintf("gaiacli send %v --amount=10steak --to=%v --name=foo", flags, barCech), pass) - time.Sleep(time.Second * 3) // waiting for some blocks to pass + tests.WaitForNextHeightTM(port) barAcc := executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags)) assert.Equal(t, int64(10), barAcc.GetCoins().AmountOf("steak")) @@ -112,7 +111,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { cvStr += fmt.Sprintf(" --moniker=%v", "bar-vally") executeWrite(t, cvStr, pass) - time.Sleep(time.Second * 3) // waiting for some blocks to pass + tests.WaitForNextHeightTM(port) barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags)) require.Equal(t, int64(8), barAcc.GetCoins().AmountOf("steak"), "%v", barAcc) @@ -131,7 +130,7 @@ func TestGaiaCLICreateValidator(t *testing.T) { t.Log(fmt.Sprintf("debug unbondStr: %v\n", unbondStr)) executeWrite(t, unbondStr, pass) - time.Sleep(time.Second * 3) // waiting for some blocks to pass + tests.WaitForNextHeightTM(port) barAcc = executeGetAccount(t, fmt.Sprintf("gaiacli account %v %v", barCech, flags)) require.Equal(t, int64(9), barAcc.GetCoins().AmountOf("steak"), "%v", barAcc) @@ -150,6 +149,8 @@ func executeWrite(t *testing.T, cmdStr string, writes ...string) { require.NoError(t, err) } proc.Wait() + // bz := proc.StdoutBuffer.Bytes() + // fmt.Println("EXEC WRITE", string(bz)) } func executeInit(t *testing.T, cmdStr string) (chainID string) { diff --git a/tests/util.go b/tests/util.go index 292cbab06..c47d9907f 100644 --- a/tests/util.go +++ b/tests/util.go @@ -7,16 +7,61 @@ import ( "time" amino "github.com/tendermint/go-amino" + tmclient "github.com/tendermint/tendermint/rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpcclient "github.com/tendermint/tendermint/rpc/lib/client" ) +func WaitForNextHeightTM(port string) { + url := fmt.Sprintf("http://localhost:%v", port) + cl := tmclient.NewHTTP(url, "/websocket") + resBlock, err := cl.Block(nil) + if err != nil { + panic(err) + } + waitForHeightTM(resBlock.Block.Height+1, url) +} + +func WaitForHeightTM(height int64, port string) { + url := fmt.Sprintf("http://localhost:%v", port) + waitForHeightTM(height, url) +} + +func waitForHeightTM(height int64, url string) { + cl := tmclient.NewHTTP(url, "/websocket") + for { + // get url, try a few times + var resBlock *ctypes.ResultBlock + var err error + INNER: + for i := 0; i < 5; i++ { + resBlock, err = cl.Block(nil) + if err == nil { + break INNER + } + time.Sleep(time.Millisecond * 200) + } + if err != nil { + panic(err) + } + + if resBlock.Block != nil && + resBlock.Block.Height >= height { + fmt.Println("HEIGHT", resBlock.Block.Height) + return + } + time.Sleep(time.Millisecond * 100) + } +} + // Uses localhost func WaitForHeight(height int64, port string) { + url := fmt.Sprintf("http://localhost:%v/blocks/latest", port) + waitForHeight(height, url) +} + +func waitForHeight(height int64, url string) { for { - - url := fmt.Sprintf("http://localhost:%v/blocks/latest", port) - // get url, try a few times var res *http.Response var err error @@ -25,7 +70,7 @@ func WaitForHeight(height int64, port string) { if err == nil { break } - time.Sleep(time.Second) + time.Sleep(time.Millisecond * 200) } if err != nil { panic(err) @@ -45,7 +90,8 @@ func WaitForHeight(height int64, port string) { panic(err) } - if resultBlock.Block.Height >= height { + if resultBlock.Block != nil && + resultBlock.Block.Height >= height { return } time.Sleep(time.Millisecond * 100) From 059a1659bee9907b1ee705d42328fdd85f309c34 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 13 Jun 2018 18:33:09 -0700 Subject: [PATCH 25/27] add comments --- tests/util.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/util.go b/tests/util.go index c47d9907f..387ccb769 100644 --- a/tests/util.go +++ b/tests/util.go @@ -12,6 +12,8 @@ import ( rpcclient "github.com/tendermint/tendermint/rpc/lib/client" ) +// Wait for the next tendermint block from the Tendermint RPC +// on localhost func WaitForNextHeightTM(port string) { url := fmt.Sprintf("http://localhost:%v", port) cl := tmclient.NewHTTP(url, "/websocket") @@ -22,6 +24,8 @@ func WaitForNextHeightTM(port string) { waitForHeightTM(resBlock.Block.Height+1, url) } +// Wait for the given height from the Tendermint RPC +// on localhost func WaitForHeightTM(height int64, port string) { url := fmt.Sprintf("http://localhost:%v", port) waitForHeightTM(height, url) @@ -54,7 +58,7 @@ func waitForHeightTM(height int64, url string) { } } -// Uses localhost +// Wait for height from the LCD API on localhost func WaitForHeight(height int64, port string) { url := fmt.Sprintf("http://localhost:%v/blocks/latest", port) waitForHeight(height, url) From ab028a780570caeedefd5786f10ece2513539519 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Wed, 13 Jun 2018 21:58:36 -0700 Subject: [PATCH 26/27] cwgoes comments, improved pseudocode --- docs/guides/sdk/key-management.md | 14 +------ docs/guides/staking/overview.md | 65 ++++++++++++++++--------------- docs/spec/README.md | 2 +- docs/spec/staking/state.md | 12 ++++++ docs/spec/staking/transactions.md | 58 +++++++++++---------------- 5 files changed, 72 insertions(+), 79 deletions(-) diff --git a/docs/guides/sdk/key-management.md b/docs/guides/sdk/key-management.md index a4c0f5501..1474989b9 100644 --- a/docs/guides/sdk/key-management.md +++ b/docs/guides/sdk/key-management.md @@ -3,15 +3,5 @@ Here we cover many aspects of handling keys within the Cosmos SDK framework. -## Pseudo Code - -Generating an address for an ed25519 public key (in pseudo code): - -``` -const TypeDistinguisher = HexToBytes("1624de6220") - -// prepend the TypeDistinguisher as Bytes -SerializedBytes = TypeDistinguisher ++ PubKey.asBytes() - -Address = ripemd160(SerializedBytes) -``` +// TODO add relevant key discussion +(related https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md#public-key-cryptography) diff --git a/docs/guides/staking/overview.md b/docs/guides/staking/overview.md index 570a0bfc5..79033fe1e 100644 --- a/docs/guides/staking/overview.md +++ b/docs/guides/staking/overview.md @@ -8,23 +8,24 @@ The Cosmos Hub is a Tendermint-based Delegated Proof of Stake (DPos) blockchain system that serves as a backbone of the Cosmos ecosystem. It is operated and secured by an open and globally decentralized set of validators. Tendermint is a Byzantine fault-tolerant distributed protocol for consensus among distrusting -parties, in this case the group of validators which produce the blocks for -cosmos. To avoid the nothing-at-stake problem, a validator in Tendermint needs -to lock up coins in a bond deposit. Each bond's atoms are illiquid, they cannot -be transferred however they can be unbonded to become liquid, this process -takes the unbonding-period which will be 3 weeks by default at Cosmos-Hub -launch. Tendermint protocol messages are signed by the validator's private key, -and this is a basis for Tendermint strict accountability that allows punishing -misbehaving validators by slashing (burning) their bonded Atoms. On the other -hand, validators are rewarded for their service of securing blockchain network -by the inflationary provisions and transactions fees. This incentivizes correct -behavior of the validators and provides the economic security of the network. +parties, in this case the group of validators which produce the blocks for the +Cosmos Hub. To avoid the nothing-at-stake problem, a validator in Tendermint +needs to lock up coins in a bond deposit. Each bond's atoms are illiquid, they +cannot be transferred - in order to become liquid, they must be unbonded, a +process which will take 3 weeks by default at Cosmos Hub launch. Tendermint +protocol messages are signed by the validator's private key and are therefor +attributable. Validators acting outside protocol specifications can be made +accountable through punishing by slashing (burning) their bonded Atoms. On the +other hand, validators are rewarded for their service of securing blockchain +network by the inflationary provisions and transactions fees. This incentivizes +correct behavior of the validators and provides the economic security of the +network. The native token of the Cosmos Hub is called the Atom; becoming a validator of the Cosmos Hub requires holding Atoms. However, not all Atom holders are validators of the Cosmos Hub. More precisely, there is a selection process that determines the validator set as a subset of all validators (Atom holders that -wants to become a validator). The other option for Atom holder is to delegate +want to become a validator). The other option for Atom holders is to delegate their atoms to validators, i.e., being a delegator. A delegator is an Atom holder that has put its Atoms at stake by delegating it to a validator. By bonding Atoms to secure the network (and taking a risk of being slashed in case of @@ -59,20 +60,22 @@ transaction fees. bonded the shares must first remain in an inbetween unbonding state for the duration of the unbonding period * Redelegating Shares - Process of redelegating atoms from one validator to - another. This process is instantanious, the redelegated delegation is - slashible to the old validator for all blocks before the redelegation and to - the new validator for all new blocks. + another. This process is instantaneous, but the redelegated atoms are + retrospecively slashable if the old validator is found to misbehave for any + blocks before the redelegation. These atoms are simultaniously slashable + for any new blocks which the new validator misbehavess * Validator - entity with atoms which is either actively validating the Tendermint protocol (bonded validator) or vying to validate . * Bonded Validator - a validator whose atoms are currently bonded and liable to - be slashed. These validators are to be able to sign protocol messages in the - Tendermint consensus protocol. There are limited number of bonded validators - at Cosmos Hub genesis there is a maximum of 100 bonded validators. Only Bonded - Validators receive atom provisions and fee rewards. + be slashed. These validators are to be able to sign protocol messages for + Tendermint consensus. At Cosmos Hub genesis there is a maximum of 100 + bonded validator positions. Only Bonded Validators receive atom provisions + and fee rewards. * Delegator - an Atom holder that has bonded Atoms to a validator * Unbonding period - time required in the unbonding state when unbonding shares. Time slashable to old validator after a redelegation. Time for which - validators can be slashed after an infraction + validators can be slashed after an infraction. To provide the requisite + cryptoeconomic security guarantees, all of these must be equal. * Atom provisions - The process of increasing the Atom supply. Atoms are periodically created on the Cosmos Hub and issued to bonded Atom holders. The goal of inflation is to incentize most of the Atoms in existence to be @@ -90,7 +93,7 @@ At the core of the Staking module is the concept of a pool which denotes a collection of Atoms contributed by different Atom holders. There are three pools in the Staking module: the bonded, unbonding, and unbonded pool. Bonded Atoms are part of the global bonded pool. If a validator or delegator wants to -unbond its Shares, these Shares are moved to the the unbonding pool for the +unbond its shares, these Shares are moved to the the unbonding pool for the duration of the unbonding period. From here normally Atoms will be moved directly into the delegators wallet, however under the situation thatn an entire validator gets unbonded, the Atoms of the delegations will remain with @@ -169,17 +172,17 @@ delegators (we will explain this in section X). #### Delegator shares -A validator is, depending on its status, contributing Atoms to either the bond, +A validator is, depending on its status, contributing Atoms to either the unbonding or unbonded pool - the validator in turn holds some amount of pool -shares. Not all of a validators Atoms (and respective shares) are owned by the -validator, some may be owned by delegators to that validator. The mechanism for -distribution of Atoms (and shares) between a validator and its delegators is -based on a notion of delegator shares. More precisely, every validator is -issuing (local) delegator shares (`Validator.IssuedDelegatorShares`) that -represents some portion of global shares managed by the validator -(`Validator.GlobalStakeShares`). The principle behind managing delegator shares -is the same as described in [Section](#The pool and the share). We now -illustrate it with an example. +shares. Not all of a validator's Atoms (and respective shares) are necessarily +owned by the validator, some may be owned by delegators to that validator. The +mechanism for distribution of Atoms (and shares) between a validator and its +delegators is based on a notion of delegator shares. More precisely, every +validator is issuing (local) delegator shares +(`Validator.IssuedDelegatorShares`) that represents some portion of global +shares managed by the validator (`Validator.GlobalStakeShares`). The principle +behind managing delegator shares is the same as described in [Section](#The +pool and the share). We now illustrate it with an example. XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXX TODO make way less verbose lets use bullet points to describe the example diff --git a/docs/spec/README.md b/docs/spec/README.md index 7f773e6aa..b115e0d45 100644 --- a/docs/spec/README.md +++ b/docs/spec/README.md @@ -10,7 +10,7 @@ NOTE: the specifications are not yet complete and very much a work in progress. - [Governance](governance) - Governance related specifications including proposals and voting. - [IBC](ibc) - Specification of the Cosmos inter-blockchain communication (IBC) protocol. -- [Staking](staking) - Proof of Stake related specifications including bonding +- [Staking](staking) - Proof-of-stake related specifications including bonding and delegation transactions, inflation, etc. - [Slashing](slashing) - Specifications of validator punishment mechanisms - [Provisioning](provisioning) - Fee distribution, and atom provision distribution specification diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 82a22dc65..1cfe4b26e 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -8,6 +8,8 @@ information about the total amounts of Atoms in all states, representative validator shares for stake in the global pools, moving Atom inflation information, etc. + - stored object: + ```golang type Pool struct { LooseUnbondedTokens int64 // tokens not associated with any validator @@ -35,6 +37,8 @@ type PoolShares struct { Params is global data structure that stores system parameters and defines overall functioning of the stake module. + - stored object: + ```golang type Params struct { InflationRateChange sdk.Rat // maximum annual change in inflation rate @@ -59,6 +63,8 @@ Related Store which holds Validator.ABCIValidator() The `Validator` holds the current state and some historical actions of the validator. + - stored object: + ```golang type Validator struct { Owner sdk.Address // sender of BondTx - UnbondTx returns here @@ -98,6 +104,8 @@ 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. + - stored object: + ```golang type Delegation struct { DelegatorAddr sdk.Address // delegation owner address @@ -114,6 +122,8 @@ A UnbondingDelegation object is created every time an unbonding is initiated. The unbond must be completed with a second transaction provided by the delegation owner after the unbonding period has passed. + - stored object: + ```golang type UnbondingDelegation struct { DelegationKey sdk.Address // key of the delegation @@ -135,6 +145,8 @@ delegation owner after the unbonding period has passed. The destination delegation of a redelegation may not itself undergo a new redelegation until the original redelegation has been completed. + - stored object: + ```golang type Redelegation struct { SourceDelegation sdk.Address // source delegation key diff --git a/docs/spec/staking/transactions.md b/docs/spec/staking/transactions.md index 11edbb98b..91df029c9 100644 --- a/docs/spec/staking/transactions.md +++ b/docs/spec/staking/transactions.md @@ -44,7 +44,7 @@ createValidator(tx TxCreateValidator): if validator != nil return // only one validator per address validator = NewValidator(OwnerAddr, ConsensusPubKey, GovernancePubKey, Description) - init validator poolShares, delegatorShares set to 0 //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + init validator poolShares, delegatorShares set to 0 init validator commision fields from tx validator.PoolShares = 0 @@ -71,8 +71,8 @@ type TxEditCandidacy struct { editCandidacy(tx TxEditCandidacy): validator = getValidator(tx.ValidatorAddr) - if tx.Commission > CommissionMax || tx.Commission < 0 return halt tx - if rateChange(tx.Commission) > CommissionMaxChange return halt tx + if tx.Commission > CommissionMax || tx.Commission < 0 then fail + if rateChange(tx.Commission) > CommissionMaxChange then fail validator.Commission = tx.Commission if tx.GovernancePubKey != nil validator.GovernancePubKey = tx.GovernancePubKey @@ -126,28 +126,16 @@ startUnbonding(tx TxStartUnbonding): delegation, found = getDelegatorBond(store, sender, tx.PubKey) if !found == nil return - if tx.Shares == "MAX" { - if !bond.Shares.GT(sdk.ZeroRat()) { - return ErrNotEnoughBondShares - else - var err sdk.Error - delShares, err = sdk.NewRatFromDecimal(tx.Shares) - if err != nil - return err - if bond.Shares.LT(delShares) + if bond.Shares < tx.Shares return ErrNotEnoughBondShares - validator, found := GetValidator(tx.ValidatorAddr) + validator, found = GetValidator(tx.ValidatorAddr) if !found { return err - if tx.Shares == "MAX" - delShares = bond.Shares + bond.Shares -= tx.Shares - bond.Shares -= delShares - - - revokeCandidacy := false + revokeCandidacy = false if bond.Shares.IsZero() { if bond.DelegatorAddr == validator.Owner && validator.Revoked == false @@ -158,8 +146,8 @@ startUnbonding(tx TxStartUnbonding): bond.Height = currentBlockHeight setDelegation(bond) - pool := GetPool() - validator, pool, returnAmount := validator.removeDelShares(pool, delShares) + pool = GetPool() + validator, pool, returnAmount = validator.removeDelShares(pool, tx.Shares) setPool( pool) unbondingDelegation = NewUnbondingDelegation(sender, returnAmount, currentHeight/Time, startSlashRatio) @@ -261,13 +249,13 @@ Tendermint. ```golang updateBondedValidators(newValidator Validator) (updatedVal Validator) - kickCliffValidator := false - oldCliffValidatorAddr := getCliffValidator(ctx) + kickCliffValidator = false + oldCliffValidatorAddr = getCliffValidator(ctx) // add the actual validator power sorted store - maxValidators := GetParams(ctx).MaxValidators - iterator := ReverseSubspaceIterator(ValidatorsByPowerKey) // largest to smallest - bondedValidatorsCount := 0 + maxValidators = GetParams(ctx).MaxValidators + iterator = ReverseSubspaceIterator(ValidatorsByPowerKey) // largest to smallest + bondedValidatorsCount = 0 var validator Validator for { if !iterator.Valid() || bondedValidatorsCount > int(maxValidators-1) { @@ -282,7 +270,7 @@ updateBondedValidators(newValidator Validator) (updatedVal Validator) // use the validator provided because it has not yet been updated // in the main validator store - ownerAddr := iterator.Value() + ownerAddr = iterator.Value() if bytes.Equal(ownerAddr, newValidator.Owner) { validator = newValidator else @@ -290,7 +278,7 @@ updateBondedValidators(newValidator Validator) (updatedVal Validator) // if not previously a validator (and unrevoked), // kick the cliff validator / bond this new validator - if validator.Status() != sdk.Bonded && !validator.Revoked { + if validator.Status() != Bonded && !validator.Revoked { kickCliffValidator = true validator = bondValidator(ctx, store, validator) @@ -302,16 +290,16 @@ updateBondedValidators(newValidator Validator) (updatedVal Validator) // perform the actual kicks if oldCliffValidatorAddr != nil && kickCliffValidator { - validator := getValidator(store, oldCliffValidatorAddr) + validator = getValidator(store, oldCliffValidatorAddr) unbondValidator(ctx, store, validator) return // perform all the store operations for when a validator status becomes unbonded -unbondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) - pool := GetPool(ctx) +unbondValidator(ctx Context, store KVStore, validator Validator) + pool = GetPool(ctx) // set the status - validator, pool = validator.UpdateStatus(pool, sdk.Unbonded) + validator, pool = validator.UpdateStatus(pool, Unbonded) setPool(ctx, pool) // save the now unbonded validator record @@ -325,11 +313,11 @@ unbondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) } // perform all the store operations for when a validator status becomes bonded -bondValidator(ctx sdk.Context, store sdk.KVStore, validator Validator) Validator - pool := GetPool(ctx) +bondValidator(ctx Context, store KVStore, validator Validator) Validator + pool = GetPool(ctx) // set the status - validator, pool = validator.UpdateStatus(pool, sdk.Bonded) + validator, pool = validator.UpdateStatus(pool, Bonded) setPool(ctx, pool) // save the now bonded validator record to the three referenced stores From 42e72956f45aaeb668c5f823d27f93295537edd7 Mon Sep 17 00:00:00 2001 From: Fabian Date: Wed, 13 Jun 2018 22:49:21 -0700 Subject: [PATCH 27/27] Merge PR #1252: Query node version introduce non store queries add version query update lcd spec changelog moved version query into baseapp --- CHANGELOG.md | 1 + baseapp/baseapp.go | 6 ++++++ client/context/helpers.go | 28 ++++++++++++++++++++-------- client/lcd/lcd_test.go | 9 +++++++++ client/lcd/root.go | 4 ++-- client/lcd/version.go | 29 +++++++++++++++++++++++++++++ docs/sdk/lcd-rest-api.yaml | 7 +++++++ version/command.go | 12 +++--------- x/auth/client/cli/account.go | 2 +- x/auth/client/rest/query.go | 2 +- x/ibc/client/cli/relay.go | 2 +- x/slashing/client/cli/query.go | 2 +- x/stake/client/cli/query.go | 4 ++-- x/stake/client/rest/query.go | 2 +- 14 files changed, 84 insertions(+), 26 deletions(-) create mode 100644 client/lcd/version.go diff --git a/CHANGELOG.md b/CHANGELOG.md index b37db2292..f7d4b3c85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ BREAKING CHANGES FEATURES * [x/auth] Added AccountNumbers to BaseAccount and StdTxs to allow for replay protection with account pruning +* [lcd] added an endpoint to query for the SDK version of the connected node IMPROVEMENTS * export command now writes current validator set for Tendermint diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 93ea6ae03..3265a870f 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -14,6 +14,7 @@ import ( "github.com/cosmos/cosmos-sdk/store" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" ) @@ -338,6 +339,11 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { } else { result = app.Simulate(tx) } + case "version": + return abci.ResponseQuery{ + Code: uint32(sdk.ABCICodeOK), + Value: []byte(version.GetVersion()), + } default: result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() } diff --git a/client/context/helpers.go b/client/context/helpers.go index 0e28f5fd0..5449afa97 100644 --- a/client/context/helpers.go +++ b/client/context/helpers.go @@ -3,6 +3,8 @@ package context import ( "fmt" + "github.com/tendermint/tmlibs/common" + "github.com/pkg/errors" "github.com/cosmos/cosmos-sdk/wire" @@ -42,14 +44,19 @@ func (ctx CoreContext) BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, return res, err } -// Query from Tendermint with the provided key and storename -func (ctx CoreContext) Query(key cmn.HexBytes, storeName string) (res []byte, err error) { - return ctx.query(key, storeName, "key") +// Query information about the connected node +func (ctx CoreContext) Query(path string) (res []byte, err error) { + return ctx.query(path, nil) +} + +// QueryStore from Tendermint with the provided key and storename +func (ctx CoreContext) QueryStore(key cmn.HexBytes, storeName string) (res []byte, err error) { + return ctx.queryStore(key, storeName, "key") } // Query from Tendermint with the provided storename and subspace func (ctx CoreContext) QuerySubspace(cdc *wire.Codec, subspace []byte, storeName string) (res []sdk.KVPair, err error) { - resRaw, err := ctx.query(subspace, storeName, "subspace") + resRaw, err := ctx.queryStore(subspace, storeName, "subspace") if err != nil { return res, err } @@ -58,8 +65,7 @@ func (ctx CoreContext) QuerySubspace(cdc *wire.Codec, subspace []byte, storeName } // Query from Tendermint with the provided storename and path -func (ctx CoreContext) query(key cmn.HexBytes, storeName, endPath string) (res []byte, err error) { - path := fmt.Sprintf("/store/%s/%s", storeName, endPath) +func (ctx CoreContext) query(path string, key common.HexBytes) (res []byte, err error) { node, err := ctx.GetNode() if err != nil { return res, err @@ -80,6 +86,12 @@ func (ctx CoreContext) query(key cmn.HexBytes, storeName, endPath string) (res [ return resp.Value, nil } +// Query from Tendermint with the provided storename and path +func (ctx CoreContext) queryStore(key cmn.HexBytes, storeName, endPath string) (res []byte, err error) { + path := fmt.Sprintf("/store/%s/%s", storeName, endPath) + return ctx.query(path, key) +} + // Get the from address from the name flag func (ctx CoreContext) GetFromAddress() (from sdk.Address, err error) { @@ -177,7 +189,7 @@ func (ctx CoreContext) GetAccountNumber(address []byte) (int64, error) { return 0, errors.New("accountDecoder required but not provided") } - res, err := ctx.Query(auth.AddressStoreKey(address), ctx.AccountStore) + res, err := ctx.QueryStore(auth.AddressStoreKey(address), ctx.AccountStore) if err != nil { return 0, err } @@ -201,7 +213,7 @@ func (ctx CoreContext) NextSequence(address []byte) (int64, error) { return 0, errors.New("accountDecoder required but not provided") } - res, err := ctx.Query(auth.AddressStoreKey(address), ctx.AccountStore) + res, err := ctx.QueryStore(auth.AddressStoreKey(address), ctx.AccountStore) if err != nil { return 0, err } diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index ba2937e05..0bacbee20 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -116,6 +116,15 @@ func TestVersion(t *testing.T) { require.Nil(t, err) match := reg.MatchString(body) assert.True(t, match, body) + + // node info + res, body = Request(t, port, "GET", "/node_version", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + reg, err = regexp.Compile(`\d+\.\d+\.\d+(-dev)?`) + require.Nil(t, err) + match = reg.MatchString(body) + assert.True(t, match, body) } func TestNodeStatus(t *testing.T) { diff --git a/client/lcd/root.go b/client/lcd/root.go index 7d819740c..4af034297 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -17,7 +17,6 @@ import ( keys "github.com/cosmos/cosmos-sdk/client/keys" rpc "github.com/cosmos/cosmos-sdk/client/rpc" tx "github.com/cosmos/cosmos-sdk/client/tx" - version "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/wire" auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" @@ -63,7 +62,6 @@ func ServeCommand(cdc *wire.Codec) *cobra.Command { func createHandler(cdc *wire.Codec) http.Handler { r := mux.NewRouter() - r.HandleFunc("/version", version.RequestHandler).Methods("GET") kb, err := keys.GetKeyBase() //XXX if err != nil { @@ -73,6 +71,8 @@ func createHandler(cdc *wire.Codec) http.Handler { ctx := context.NewCoreContextFromViper() // TODO make more functional? aka r = keys.RegisterRoutes(r) + r.HandleFunc("/version", CLIVersionRequestHandler).Methods("GET") + r.HandleFunc("/node_version", NodeVersionRequestHandler(cdc, ctx)).Methods("GET") keys.RegisterRoutes(r) rpc.RegisterRoutes(ctx, r) tx.RegisterRoutes(ctx, r, cdc) diff --git a/client/lcd/version.go b/client/lcd/version.go new file mode 100644 index 000000000..0c8ef4ef6 --- /dev/null +++ b/client/lcd/version.go @@ -0,0 +1,29 @@ +package lcd + +import ( + "fmt" + "net/http" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/version" + "github.com/cosmos/cosmos-sdk/wire" +) + +// cli version REST handler endpoint +func CLIVersionRequestHandler(w http.ResponseWriter, r *http.Request) { + v := version.GetVersion() + w.Write([]byte(v)) +} + +// connected node version REST handler endpoint +func NodeVersionRequestHandler(cdc *wire.Codec, ctx context.CoreContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + version, err := ctx.Query("/app/version") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(fmt.Sprintf("Could't query version. Error: %s", err.Error()))) + return + } + w.Write([]byte(version)) + } +} diff --git a/docs/sdk/lcd-rest-api.yaml b/docs/sdk/lcd-rest-api.yaml index 6a5be2394..a39b7bf08 100644 --- a/docs/sdk/lcd-rest-api.yaml +++ b/docs/sdk/lcd-rest-api.yaml @@ -17,6 +17,13 @@ paths: responses: 200: description: Plaintext version i.e. "v0.5.0" + /node_version: + get: + summary: Version of the connected node + description: Get the version of the SDK running on the connected node to compare against expected + responses: + 200: + description: Plaintext version i.e. "v0.5.0" /node_info: get: description: Only the node info. Block information can be queried via /block/latest diff --git a/version/command.go b/version/command.go index b505414b1..2cff1bbe9 100644 --- a/version/command.go +++ b/version/command.go @@ -2,7 +2,6 @@ package version import ( "fmt" - "net/http" "github.com/spf13/cobra" ) @@ -16,7 +15,8 @@ var ( } ) -func getVersion() string { +// return version of CLI/node and commit hash +func GetVersion() string { v := Version if GitCommit != "" { v = v + "-" + GitCommit @@ -26,12 +26,6 @@ func getVersion() string { // CMD func printVersion(cmd *cobra.Command, args []string) { - v := getVersion() + v := GetVersion() fmt.Println(v) } - -// version REST handler endpoint -func RequestHandler(w http.ResponseWriter, r *http.Request) { - v := getVersion() - w.Write([]byte(v)) -} diff --git a/x/auth/client/cli/account.go b/x/auth/client/cli/account.go index a3265a78c..ccc1e277b 100644 --- a/x/auth/client/cli/account.go +++ b/x/auth/client/cli/account.go @@ -47,7 +47,7 @@ func GetAccountCmd(storeName string, cdc *wire.Codec, decoder auth.AccountDecode // perform query ctx := context.NewCoreContextFromViper() - res, err := ctx.Query(auth.AddressStoreKey(key), storeName) + res, err := ctx.QueryStore(auth.AddressStoreKey(key), storeName) if err != nil { return err } diff --git a/x/auth/client/rest/query.go b/x/auth/client/rest/query.go index 1f68a69e5..a5d6f328c 100644 --- a/x/auth/client/rest/query.go +++ b/x/auth/client/rest/query.go @@ -34,7 +34,7 @@ func QueryAccountRequestHandlerFn(storeName string, cdc *wire.Codec, decoder aut return } - res, err := ctx.Query(auth.AddressStoreKey(addr), storeName) + res, err := ctx.QueryStore(auth.AddressStoreKey(addr), storeName) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("couldn't query account. Error: %s", err.Error()))) diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index 9742f83f9..90ea74b77 100644 --- a/x/ibc/client/cli/relay.go +++ b/x/ibc/client/cli/relay.go @@ -151,7 +151,7 @@ OUTER: } func query(node string, key []byte, storeName string) (res []byte, err error) { - return context.NewCoreContextFromViper().WithNodeURI(node).Query(key, storeName) + return context.NewCoreContextFromViper().WithNodeURI(node).QueryStore(key, storeName) } func (c relayCommander) broadcastTx(seq int64, node string, tx []byte) error { diff --git a/x/slashing/client/cli/query.go b/x/slashing/client/cli/query.go index 948e75667..c1b16eb3a 100644 --- a/x/slashing/client/cli/query.go +++ b/x/slashing/client/cli/query.go @@ -27,7 +27,7 @@ func GetCmdQuerySigningInfo(storeName string, cdc *wire.Codec) *cobra.Command { } key := slashing.GetValidatorSigningInfoKey(pk.Address()) ctx := context.NewCoreContextFromViper() - res, err := ctx.Query(key, storeName) + res, err := ctx.QueryStore(key, storeName) if err != nil { return err } diff --git a/x/stake/client/cli/query.go b/x/stake/client/cli/query.go index eac39b9ef..727cddcde 100644 --- a/x/stake/client/cli/query.go +++ b/x/stake/client/cli/query.go @@ -27,7 +27,7 @@ func GetCmdQueryValidator(storeName string, cdc *wire.Codec) *cobra.Command { } key := stake.GetValidatorKey(addr) ctx := context.NewCoreContextFromViper() - res, err := ctx.Query(key, storeName) + res, err := ctx.QueryStore(key, storeName) if err != nil { return err } @@ -124,7 +124,7 @@ func GetCmdQueryDelegation(storeName string, cdc *wire.Codec) *cobra.Command { key := stake.GetDelegationKey(delAddr, addr, cdc) ctx := context.NewCoreContextFromViper() - res, err := ctx.Query(key, storeName) + res, err := ctx.QueryStore(key, storeName) if err != nil { return err } diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index d9288fb11..ec0e81330 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -48,7 +48,7 @@ func bondingStatusHandlerFn(ctx context.CoreContext, storeName string, cdc *wire key := stake.GetDelegationKey(delegatorAddr, validatorAddr, cdc) - res, err := ctx.Query(key, storeName) + res, err := ctx.QueryStore(key, storeName) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("couldn't query bond. Error: %s", err.Error())))