From 3445a85aad5c595a7e2d666950fdba53d19255b3 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Fri, 27 Jul 2018 18:35:21 -0700 Subject: [PATCH 01/36] initial progress on vesting spec --- docs/spec/auth/vesting.md | 88 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 docs/spec/auth/vesting.md diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md new file mode 100644 index 0000000000..0515953573 --- /dev/null +++ b/docs/spec/auth/vesting.md @@ -0,0 +1,88 @@ +## Vesting + +### Intro and Requirements + +This paper specifies changes to the auth and bank modules to implement vested accounts for the Cosmos Hub. +The requirements for this vested account is that it should be capable of being initialized during genesis with +a starting balance X and a vesting blocknumber N. The owner of this account should be able to delegate to validators, +but they cannot send their initial coins to other accounts. However; funds sent to this account, or fees and +inflation rewards from delegation should be spendable. Thus, the bank module's MsgSend handler should error if +a vested account is trying to send an amount `x > currentBalance - initialBalance` before block N. + +### Implementation + +##### Changes to x/auth Module + +The first change is to the Account interface to specify both the Account type and any parameters it needs. + +```go +// Account is a standard account using a sequence number for replay protection +// and a pubkey for authentication. +type Account interface { + Type() string // returns the type of the account + + GetAddress() sdk.AccAddress + SetAddress(sdk.AccAddress) error // errors if already set. + + GetPubKey() crypto.PubKey // can return nil. + SetPubKey(crypto.PubKey) error + + GetAccountNumber() int64 + SetAccountNumber(int64) error + + GetSequence() int64 + SetSequence(int64) error + + GetCoins() sdk.Coins + SetCoins(sdk.Coins) error + + // Getter and setter methods for account params + // It is upto handler to use these appropriately + GetParams() map[string]interface{} + SetParams(map[string]interface{}) error +} +``` + +The `Type` method will allow handlers to determine what type of account is sending the message, and the +handler can then call `GetParams` to handle the specific account type using the parameters it expects to +exist in the parameter map. + +The `VestedAccount` will be an implementation of `Account` interface that wraps `BaseAccount` with +`Type() => "vested` and params, `GetParams() => {"Funds": initialBalance (sdk.Coins), "BlockLock": blockN (int64)}`. +`SetParams` will be disabled as we do not want to update params after vested account initialization. +The `VestedAccount` will also maintain an attribute called `FreeCoins` + + +`auth.AccountMapper` to handle vested accounts as well. Specific changes +are omitted in this doc for succinctness. + + +##### Changes to bank MsgSend Handler + +Since a vested account should be capable of doing everything but sending, the restriction should be +handled at the `bank.Keeper` level. Specifically in methods that are explicitly used for sending like +`sendCoins` and `inputOutputCoins`. These methods must check an account's `Type` method; if it is a vested +account (i.e. `acc.Type() == "vested"`): + +1. Check if `ctx.BlockHeight() < acc.GetParams()["BlockLock"]` + * If `true`, the account is still vesting +2. If account is still vesting, check that `(acc.GetCoins() - acc.GetParams()["Funds"] - amount).IsValid()`. + * This will check that amount trying to be spent will not come from initial balance. +3. If above checks pass, allow transaction to go through. Else, return sdk.Error. + +### Initializing at Genesis + +### Pros and Cons + +##### Pros + +- Easily Extensible. If more account types need to get added in the future or if developers building on top of SDK +want to handle multiple custom account types, they simply have to implement the `Account` interface with unique `Type` +and their custom parameters. +- Handlers (and their associated keepers) get to determine what types of accounts they will handle and can use the parameters +in Account interface to handle different accounts appropriately. + +##### Cons + +- Changes to `Account` interface +- Slightly more complex code in `bank.Keeper` functions From 39d1cf69b5240ea84692261411de02f40b8d1b5b Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Sun, 29 Jul 2018 19:29:54 -0700 Subject: [PATCH 02/36] simplify spec --- docs/spec/auth/vesting.md | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 0515953573..3a3ef07e85 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -4,16 +4,15 @@ This paper specifies changes to the auth and bank modules to implement vested accounts for the Cosmos Hub. The requirements for this vested account is that it should be capable of being initialized during genesis with -a starting balance X and a vesting blocknumber N. The owner of this account should be able to delegate to validators, -but they cannot send their initial coins to other accounts. However; funds sent to this account, or fees and -inflation rewards from delegation should be spendable. Thus, the bank module's MsgSend handler should error if -a vested account is trying to send an amount `x > currentBalance - initialBalance` before block N. +a starting balance X coins and a vesting blocknumber N. The owner of this account should be able to delegate to validators, +but they cannot send their coins to other accounts. Thus, the bank module's MsgSend handler should error if +a vested account is trying to send an amount before block N. ### Implementation ##### Changes to x/auth Module -The first change is to the Account interface to specify both the Account type and any parameters it needs. +The Account interface will specify both the Account type and any parameters it needs. ```go // Account is a standard account using a sequence number for replay protection @@ -48,12 +47,11 @@ handler can then call `GetParams` to handle the specific account type using the exist in the parameter map. The `VestedAccount` will be an implementation of `Account` interface that wraps `BaseAccount` with -`Type() => "vested` and params, `GetParams() => {"Funds": initialBalance (sdk.Coins), "BlockLock": blockN (int64)}`. -`SetParams` will be disabled as we do not want to update params after vested account initialization. -The `VestedAccount` will also maintain an attribute called `FreeCoins` +`Type() => "vested` and params, `GetParams() => {"BlockLock": blockN (int64)}`. +`SetParams` will be disabled as we do not want to update params after vested account initialization. -`auth.AccountMapper` to handle vested accounts as well. Specific changes +`auth.AccountMapper` will be modified handle vested accounts as well. Specific changes are omitted in this doc for succinctness. @@ -65,13 +63,25 @@ handled at the `bank.Keeper` level. Specifically in methods that are explicitly account (i.e. `acc.Type() == "vested"`): 1. Check if `ctx.BlockHeight() < acc.GetParams()["BlockLock"]` - * If `true`, the account is still vesting -2. If account is still vesting, check that `(acc.GetCoins() - acc.GetParams()["Funds"] - amount).IsValid()`. - * This will check that amount trying to be spent will not come from initial balance. -3. If above checks pass, allow transaction to go through. Else, return sdk.Error. +2. If `true`, the account is still vesting, return sdk.Error. Else, allow transaction to be processed as normal. ### Initializing at Genesis +To initialize both vested accounts and base accounts, the `GenesisAccount` struct will be: + +```go +type GenesisAccount struct { + Address sdk.AccAddress `json:"address"` + Coins sdk.Coins `json:"coins"` + Type string `json:"type"` + BlockLock int64 `json:"lock"` +} +``` + +During `InitChain`, the GenesisAccount's are decoded. If they have `Type == "vested`, a vested account with parameters => +`{"BlockLock": BlockLock}` gets created and put in initial state. Otherwise if `Type == "base"` a base account is created +and the `BlockLock` attribute of corresponding `GenesisAccount` is ignored. `InitChain` will panic on any other account types. + ### Pros and Cons ##### Pros From 69d1fe2fb18c0e6181a6ead92f3f9e031a6f688d Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Sun, 29 Jul 2018 19:33:17 -0700 Subject: [PATCH 03/36] clarify requirements --- docs/spec/auth/vesting.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 3a3ef07e85..c188f3f644 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -4,9 +4,9 @@ This paper specifies changes to the auth and bank modules to implement vested accounts for the Cosmos Hub. The requirements for this vested account is that it should be capable of being initialized during genesis with -a starting balance X coins and a vesting blocknumber N. The owner of this account should be able to delegate to validators, -but they cannot send their coins to other accounts. Thus, the bank module's MsgSend handler should error if -a vested account is trying to send an amount before block N. +a starting balance X coins and a vesting blocknumber N. The owner of this account should be able to delegate to validators and vote, +however they cannot send their coins to other accounts until the account has fully vested. Thus, the bank module's MsgSend handler +should error if a vested account is trying to send an amount before block N. ### Implementation From 89494ef73eedb5e1ca0527eca161bf21030d3867 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Sun, 29 Jul 2018 19:35:23 -0700 Subject: [PATCH 04/36] improve readability --- docs/spec/auth/vesting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index c188f3f644..dcb07a18bc 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -5,14 +5,14 @@ This paper specifies changes to the auth and bank modules to implement vested accounts for the Cosmos Hub. The requirements for this vested account is that it should be capable of being initialized during genesis with a starting balance X coins and a vesting blocknumber N. The owner of this account should be able to delegate to validators and vote, -however they cannot send their coins to other accounts until the account has fully vested. Thus, the bank module's MsgSend handler +however they cannot send their coins to other accounts until the account has fully vested. Thus, the bank module's `MsgSend` handler should error if a vested account is trying to send an amount before block N. ### Implementation ##### Changes to x/auth Module -The Account interface will specify both the Account type and any parameters it needs. +The `Account` interface will specify both the Account type and any parameters it needs. ```go // Account is a standard account using a sequence number for replay protection From 41130f87422dcb401b2a18a8d730724c22cd8510 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Mon, 30 Jul 2018 13:25:44 -0700 Subject: [PATCH 05/36] Addressed basic comments --- docs/spec/auth/vesting.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index dcb07a18bc..1f72b48e51 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -4,9 +4,9 @@ This paper specifies changes to the auth and bank modules to implement vested accounts for the Cosmos Hub. The requirements for this vested account is that it should be capable of being initialized during genesis with -a starting balance X coins and a vesting blocknumber N. The owner of this account should be able to delegate to validators and vote, +a starting balance X coins and a vesting blocktime T. The owner of this account should be able to delegate to validators and vote, however they cannot send their coins to other accounts until the account has fully vested. Thus, the bank module's `MsgSend` handler -should error if a vested account is trying to send an amount before block N. +should error if a vested account is trying to send an amount before time T. ### Implementation @@ -36,9 +36,10 @@ type Account interface { SetCoins(sdk.Coins) error // Getter and setter methods for account params + // Parameters can be understood to be a map[string]interface{} with encoded keys and vals in store // It is upto handler to use these appropriately - GetParams() map[string]interface{} - SetParams(map[string]interface{}) error + GetParams([]byte) []byte + SetParams([]byte, []byte) error } ``` @@ -47,7 +48,7 @@ handler can then call `GetParams` to handle the specific account type using the exist in the parameter map. The `VestedAccount` will be an implementation of `Account` interface that wraps `BaseAccount` with -`Type() => "vested` and params, `GetParams() => {"BlockLock": blockN (int64)}`. +`Type() => "vested` and params, `GetParams() => {"TimeLock": N (int64)}`. `SetParams` will be disabled as we do not want to update params after vested account initialization. @@ -62,7 +63,7 @@ handled at the `bank.Keeper` level. Specifically in methods that are explicitly `sendCoins` and `inputOutputCoins`. These methods must check an account's `Type` method; if it is a vested account (i.e. `acc.Type() == "vested"`): -1. Check if `ctx.BlockHeight() < acc.GetParams()["BlockLock"]` +1. Check if `ctx.BlockHeader().Time < acc.GetParams()["BlockLock"]` 2. If `true`, the account is still vesting, return sdk.Error. Else, allow transaction to be processed as normal. ### Initializing at Genesis @@ -71,16 +72,16 @@ To initialize both vested accounts and base accounts, the `GenesisAccount` struc ```go type GenesisAccount struct { - Address sdk.AccAddress `json:"address"` - Coins sdk.Coins `json:"coins"` - Type string `json:"type"` - BlockLock int64 `json:"lock"` + Address sdk.AccAddress `json:"address"` + Coins sdk.Coins `json:"coins"` + Type string `json:"type"` + TimeLock int64 `json:"lock"` } ``` During `InitChain`, the GenesisAccount's are decoded. If they have `Type == "vested`, a vested account with parameters => -`{"BlockLock": BlockLock}` gets created and put in initial state. Otherwise if `Type == "base"` a base account is created -and the `BlockLock` attribute of corresponding `GenesisAccount` is ignored. `InitChain` will panic on any other account types. +`{"TimeLock": N}` gets created and put in initial state. Otherwise if `Type == "base"` a base account is created +and the `TimeLock` attribute of corresponding `GenesisAccount` is ignored. `InitChain` will panic on any other account types. ### Pros and Cons From d4d7658166100a156a76f8b29c6a1d1f3bc5ea99 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Tue, 31 Jul 2018 18:11:19 -0700 Subject: [PATCH 06/36] New idea for spec --- docs/spec/auth/vesting.md | 129 +++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 70 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 1f72b48e51..87194ce8e9 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -2,98 +2,87 @@ ### Intro and Requirements -This paper specifies changes to the auth and bank modules to implement vested accounts for the Cosmos Hub. -The requirements for this vested account is that it should be capable of being initialized during genesis with -a starting balance X coins and a vesting blocktime T. The owner of this account should be able to delegate to validators and vote, -however they cannot send their coins to other accounts until the account has fully vested. Thus, the bank module's `MsgSend` handler -should error if a vested account is trying to send an amount before time T. +This paper specifies changes to the auth and bank modules to implement vesting accounts for the Cosmos Hub. +The requirements for this vesting account is that it should be capable of being initialized during genesis with +a starting balance X coins and a vesting blocktime T. The owner of this account should be able to delegate to validators +and vote with locked coins, however they cannot send locked coins to other accounts until those coins have been unlocked. +The vesting account should also be able to spend any coins it receives from other users or from fees/inflation rewards. +Thus, the bank module's `MsgSend` handler should error if a vesting account is trying to send an amount that exceeds their +unlocked coin amount. ### Implementation -##### Changes to x/auth Module - -The `Account` interface will specify both the Account type and any parameters it needs. +##### Vesting Account implementation ```go -// Account is a standard account using a sequence number for replay protection -// and a pubkey for authentication. -type Account interface { - Type() string // returns the type of the account +type VestingAccount interface { + Account + AssertIsVestingAccount() // existence implies that account is vesting. +} - GetAddress() sdk.AccAddress - SetAddress(sdk.AccAddress) error // errors if already set. +// Implements Vesting Account +// Continuously vests by unlocking coins linearly with respect to time +type ContinuousVestingAccount struct { + BaseAccount + OriginalCoins sdk.Coins + ReceivedCoins sdk.Coins + StartTime int64 + EndTime int64 +} - GetPubKey() crypto.PubKey // can return nil. - SetPubKey(crypto.PubKey) error - - GetAccountNumber() int64 - SetAccountNumber(int64) error - - GetSequence() int64 - SetSequence(int64) error - - GetCoins() sdk.Coins - SetCoins(sdk.Coins) error - - // Getter and setter methods for account params - // Parameters can be understood to be a map[string]interface{} with encoded keys and vals in store - // It is upto handler to use these appropriately - GetParams([]byte) []byte - SetParams([]byte, []byte) error +func (vacc ContinuousVestingAccount) ConvertAccount() BaseAccount { + if T > vacc.EndTime { + // Convert to BaseAccount + } } ``` -The `Type` method will allow handlers to determine what type of account is sending the message, and the -handler can then call `GetParams` to handle the specific account type using the parameters it expects to -exist in the parameter map. +The `VestingAccount` interface is used purely to assert that an account is a vesting account like so: -The `VestedAccount` will be an implementation of `Account` interface that wraps `BaseAccount` with -`Type() => "vested` and params, `GetParams() => {"TimeLock": N (int64)}`. -`SetParams` will be disabled as we do not want to update params after vested account initialization. +```go +vacc, ok := acc.(VestingAccount); ok +``` +The `ContinuousVestingAccount` struct implements the Vesting account interface. It uses `OriginalCoins`, `ReceivedCoins`, +`StartTime`, and `EndTime` to calculate how many coins are sendable at any given point. Once the account has fully vested, +the next `bank.MsgSend` will convert the account into a `BaseAccount` and store it in state as such from that point on. +Since the vesting restrictions need to be implemented on a per-module basis, the `ContinuouosVestingAccount` implements +the `Account` interface exactly like `BaseAccount`. -`auth.AccountMapper` will be modified handle vested accounts as well. Specific changes -are omitted in this doc for succinctness. +##### Changes to Keepers/Handler - -##### Changes to bank MsgSend Handler - -Since a vested account should be capable of doing everything but sending, the restriction should be +Since a vesting account should be capable of doing everything but sending with its locked coins, the restriction should be handled at the `bank.Keeper` level. Specifically in methods that are explicitly used for sending like -`sendCoins` and `inputOutputCoins`. These methods must check an account's `Type` method; if it is a vested -account (i.e. `acc.Type() == "vested"`): +`sendCoins` and `inputOutputCoins`. These methods must check that an account is a vesting account using the check described above. +NOTE: `Now = ctx.BlockHeader().Time` -1. Check if `ctx.BlockHeader().Time < acc.GetParams()["BlockLock"]` -2. If `true`, the account is still vesting, return sdk.Error. Else, allow transaction to be processed as normal. +1. If `Now < vacc.EndTime` + 1. Calculate `SendableCoins := ReceivedCoins + OriginalCoins * (Now - StartTime)/(EndTime - StartTime))` + - NOTE: `SendableCoins` may be greater than total coins in account. This is because coins can be subtracted by staking module. + `SendableCoins` denotes maximum coins allowed to be spent right now. + 2. If `msg.Amount > SendableCoins`, return sdk.Error. Else, allow transaction to process normally. +2. Else: + 1. Convert account to `BaseAccount` and process normally. + +Coins that are sent to a vesting account after initialization either through users sending them coins or through fees/inflation rewards +should be spendable immediately after receiving them. Thus, handlers (like staking or bank) that send coins that a vesting account did not +originally own should increment `ReceivedCoins` by the amount sent. + +WARNING: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account +unbonds from a validator, their tokens should be added back to account but `ReceivedCoins` SHOULD NOT be incremented. +However when the staking handler is handing out fees or inflation rewards, then `ReceivedCoins` SHOULD be incremented. ### Initializing at Genesis -To initialize both vested accounts and base accounts, the `GenesisAccount` struct will be: +To initialize both vesting accounts and base accounts, the `GenesisAccount` struct will be: ```go type GenesisAccount struct { - Address sdk.AccAddress `json:"address"` - Coins sdk.Coins `json:"coins"` - Type string `json:"type"` - TimeLock int64 `json:"lock"` + Address sdk.AccAddress `json:"address"` + Coins sdk.Coins `json:"coins"` + EndTime int64 `json:"lock"` } ``` -During `InitChain`, the GenesisAccount's are decoded. If they have `Type == "vested`, a vested account with parameters => -`{"TimeLock": N}` gets created and put in initial state. Otherwise if `Type == "base"` a base account is created -and the `TimeLock` attribute of corresponding `GenesisAccount` is ignored. `InitChain` will panic on any other account types. - -### Pros and Cons - -##### Pros - -- Easily Extensible. If more account types need to get added in the future or if developers building on top of SDK -want to handle multiple custom account types, they simply have to implement the `Account` interface with unique `Type` -and their custom parameters. -- Handlers (and their associated keepers) get to determine what types of accounts they will handle and can use the parameters -in Account interface to handle different accounts appropriately. - -##### Cons - -- Changes to `Account` interface -- Slightly more complex code in `bank.Keeper` functions +During `InitChain`, the GenesisAccounts are decoded. If `EndTime == 0`, a BaseAccount gets created and put in Genesis state. +Otherwise a vesting account is created with `StartTime = RequestInitChain.Time`, `EndTime = gacc.EndTime`, and `OriginalCoins = Coins`. From 10b2e830a224b00f5096c486ffdaec74b0ae29bf Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Fri, 3 Aug 2018 13:12:34 -0700 Subject: [PATCH 07/36] addressed comments, added formulas for easy verification --- docs/spec/auth/vesting.md | 108 +++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 30 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 87194ce8e9..99a051284a 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -2,11 +2,11 @@ ### Intro and Requirements -This paper specifies changes to the auth and bank modules to implement vesting accounts for the Cosmos Hub. -The requirements for this vesting account is that it should be capable of being initialized during genesis with -a starting balance X coins and a vesting blocktime T. The owner of this account should be able to delegate to validators +This paper specifies vesting account implementation for the Cosmos Hub. +The requirements for this vesting account is that it should be initialized during genesis with +a starting balance X coins and a vesting endtime T. The owner of this account should be able to delegate to validators and vote with locked coins, however they cannot send locked coins to other accounts until those coins have been unlocked. -The vesting account should also be able to spend any coins it receives from other users or from fees/inflation rewards. +The vesting account should also be able to spend any coins it receives from other users. Thus, the bank module's `MsgSend` handler should error if a vesting account is trying to send an amount that exceeds their unlocked coin amount. @@ -14,35 +14,40 @@ unlocked coin amount. ##### Vesting Account implementation +NOTE: `Now = ctx.BlockHeader().Time` + ```go type VestingAccount interface { Account AssertIsVestingAccount() // existence implies that account is vesting. + ConvertAccount(sdk.Context) BaseAccount } // Implements Vesting Account // Continuously vests by unlocking coins linearly with respect to time type ContinuousVestingAccount struct { BaseAccount - OriginalCoins sdk.Coins - ReceivedCoins sdk.Coins + OriginalCoins sdk.Coins // Coins in account on Initialization + ReceivedCoins sdk.Coins // Coins received from other accounts + + // StartTime and EndTime used to calculate how much of OriginalCoins is unlocked at any given point StartTime int64 EndTime int64 } -func (vacc ContinuousVestingAccount) ConvertAccount() BaseAccount { - if T > vacc.EndTime { - // Convert to BaseAccount - } -} +ConvertAccount(vacc ContinuousVestingAccount) (BaseAccount): + if Now > vacc.EndTime then // Convert to BaseAccount + ``` -The `VestingAccount` interface is used purely to assert that an account is a vesting account like so: +The `VestingAccount` interface is used to assert that an account is a vesting account like so: ```go vacc, ok := acc.(VestingAccount); ok ``` +as well as to convert to BaseAccount again once the account has fully vested. + The `ContinuousVestingAccount` struct implements the Vesting account interface. It uses `OriginalCoins`, `ReceivedCoins`, `StartTime`, and `EndTime` to calculate how many coins are sendable at any given point. Once the account has fully vested, the next `bank.MsgSend` will convert the account into a `BaseAccount` and store it in state as such from that point on. @@ -54,35 +59,78 @@ the `Account` interface exactly like `BaseAccount`. Since a vesting account should be capable of doing everything but sending with its locked coins, the restriction should be handled at the `bank.Keeper` level. Specifically in methods that are explicitly used for sending like `sendCoins` and `inputOutputCoins`. These methods must check that an account is a vesting account using the check described above. -NOTE: `Now = ctx.BlockHeader().Time` -1. If `Now < vacc.EndTime` - 1. Calculate `SendableCoins := ReceivedCoins + OriginalCoins * (Now - StartTime)/(EndTime - StartTime))` - - NOTE: `SendableCoins` may be greater than total coins in account. This is because coins can be subtracted by staking module. - `SendableCoins` denotes maximum coins allowed to be spent right now. - 2. If `msg.Amount > SendableCoins`, return sdk.Error. Else, allow transaction to process normally. -2. Else: - 1. Convert account to `BaseAccount` and process normally. +```go +if Now < vacc.EndTime: + // NOTE: SendableCoins may be greater than total coins in account because coins can be subtracted by staking module + // SendableCoins denotes maximum coins allowed to be spent. + SendableCoins := ReceivedCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime) + if msg.Amount > SendableCoins then fail -Coins that are sent to a vesting account after initialization either through users sending them coins or through fees/inflation rewards -should be spendable immediately after receiving them. Thus, handlers (like staking or bank) that send coins that a vesting account did not +else: account = ConvertAccount(account) // Account fully vested, convert to BaseAccount + +if msg.Amount > account.GetCoins() then fail // Must still check if account has enough coins, since SendableCoins does not check this. + +// All checks passed, send the coins +SendCoins(inputs, outputs) + +``` + +Coins that are sent to a vesting account after initialization by users sending them coins should be spendable +immediately after receiving them. Thus, handlers (like staking or bank) that send coins that a vesting account did not originally own should increment `ReceivedCoins` by the amount sent. -WARNING: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account -unbonds from a validator, their tokens should be added back to account but `ReceivedCoins` SHOULD NOT be incremented. +CONTRACT: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account unbonds from a validator, their tokens should be added back to account but `ReceivedCoins` SHOULD NOT be incremented. However when the staking handler is handing out fees or inflation rewards, then `ReceivedCoins` SHOULD be incremented. ### Initializing at Genesis -To initialize both vesting accounts and base accounts, the `GenesisAccount` struct will be: +To initialize both vesting accounts and base accounts, the `GenesisAccount` struct will include an EndTime. Accounts meant to be +BaseAccounts will have `EndTime = 0`. The `initChainer` method will parse the GenesisAccount into BaseAccounts and VestingAccounts +as appropriate. ```go type GenesisAccount struct { - Address sdk.AccAddress `json:"address"` - Coins sdk.Coins `json:"coins"` - EndTime int64 `json:"lock"` + Address sdk.AccAddress `json:"address"` + GenesisCoins sdk.Coins `json:"coins"` + EndTime int64 `json:"lock"` } + +initChainer: + for genesis_acc in GenesisAccounts: + if EndTime == 0 then // Create BaseAccount + else: + vesting_account = ContinuouslyVestingAccount{ + OriginalCoins: GenesisCoins, + StartTime: RequestInitChain.Time, + EndTime: EndTime, + } + // Add account to initial state ``` -During `InitChain`, the GenesisAccounts are decoded. If `EndTime == 0`, a BaseAccount gets created and put in Genesis state. -Otherwise a vesting account is created with `StartTime = RequestInitChain.Time`, `EndTime = gacc.EndTime`, and `OriginalCoins = Coins`. +### Formulas + +`OriginalCoins`: Amount of coins in account at Genesis + +`CurrentCoins`: Coins currently in the baseaccount (both locked and unlocked) + +`ReceivedCoins`: Coins received from other accounts (always unlocked) + +`LockedCoins`: Coins that are currently locked + +`Delegated`: Coins that have been delegated (no longer in account; may be locked or unlocked) + +`Sent`: Coins sent to other accounts (MUST be unlocked) + +Maximum amount of coins vesting schedule allows to be sent: + +`ReceivedCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime)` +`ReceivedCoins + OriginalCoins - LockedCoins` + +Coins currently in Account: + +`CurrentCoins = OriginalCoins + ReceivedCoins - Delegated - Sent` + +**Maximum amount of coins spendable right now:** + +`min( ReceivedCoins + OriginalCoins - LockedCoins, CurrentCoins )` From 9c1918efdc04545297ff5b0c3f07da076fdc7e97 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Fri, 3 Aug 2018 13:16:41 -0700 Subject: [PATCH 08/36] improve readability --- docs/spec/auth/vesting.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 99a051284a..d70c94b1f9 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -62,14 +62,18 @@ handled at the `bank.Keeper` level. Specifically in methods that are explicitly ```go if Now < vacc.EndTime: - // NOTE: SendableCoins may be greater than total coins in account because coins can be subtracted by staking module + // NOTE: SendableCoins may be greater than total coins in account + // because coins can be subtracted by staking module // SendableCoins denotes maximum coins allowed to be spent. SendableCoins := ReceivedCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime) if msg.Amount > SendableCoins then fail -else: account = ConvertAccount(account) // Account fully vested, convert to BaseAccount +// Account fully vested, convert to BaseAccount +else: account = ConvertAccount(account) -if msg.Amount > account.GetCoins() then fail // Must still check if account has enough coins, since SendableCoins does not check this. +// Must still check if account has enough coins, +// since SendableCoins does not check this. +if msg.Amount > account.GetCoins() then fail // All checks passed, send the coins SendCoins(inputs, outputs) @@ -125,6 +129,7 @@ initChainer: Maximum amount of coins vesting schedule allows to be sent: `ReceivedCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime)` + `ReceivedCoins + OriginalCoins - LockedCoins` Coins currently in Account: From d56e3a7ef919f1fc3b64bc028a08bf8cc2ab9652 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Mon, 6 Aug 2018 13:03:58 -0700 Subject: [PATCH 09/36] better pseudocode --- docs/spec/auth/vesting.md | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index d70c94b1f9..3d2be53c32 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -35,8 +35,12 @@ type ContinuousVestingAccount struct { EndTime int64 } +// ConvertAccount converts VestingAccount into BaseAccount +// Will convert only after account has fully vested ConvertAccount(vacc ContinuousVestingAccount) (BaseAccount): - if Now > vacc.EndTime then // Convert to BaseAccount + if Now > vacc.EndTime: + account = NewBaseAccount(vacc.Address, vacc.OriginalCoins + vacc.ReceivedCoins) + return account ``` @@ -101,15 +105,22 @@ type GenesisAccount struct { } initChainer: - for genesis_acc in GenesisAccounts: - if EndTime == 0 then // Create BaseAccount - else: - vesting_account = ContinuouslyVestingAccount{ - OriginalCoins: GenesisCoins, + for gacc in GenesisAccounts: + baseAccount := BaseAccount{ + Address: gacc.Address, + Coins: gacc.GenesisCoins, + } + if gacc.EndTime != 0: + vestingAccount := ContinuouslyVestingAccount{ + BaseAccount: baseAccount, + OriginalCoins: gacc.GenesisCoins, StartTime: RequestInitChain.Time, - EndTime: EndTime, + EndTime: gacc.EndTime, } - // Add account to initial state + AddAccountToState(vestingAccount) + else: + AddAccountToState(baseAccount) + ``` ### Formulas From 2ac55ebb1dac3b53fe5eaf06daf757eecbb233e8 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Mon, 6 Aug 2018 15:23:47 -0700 Subject: [PATCH 10/36] even better pseudocode --- docs/spec/auth/vesting.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 3d2be53c32..3123e1da6b 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -65,7 +65,7 @@ handled at the `bank.Keeper` level. Specifically in methods that are explicitly `sendCoins` and `inputOutputCoins`. These methods must check that an account is a vesting account using the check described above. ```go -if Now < vacc.EndTime: +if Now < vestingAccount.EndTime: // NOTE: SendableCoins may be greater than total coins in account // because coins can be subtracted by staking module // SendableCoins denotes maximum coins allowed to be spent. @@ -73,11 +73,12 @@ if Now < vacc.EndTime: if msg.Amount > SendableCoins then fail // Account fully vested, convert to BaseAccount -else: account = ConvertAccount(account) +else: + account = ConvertAccount(account) // Must still check if account has enough coins, // since SendableCoins does not check this. -if msg.Amount > account.GetCoins() then fail +if msg.Amount > account.GetCoins() then fail // All checks passed, send the coins SendCoins(inputs, outputs) From cd3d197cc12265b2d6ffeb92a0e56a654b4bd6a0 Mon Sep 17 00:00:00 2001 From: Rigel Date: Tue, 7 Aug 2018 13:55:09 -0400 Subject: [PATCH 11/36] Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f7e2d0fd6f..6a25260591 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -10,6 +10,7 @@ v If a checkbox is n/a - please still include it but + a little note why - [ ] Wrote tests - [ ] Added entries in `PENDING.md` that include links to the relevant issue or PR that most accurately describes the change. - [ ] Updated `cmd/gaia` and `examples/` +- [ ] Read PR `Files changed` in the github PR explorer ___________________________________ For Admin Use: - [ ] Added appropriate labels to PR (ex. wip, ready-for-review, docs) From feb9a22663546fc55bd627ae2a2b2d471697de72 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Tue, 7 Aug 2018 17:02:28 -0700 Subject: [PATCH 12/36] Cleaned up spec further --- docs/spec/auth/vesting.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 3123e1da6b..b9535af05a 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -21,6 +21,10 @@ type VestingAccount interface { Account AssertIsVestingAccount() // existence implies that account is vesting. ConvertAccount(sdk.Context) BaseAccount + + // Calculates total amount of unlocked coins released by vesting schedule + // May be larger than total coins in account right now + TotalUnlockedCoins(sdk.Context) sdk.Coins } // Implements Vesting Account @@ -37,10 +41,13 @@ type ContinuousVestingAccount struct { // ConvertAccount converts VestingAccount into BaseAccount // Will convert only after account has fully vested -ConvertAccount(vacc ContinuousVestingAccount) (BaseAccount): +ConvertAccount(vacc ContinuousVestingAccount, ctx sdk.Context) (BaseAccount): if Now > vacc.EndTime: - account = NewBaseAccount(vacc.Address, vacc.OriginalCoins + vacc.ReceivedCoins) - return account + return vacc.BaseAccount + +// Uses time in context to calculate total unlocked coins +TotalUnlockedCoins(vacc ContinuousVestingAccount, ctx sdk.Context) sdk.Coins: + return ReceivedCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime) ``` @@ -69,8 +76,7 @@ if Now < vestingAccount.EndTime: // NOTE: SendableCoins may be greater than total coins in account // because coins can be subtracted by staking module // SendableCoins denotes maximum coins allowed to be spent. - SendableCoins := ReceivedCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime) - if msg.Amount > SendableCoins then fail + if msg.Amount > vestingAccount.TotalUnlockedCoins() then fail // Account fully vested, convert to BaseAccount else: @@ -148,6 +154,8 @@ Coins currently in Account: `CurrentCoins = OriginalCoins + ReceivedCoins - Delegated - Sent` +`CurrentCoins = vestingAccount.BaseAccount.GetCoins()` + **Maximum amount of coins spendable right now:** `min( ReceivedCoins + OriginalCoins - LockedCoins, CurrentCoins )` From 7539e212ee5f4dc7dfed4a746430a9e5ae1278b4 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Tue, 7 Aug 2018 17:04:22 -0700 Subject: [PATCH 13/36] more readable pseudo --- docs/spec/auth/vesting.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index b9535af05a..a69a342e4d 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -47,7 +47,8 @@ ConvertAccount(vacc ContinuousVestingAccount, ctx sdk.Context) (BaseAccount): // Uses time in context to calculate total unlocked coins TotalUnlockedCoins(vacc ContinuousVestingAccount, ctx sdk.Context) sdk.Coins: - return ReceivedCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime) + unlockedCoins := ReceivedCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime) + return unlockedCoins ``` From 599b8ba4cf26d139b827aac504092ffb77e66080 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Wed, 15 Aug 2018 13:54:03 -0700 Subject: [PATCH 14/36] Fix bug, clearer logic --- docs/spec/auth/vesting.md | 71 +++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index a69a342e4d..344e10049d 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -20,11 +20,9 @@ NOTE: `Now = ctx.BlockHeader().Time` type VestingAccount interface { Account AssertIsVestingAccount() // existence implies that account is vesting. - ConvertAccount(sdk.Context) BaseAccount - // Calculates total amount of unlocked coins released by vesting schedule - // May be larger than total coins in account right now - TotalUnlockedCoins(sdk.Context) sdk.Coins + // Calculates amount of coins that can be sent to other accounts given the current time + SendableCoins(sdk.Context) sdk.Coins } // Implements Vesting Account @@ -33,22 +31,24 @@ type ContinuousVestingAccount struct { BaseAccount OriginalCoins sdk.Coins // Coins in account on Initialization ReceivedCoins sdk.Coins // Coins received from other accounts + SentCoins sdk.Coins // Coins sent to other accounts // StartTime and EndTime used to calculate how much of OriginalCoins is unlocked at any given point - StartTime int64 - EndTime int64 + StartTime time.Time + EndTime time.Time } -// ConvertAccount converts VestingAccount into BaseAccount -// Will convert only after account has fully vested -ConvertAccount(vacc ContinuousVestingAccount, ctx sdk.Context) (BaseAccount): - if Now > vacc.EndTime: - return vacc.BaseAccount - // Uses time in context to calculate total unlocked coins -TotalUnlockedCoins(vacc ContinuousVestingAccount, ctx sdk.Context) sdk.Coins: - unlockedCoins := ReceivedCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime) - return unlockedCoins +SendableCoins(vacc ContinuousVestingAccount, ctx sdk.Context) sdk.Coins: + + // Coins unlocked by vesting schedule + unlockedCoins := ReceivedCoins - SentCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime) + + // Must still check for currentCoins constraint since some unlocked coins may have been delegated. + currentCoins := vacc.BaseAccount.GetCoins() + + // min will return sdk.Coins with each denom having the minimum amount from unlockedCoins and currentCoins + return min(unlockedCoins, currentCoins) ``` @@ -58,13 +58,13 @@ The `VestingAccount` interface is used to assert that an account is a vesting ac vacc, ok := acc.(VestingAccount); ok ``` -as well as to convert to BaseAccount again once the account has fully vested. +as well as to calculate the SendableCoins at any given moment. The `ContinuousVestingAccount` struct implements the Vesting account interface. It uses `OriginalCoins`, `ReceivedCoins`, -`StartTime`, and `EndTime` to calculate how many coins are sendable at any given point. Once the account has fully vested, -the next `bank.MsgSend` will convert the account into a `BaseAccount` and store it in state as such from that point on. -Since the vesting restrictions need to be implemented on a per-module basis, the `ContinuouosVestingAccount` implements -the `Account` interface exactly like `BaseAccount`. +`SentCoins`, `StartTime`, and `EndTime` to calculate how many coins are sendable at any given point. +Since the vesting restrictions need to be implemented on a per-module basis, the `ContinuousVestingAccount` implements +the `Account` interface exactly like `BaseAccount`. Thus, `ContinuousVestingAccount.GetCoins()` will return the total of +both locked coins and unlocked coins currently in the account. ##### Changes to Keepers/Handler @@ -73,19 +73,15 @@ handled at the `bank.Keeper` level. Specifically in methods that are explicitly `sendCoins` and `inputOutputCoins`. These methods must check that an account is a vesting account using the check described above. ```go -if Now < vestingAccount.EndTime: - // NOTE: SendableCoins may be greater than total coins in account - // because coins can be subtracted by staking module - // SendableCoins denotes maximum coins allowed to be spent. - if msg.Amount > vestingAccount.TotalUnlockedCoins() then fail +if acc is VestingAccount and Now < vestingAccount.EndTime: + // Check if amount is less than currently allowed sendable coins + if msg.Amount > vestingAccount.SendableCoins(ctx) then fail + else: + vestingAccount.SentCoins += msg.Amount -// Account fully vested, convert to BaseAccount else: - account = ConvertAccount(account) - -// Must still check if account has enough coins, -// since SendableCoins does not check this. -if msg.Amount > account.GetCoins() then fail + // Account has fully vested, treat like regular account + if msg.Amount > account.GetCoins() then fail // All checks passed, send the coins SendCoins(inputs, outputs) @@ -95,9 +91,10 @@ SendCoins(inputs, outputs) Coins that are sent to a vesting account after initialization by users sending them coins should be spendable immediately after receiving them. Thus, handlers (like staking or bank) that send coins that a vesting account did not originally own should increment `ReceivedCoins` by the amount sent. +Unlocked coins that are sent to other accounts will increment the vesting account's `SentCoins` attribute. CONTRACT: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account unbonds from a validator, their tokens should be added back to account but `ReceivedCoins` SHOULD NOT be incremented. -However when the staking handler is handing out fees or inflation rewards, then `ReceivedCoins` SHOULD be incremented. +However when the staking handler is handing out fees/inflation rewards or a user sends coins to vesting account, then `ReceivedCoins` SHOULD be incremented. ### Initializing at Genesis @@ -135,7 +132,7 @@ initChainer: `OriginalCoins`: Amount of coins in account at Genesis -`CurrentCoins`: Coins currently in the baseaccount (both locked and unlocked) +`CurrentCoins`: Coins currently in the baseaccount (both locked and unlocked: `vestingAccount.GetCoins`) `ReceivedCoins`: Coins received from other accounts (always unlocked) @@ -147,16 +144,16 @@ initChainer: Maximum amount of coins vesting schedule allows to be sent: -`ReceivedCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime)` +`ReceivedCoins - SentCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime)` -`ReceivedCoins + OriginalCoins - LockedCoins` +`ReceivedCoins - SentCoins + OriginalCoins - LockedCoins` Coins currently in Account: `CurrentCoins = OriginalCoins + ReceivedCoins - Delegated - Sent` -`CurrentCoins = vestingAccount.BaseAccount.GetCoins()` +`CurrentCoins = vestingAccount.GetCoins()` **Maximum amount of coins spendable right now:** -`min( ReceivedCoins + OriginalCoins - LockedCoins, CurrentCoins )` +`min( ReceivedCoins - SentCoins + OriginalCoins - LockedCoins, CurrentCoins )` From c79e130d0fbabea6dae0fca5d4c47dabb66f79f9 Mon Sep 17 00:00:00 2001 From: Aditya Sripal Date: Thu, 16 Aug 2018 12:07:09 -0700 Subject: [PATCH 15/36] jae comments --- docs/spec/auth/vesting.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/spec/auth/vesting.md b/docs/spec/auth/vesting.md index 344e10049d..c5c25ecaed 100644 --- a/docs/spec/auth/vesting.md +++ b/docs/spec/auth/vesting.md @@ -29,20 +29,20 @@ type VestingAccount interface { // Continuously vests by unlocking coins linearly with respect to time type ContinuousVestingAccount struct { BaseAccount - OriginalCoins sdk.Coins // Coins in account on Initialization - ReceivedCoins sdk.Coins // Coins received from other accounts - SentCoins sdk.Coins // Coins sent to other accounts + OriginalVestingCoins sdk.Coins // Coins in account on Initialization + ReceivedCoins sdk.Coins // Coins received from other accounts + SentCoins sdk.Coins // Coins sent to other accounts // StartTime and EndTime used to calculate how much of OriginalCoins is unlocked at any given point - StartTime time.Time - EndTime time.Time + StartTime time.Time + EndTime time.Time } // Uses time in context to calculate total unlocked coins SendableCoins(vacc ContinuousVestingAccount, ctx sdk.Context) sdk.Coins: // Coins unlocked by vesting schedule - unlockedCoins := ReceivedCoins - SentCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime) + unlockedCoins := ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime) // Must still check for currentCoins constraint since some unlocked coins may have been delegated. currentCoins := vacc.BaseAccount.GetCoins() @@ -60,11 +60,11 @@ vacc, ok := acc.(VestingAccount); ok as well as to calculate the SendableCoins at any given moment. -The `ContinuousVestingAccount` struct implements the Vesting account interface. It uses `OriginalCoins`, `ReceivedCoins`, +The `ContinuousVestingAccount` struct implements the Vesting account interface. It uses `OriginalVestingCoins`, `ReceivedCoins`, `SentCoins`, `StartTime`, and `EndTime` to calculate how many coins are sendable at any given point. Since the vesting restrictions need to be implemented on a per-module basis, the `ContinuousVestingAccount` implements the `Account` interface exactly like `BaseAccount`. Thus, `ContinuousVestingAccount.GetCoins()` will return the total of -both locked coins and unlocked coins currently in the account. +both locked coins and unlocked coins currently in the account. Delegated coins are deducted from `Account.GetCoins()`, but do not count against unlocked coins because they are still at stake and will be reinstated (partially if slashed) after waiting the full unbonding period. ##### Changes to Keepers/Handler @@ -93,8 +93,8 @@ immediately after receiving them. Thus, handlers (like staking or bank) that sen originally own should increment `ReceivedCoins` by the amount sent. Unlocked coins that are sent to other accounts will increment the vesting account's `SentCoins` attribute. -CONTRACT: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account unbonds from a validator, their tokens should be added back to account but `ReceivedCoins` SHOULD NOT be incremented. -However when the staking handler is handing out fees/inflation rewards or a user sends coins to vesting account, then `ReceivedCoins` SHOULD be incremented. +CONTRACT: Handlers SHOULD NOT update `ReceivedCoins` if they were originally sent from the vesting account. For example, if a vesting account unbonds from a validator, their tokens should be added back to account but staking handlers SHOULD NOT update `ReceivedCoins`. +However when a user sends coins to vesting account, then `ReceivedCoins` SHOULD be incremented. ### Initializing at Genesis @@ -117,10 +117,10 @@ initChainer: } if gacc.EndTime != 0: vestingAccount := ContinuouslyVestingAccount{ - BaseAccount: baseAccount, - OriginalCoins: gacc.GenesisCoins, - StartTime: RequestInitChain.Time, - EndTime: gacc.EndTime, + BaseAccount: baseAccount, + OriginalVestingCoins: gacc.GenesisCoins, + StartTime: RequestInitChain.Time, + EndTime: gacc.EndTime, } AddAccountToState(vestingAccount) else: @@ -130,7 +130,7 @@ initChainer: ### Formulas -`OriginalCoins`: Amount of coins in account at Genesis +`OriginalVestingCoins`: Amount of coins in account at Genesis `CurrentCoins`: Coins currently in the baseaccount (both locked and unlocked: `vestingAccount.GetCoins`) @@ -144,16 +144,16 @@ initChainer: Maximum amount of coins vesting schedule allows to be sent: -`ReceivedCoins - SentCoins + OriginalCoins * (Now - StartTime) / (EndTime - StartTime)` +`ReceivedCoins - SentCoins + OriginalVestingCoins * (Now - StartTime) / (EndTime - StartTime)` -`ReceivedCoins - SentCoins + OriginalCoins - LockedCoins` +`ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins` Coins currently in Account: -`CurrentCoins = OriginalCoins + ReceivedCoins - Delegated - Sent` +`CurrentCoins = OriginalVestingCoins + ReceivedCoins - Delegated - Sent` `CurrentCoins = vestingAccount.GetCoins()` **Maximum amount of coins spendable right now:** -`min( ReceivedCoins - SentCoins + OriginalCoins - LockedCoins, CurrentCoins )` +`min( ReceivedCoins - SentCoins + OriginalVestingCoins - LockedCoins, CurrentCoins )` From 8747eaac7df4c962d2b4fc35d7926de83463eca5 Mon Sep 17 00:00:00 2001 From: Max Levy <35595512+maxim-levy@users.noreply.github.com> Date: Mon, 20 Aug 2018 11:22:46 +0900 Subject: [PATCH 16/36] A URL to abci-spec.md fixed /docs/abci-spec.md -> /docs/app-dev/abci-spec.md --- docs/sdk/core/app4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdk/core/app4.md b/docs/sdk/core/app4.md index 0724e5e7a8..b6596ef0cf 100644 --- a/docs/sdk/core/app4.md +++ b/docs/sdk/core/app4.md @@ -16,7 +16,7 @@ here we will introduce the other ABCI requests sent by Tendermint, and how we can use them to build more advanced applications. For a more complete depiction of the ABCI and how its used, see [the -specification](https://github.com/tendermint/tendermint/blob/master/docs/abci-spec.md) +specification](https://github.com/tendermint/tendermint/blob/master/docs/app-dev/abci-spec.md) ## InitChain From 459f126e9b1afd6ec2968b269564ab9b2c6ed9f2 Mon Sep 17 00:00:00 2001 From: Max Levy <35595512+maxim-levy@users.noreply.github.com> Date: Mon, 20 Aug 2018 11:29:03 +0900 Subject: [PATCH 17/36] The URL to abci-spec.md fixed docs/abci-spec.md -> docs/app-dev/abci-spec.md --- docs/sdk/core/app1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sdk/core/app1.md b/docs/sdk/core/app1.md index 54121d05bf..a2978ffb08 100644 --- a/docs/sdk/core/app1.md +++ b/docs/sdk/core/app1.md @@ -469,7 +469,7 @@ Tendermint consensus engine. It would be initialized by a Genesis file, and it would be driven by blocks of transactions committed by the underlying Tendermint consensus. We'll talk more about ABCI and how this all works a bit later, but feel free to check the -[specification](https://github.com/tendermint/tendermint/blob/master/docs/abci-spec.md). +[specification](https://github.com/tendermint/tendermint/blob/master/docs/app-dev/abci-spec.md). We'll also see how to connect our app to a complete suite of components for running and using a live blockchain application. From 87ca812b0bebe16392d37c91fae04b7b9b2c88df Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Mon, 20 Aug 2018 12:47:04 +0200 Subject: [PATCH 18/36] Correctly read/write governance state --- cmd/gaia/app/app.go | 3 ++- cmd/gaia/app/genesis.go | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 9b318c8b17..eba5f86050 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -185,7 +185,7 @@ func (app *GaiaApp) initChainer(ctx sdk.Context, req abci.RequestInitChain) abci // load the address to pubkey map slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.StakeData) - gov.InitGenesis(ctx, app.govKeeper, gov.DefaultGenesisState()) + gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData) return abci.ResponseInitChain{ Validators: validators, @@ -208,6 +208,7 @@ func (app *GaiaApp) ExportAppStateAndValidators() (appState json.RawMessage, val genState := GenesisState{ Accounts: accounts, StakeData: stake.WriteGenesis(ctx, app.stakeKeeper), + GovData: gov.WriteGenesis(ctx, app.govKeeper), } appState, err = wire.MarshalJSONIndent(app.cdc, genState) if err != nil { diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 0399d66528..24b160a510 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -11,6 +11,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/stake" "github.com/spf13/pflag" @@ -32,6 +33,7 @@ var ( type GenesisState struct { Accounts []GenesisAccount `json:"accounts"` StakeData stake.GenesisState `json:"stake"` + GovData gov.GenesisState `json:"gov"` } // GenesisAccount doesn't need pubkey or sequence @@ -216,6 +218,7 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (genesisState genesisState = GenesisState{ Accounts: genaccs, StakeData: stakeData, + GovData: gov.DefaultGenesisState(), } return } From 517682f17ccf43d11848cd83f369d81ca0ea74f4 Mon Sep 17 00:00:00 2001 From: youjing Date: Tue, 21 Aug 2018 14:54:12 +0800 Subject: [PATCH 19/36] fix Iterator leak, ref to https://github.com/cosmos/cosmos-sdk/issues/2105 --- examples/democoin/x/assoc/validator_set.go | 1 + examples/democoin/x/oracle/handler.go | 1 + store/iavlstore_test.go | 3 +++ x/auth/mapper.go | 1 + x/gov/keeper_test.go | 2 ++ x/gov/tally.go | 2 +- 6 files changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/democoin/x/assoc/validator_set.go b/examples/democoin/x/assoc/validator_set.go index 03ce506ea0..ad89aab192 100644 --- a/examples/democoin/x/assoc/validator_set.go +++ b/examples/democoin/x/assoc/validator_set.go @@ -93,6 +93,7 @@ func (valset ValidatorSet) Dissociate(ctx sdk.Context, base sdk.AccAddress, asso func (valset ValidatorSet) Associations(ctx sdk.Context, base sdk.AccAddress) (res []sdk.AccAddress) { res = make([]sdk.AccAddress, valset.maxAssoc) iter := sdk.KVStorePrefixIterator(valset.store, GetAssocPrefix(base)) + defer iter.Close() i := 0 for ; iter.Valid(); iter.Next() { key := iter.Key() diff --git a/examples/democoin/x/oracle/handler.go b/examples/democoin/x/oracle/handler.go index 3c78fc566b..1aaefc7e16 100644 --- a/examples/democoin/x/oracle/handler.go +++ b/examples/democoin/x/oracle/handler.go @@ -28,6 +28,7 @@ func (keeper Keeper) update(ctx sdk.Context, val sdk.Validator, valset sdk.Valid prefix := GetSignPrefix(p, keeper.cdc) store := ctx.KVStore(keeper.key) iter := sdk.KVStorePrefixIterator(store, prefix) + defer iter.Close() for ; iter.Valid(); iter.Next() { if valset.Validator(ctx, iter.Value()) != nil { store.Delete(iter.Key()) diff --git a/store/iavlstore_test.go b/store/iavlstore_test.go index d081b4576f..38d85c6581 100644 --- a/store/iavlstore_test.go +++ b/store/iavlstore_test.go @@ -168,6 +168,7 @@ func TestIAVLSubspaceIterator(t *testing.T) { require.EqualValues(t, value, expectedKey) i++ } + iter.Close() require.Equal(t, len(expected), i) iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)}) @@ -183,6 +184,7 @@ func TestIAVLSubspaceIterator(t *testing.T) { require.EqualValues(t, value, []byte("test4")) i++ } + iter.Close() require.Equal(t, len(expected), i) iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(255), byte(255)}) @@ -198,6 +200,7 @@ func TestIAVLSubspaceIterator(t *testing.T) { require.EqualValues(t, value, []byte("test4")) i++ } + iter.Close() require.Equal(t, len(expected), i) } diff --git a/x/auth/mapper.go b/x/auth/mapper.go index 244527af3f..c8bf94a21f 100644 --- a/x/auth/mapper.go +++ b/x/auth/mapper.go @@ -87,6 +87,7 @@ func (am AccountMapper) SetAccount(ctx sdk.Context, acc Account) { func (am AccountMapper) IterateAccounts(ctx sdk.Context, process func(Account) (stop bool)) { store := ctx.KVStore(am.key) iter := sdk.KVStorePrefixIterator(store, []byte("account:")) + defer iter.Close() for { if !iter.Valid() { return diff --git a/x/gov/keeper_test.go b/x/gov/keeper_test.go index 442caee901..a61292b93e 100644 --- a/x/gov/keeper_test.go +++ b/x/gov/keeper_test.go @@ -130,6 +130,7 @@ func TestDeposits(t *testing.T) { require.Equal(t, fourSteak, deposit.Amount) depositsIterator.Next() require.False(t, depositsIterator.Valid()) + depositsIterator.Close() // Test Refund Deposits deposit, found = keeper.GetDeposit(ctx, proposalID, addrs[1]) @@ -196,6 +197,7 @@ func TestVotes(t *testing.T) { require.Equal(t, OptionNoWithVeto, vote.Option) votesIterator.Next() require.False(t, votesIterator.Valid()) + votesIterator.Close() } func TestProposalQueues(t *testing.T) { diff --git a/x/gov/tally.go b/x/gov/tally.go index d8d4c78703..cfb113ae33 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -36,6 +36,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall // iterate over all the votes votesIterator := keeper.GetVotes(ctx, proposal.GetProposalID()) + defer votesIterator.Close() for ; votesIterator.Valid(); votesIterator.Next() { vote := &Vote{} keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), vote) @@ -64,7 +65,6 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall keeper.deleteVote(ctx, vote.ProposalID, vote.Voter) } - votesIterator.Close() // Iterate over the validators again to tally their voting power and see who didn't vote nonVoting = []sdk.AccAddress{} From e04e52451bffd85f81e02686985c371946a37332 Mon Sep 17 00:00:00 2001 From: youjing Date: Tue, 21 Aug 2018 20:23:53 +0800 Subject: [PATCH 20/36] Modify PENDING.md for #2105 --- PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/PENDING.md b/PENDING.md index dafd653fe4..f36d613202 100644 --- a/PENDING.md +++ b/PENDING.md @@ -105,3 +105,4 @@ BUG FIXES * \#1787 Fixed bug where Tally fails due to revoked/unbonding validator * [basecoin] Fixes coin transaction failure and account query [discussion](https://forum.cosmos.network/t/unmarshalbinarybare-expected-to-read-prefix-bytes-75fbfab8-since-it-is-registered-concrete-but-got-0a141dfa/664/6) * [cli] \#1997 Handle panics gracefully when `gaiacli stake {delegation,unbond}` fail to unmarshal delegation. +* \#2105 Fix DB Iterator leak, which may leak a go routine. From da30353394b633450e7d3094fdc1355d65a1935f Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 21 Aug 2018 16:39:10 -0400 Subject: [PATCH 21/36] update PR template --- .github/PULL_REQUEST_TEMPLATE.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6a25260591..36b9f97f20 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,14 +5,14 @@ v If a checkbox is n/a - please still include it but + a little note why ☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > --> - [ ] Linked to github-issue with discussion and accepted design OR link to spec that describes this work. -- [ ] Updated all relevant documentation (`docs/`) -- [ ] Updated all relevant code comments - [ ] Wrote tests -- [ ] Added entries in `PENDING.md` that include links to the relevant issue or PR that most accurately describes the change. -- [ ] Updated `cmd/gaia` and `examples/` -- [ ] Read PR `Files changed` in the github PR explorer -___________________________________ +- [ ] Updated relevant documentation (`docs/`) +- [ ] Added entries in `PENDING.md` with issue # +- [ ] rereviewed `Files changed` in the github PR explorer + +______ + For Admin Use: -- [ ] Added appropriate labels to PR (ex. wip, ready-for-review, docs) -- [ ] Reviewers Assigned -- [ ] Squashed all commits, uses message "Merge pull request #XYZ: [title]" ([coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr)) +- Added appropriate labels to PR (ex. wip, ready-for-review, docs) +- Reviewers Assigned +- Squashed all commits, uses message "Merge pull request #XYZ: [title]" ([coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr)) From f7d86752340f7002c764ad206c04a2ccdb567b90 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Thu, 16 Aug 2018 01:03:25 -0700 Subject: [PATCH 22/36] Support a proposal JSON file in submit-proposal Closes #1852. Closes #1776. --- PENDING.md | 1 + .../simple-governance/submit-proposal.md | 17 ++++ x/gov/client/cli/tx.go | 82 +++++++++++++++++-- x/gov/client/cli/tx_test.go | 70 ++++++++++++++++ 4 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 x/gov/client/cli/tx_test.go diff --git a/PENDING.md b/PENDING.md index 7f297c0f37..d1e5ff9388 100644 --- a/PENDING.md +++ b/PENDING.md @@ -27,6 +27,7 @@ FEATURES * Gaia CLI (`gaiacli`) * [cli] Cmds to query staking pool and params + * [gov][cli] #2062 added `--proposal` flag to `submit-proposal` that allows a JSON file containing a proposal to be passed in * Gaia diff --git a/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md b/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md index bb9eb289f4..57571c1511 100644 --- a/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md +++ b/docs/sdk/sdk-by-examples/simple-governance/submit-proposal.md @@ -6,6 +6,23 @@ Uuse the CLI to create a new proposal: simplegovcli propose --title="Voting Period update" --description="Should we change the proposal voting period to 3 weeks?" --deposit=300Atoms ``` +Or, via a json file: + +```bash +simplegovcli propose --proposal="path/to/proposal.json" +``` + +Where proposal.json contains: + +```json +{ + "title": "Voting Period Update", + "description": "Should we change the proposal voting period to 3 weeks?", + "type": "Text", + "deposit": "300Atoms" +} +``` + Get the details of your newly created proposal: ```bash diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index d19de9f07f..44ca104cf4 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -15,6 +15,9 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" + "io/ioutil" + "encoding/json" + "strings" ) const ( @@ -28,18 +31,51 @@ const ( flagDepositer = "depositer" flagStatus = "status" flagLatestProposalIDs = "latest" + flagProposal = "proposal" ) +type proposal struct { + Title string + Description string + Type string + Deposit string +} + +var proposalFlags = []string{ + flagTitle, + flagDescription, + flagProposalType, + flagDeposit, +} + // GetCmdSubmitProposal implements submitting a proposal transaction command. func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "submit-proposal", Short: "Submit a proposal along with an initial deposit", + Long: strings.TrimSpace(` +Submit a proposal along with an initial deposit. Proposal title, description, type and deposit can be given directly or through a proposal JSON file. For example: + +$ gaiacli gov submit-proposal --proposal="path/to/proposal.json" + +where proposal.json contains: + +{ + "title": "Test Proposal", + "description": "My awesome proposal", + "type": "Text", + "deposit": "1000test" +} + +is equivalent to + +$ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome proposal" --type="Text" --deposit="1000test" +`), RunE: func(cmd *cobra.Command, args []string) error { - title := viper.GetString(flagTitle) - description := viper.GetString(flagDescription) - strProposalType := viper.GetString(flagProposalType) - initialDeposit := viper.GetString(flagDeposit) + proposal, err := parseSubmitProposalFlags() + if err != nil { + return err + } txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). @@ -52,17 +88,17 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command { return err } - amount, err := sdk.ParseCoins(initialDeposit) + amount, err := sdk.ParseCoins(proposal.Deposit) if err != nil { return err } - proposalType, err := gov.ProposalTypeFromString(strProposalType) + proposalType, err := gov.ProposalTypeFromString(proposal.Type) if err != nil { return err } - msg := gov.NewMsgSubmitProposal(title, description, proposalType, fromAddr, amount) + msg := gov.NewMsgSubmitProposal(proposal.Title, proposal.Description, proposalType, fromAddr, amount) err = msg.ValidateBasic() if err != nil { @@ -80,10 +116,42 @@ func GetCmdSubmitProposal(cdc *wire.Codec) *cobra.Command { cmd.Flags().String(flagDescription, "", "description of proposal") cmd.Flags().String(flagProposalType, "", "proposalType of proposal") cmd.Flags().String(flagDeposit, "", "deposit of proposal") + cmd.Flags().String(flagProposal, "", "proposal file path (if this path is given, other proposal flags are ignored)") return cmd } +func parseSubmitProposalFlags() (*proposal, error) { + proposal := &proposal{} + proposalFile := viper.GetString(flagProposal) + + if proposalFile == "" { + proposal.Title = viper.GetString(flagTitle) + proposal.Description = viper.GetString(flagDescription) + proposal.Type = viper.GetString(flagProposalType) + proposal.Deposit = viper.GetString(flagDeposit) + return proposal, nil + } + + for _, flag := range proposalFlags { + if viper.GetString(flag) != "" { + return nil, fmt.Errorf("--%s flag provided alongside --proposal, which is a noop", flag) + } + } + + contents, err := ioutil.ReadFile(proposalFile) + if err != nil { + return nil, err + } + + err = json.Unmarshal(contents, proposal) + if err != nil { + return nil, err + } + + return proposal, nil +} + // GetCmdDeposit implements depositing tokens for an active proposal. func GetCmdDeposit(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ diff --git a/x/gov/client/cli/tx_test.go b/x/gov/client/cli/tx_test.go new file mode 100644 index 0000000000..0ddf992d9b --- /dev/null +++ b/x/gov/client/cli/tx_test.go @@ -0,0 +1,70 @@ +package cli + +import ( + "testing" + "github.com/spf13/viper" + "io/ioutil" + "github.com/stretchr/testify/require" +) + +func TestParseSubmitProposalFlags(t *testing.T) { + okJSON, err := ioutil.TempFile("", "proposal") + require.Nil(t, err, "unexpected error") + okJSON.WriteString(` +{ + "title": "Test Proposal", + "description": "My awesome proposal", + "type": "Text", + "deposit": "1000test" +} +`) + + badJSON, err := ioutil.TempFile("", "proposal") + require.Nil(t, err, "unexpected error") + badJSON.WriteString("bad json") + + // nonexistent json + viper.Set(flagProposal, "fileDoesNotExist") + _, err = parseSubmitProposalFlags() + require.Error(t, err) + + // invalid json + viper.Set(flagProposal, badJSON.Name()) + _, err = parseSubmitProposalFlags() + require.Error(t, err) + + // ok json + viper.Set(flagProposal, okJSON.Name()) + proposal1, err := parseSubmitProposalFlags() + require.Nil(t, err, "unexpected error") + require.Equal(t, "Test Proposal", proposal1.Title) + require.Equal(t, "My awesome proposal", proposal1.Description) + require.Equal(t, "Text", proposal1.Type) + require.Equal(t, "1000test", proposal1.Deposit) + + // flags that can't be used with --proposal + for _, incompatibleFlag := range proposalFlags { + viper.Set(incompatibleFlag, "some value") + _, err := parseSubmitProposalFlags() + require.Error(t, err) + viper.Set(incompatibleFlag, "") + } + + // no --proposal, only flags + viper.Set(flagProposal, "") + viper.Set(flagTitle, proposal1.Title) + viper.Set(flagDescription, proposal1.Description) + viper.Set(flagProposalType, proposal1.Type) + viper.Set(flagDeposit, proposal1.Deposit) + proposal2, err := parseSubmitProposalFlags() + require.Nil(t, err, "unexpected error") + require.Equal(t, proposal1.Title, proposal2.Title) + require.Equal(t, proposal1.Description, proposal2.Description) + require.Equal(t, proposal1.Type, proposal2.Type) + require.Equal(t, proposal1.Deposit, proposal2.Deposit) + + err = okJSON.Close() + require.Nil(t, err, "unexpected error") + err = badJSON.Close() + require.Nil(t, err, "unexpected error") +} From 0c0d282a0c3ab5a08f98493776c36cf202bb7742 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Fri, 10 Aug 2018 23:42:57 -0700 Subject: [PATCH 23/36] Add plaintext flags to show keys; default print response to true; helptext updates Closes #1970 Closes #1971 Closes #1967 Closes #1969 --- Gopkg.lock | 197 ++++++------------------------------------- PENDING.md | 2 + client/flags.go | 2 +- client/keys/show.go | 43 +++++++++- client/keys/utils.go | 18 +++- client/tx/search.go | 17 +++- server/tm_cmds.go | 2 +- 7 files changed, 99 insertions(+), 182 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 38830c49e9..e94ad3ec0d 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,76 +2,57 @@ [[projects]] - digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0" name = "github.com/bartekn/go-bip39" packages = ["."] - pruneopts = "UT" revision = "a05967ea095d81c8fe4833776774cfaff8e5036c" [[projects]] branch = "master" - digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" name = "github.com/beorn7/perks" packages = ["quantile"] - pruneopts = "UT" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] - digest = "1:1343a2963481a305ca4d051e84bc2abd16b601ee22ed324f8d605de1adb291b0" name = "github.com/bgentry/speakeasy" packages = ["."] - pruneopts = "UT" revision = "4aabc24848ce5fd31929f7d1e4ea74d3709c14cd" version = "v0.1.0" [[projects]] branch = "master" - digest = "1:70f6b224a59b2fa453debffa85c77f71063d8754b90c8c4fbad5794e2c382b0f" name = "github.com/brejski/hid" packages = ["."] - pruneopts = "UT" revision = "06112dcfcc50a7e0e4fd06e17f9791e788fdaafc" [[projects]] branch = "master" - digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8" name = "github.com/btcsuite/btcd" packages = ["btcec"] - pruneopts = "UT" revision = "f899737d7f2764dc13e4d01ff00108ec58f766a9" [[projects]] - digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2" name = "github.com/btcsuite/btcutil" packages = ["bech32"] - pruneopts = "UT" revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" [[projects]] - digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" name = "github.com/davecgh/go-spew" packages = ["spew"] - pruneopts = "UT" revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" version = "v1.1.1" [[projects]] - digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b" name = "github.com/ebuchman/fail-test" packages = ["."] - pruneopts = "UT" revision = "95f809107225be108efcf10a3509e4ea6ceef3c4" [[projects]] - digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" name = "github.com/fsnotify/fsnotify" packages = ["."] - pruneopts = "UT" revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" version = "v1.4.7" [[projects]] - digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11" name = "github.com/go-kit/kit" packages = [ "log", @@ -80,30 +61,24 @@ "metrics", "metrics/discard", "metrics/internal/lv", - "metrics/prometheus", + "metrics/prometheus" ] - pruneopts = "UT" revision = "4dc7be5d2d12881735283bcab7352178e190fc71" version = "v0.6.0" [[projects]] - digest = "1:31a18dae27a29aa074515e43a443abfd2ba6deb6d69309d8d7ce789c45f34659" name = "github.com/go-logfmt/logfmt" packages = ["."] - pruneopts = "UT" revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" version = "v0.3.0" [[projects]] - digest = "1:c4a2528ccbcabf90f9f3c464a5fc9e302d592861bbfd0b7135a7de8a943d0406" name = "github.com/go-stack/stack" packages = ["."] - pruneopts = "UT" revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" version = "v1.7.0" [[projects]] - digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e" name = "github.com/gogo/protobuf" packages = [ "gogoproto", @@ -111,272 +86,213 @@ "proto", "protoc-gen-gogo/descriptor", "sortkeys", - "types", + "types" ] - pruneopts = "UT" revision = "636bf0302bc95575d69441b25a2603156ffdddf1" version = "v1.1.1" [[projects]] - digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260" name = "github.com/golang/protobuf" packages = [ "proto", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/timestamp", + "ptypes/timestamp" ] - pruneopts = "UT" revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" version = "v1.1.0" [[projects]] branch = "master" - digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009" name = "github.com/golang/snappy" packages = ["."] - pruneopts = "UT" revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" [[projects]] - digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1" name = "github.com/gorilla/context" packages = ["."] - pruneopts = "UT" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] - digest = "1:e73f5b0152105f18bc131fba127d9949305c8693f8a762588a82a48f61756f5f" name = "github.com/gorilla/mux" packages = ["."] - pruneopts = "UT" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" [[projects]] - digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e" name = "github.com/gorilla/websocket" packages = ["."] - pruneopts = "UT" revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" version = "v1.2.0" [[projects]] branch = "master" - digest = "1:a361611b8c8c75a1091f00027767f7779b29cb37c456a71b8f2604c88057ab40" name = "github.com/hashicorp/hcl" packages = [ ".", "hcl/ast", "hcl/parser", - "hcl/printer", "hcl/scanner", "hcl/strconv", "hcl/token", "json/parser", "json/scanner", - "json/token", + "json/token" ] - pruneopts = "UT" revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168" [[projects]] - digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] - pruneopts = "UT" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] branch = "master" - digest = "1:39b27d1381a30421f9813967a5866fba35dc1d4df43a6eefe3b7a5444cb07214" name = "github.com/jmhodges/levigo" packages = ["."] - pruneopts = "UT" revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9" [[projects]] branch = "master" - digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72" name = "github.com/kr/logfmt" packages = ["."] - pruneopts = "UT" revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" [[projects]] - digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7" name = "github.com/magiconair/properties" packages = ["."] - pruneopts = "UT" revision = "c2353362d570a7bfa228149c62842019201cfb71" version = "v1.8.0" [[projects]] - digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb" name = "github.com/mattn/go-isatty" packages = ["."] - pruneopts = "UT" revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" version = "v0.0.3" [[projects]] - digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] - pruneopts = "UT" revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" version = "v1.0.1" [[projects]] branch = "master" - digest = "1:5ab79470a1d0fb19b041a624415612f8236b3c06070161a910562f2b2d064355" name = "github.com/mitchellh/mapstructure" packages = ["."] - pruneopts = "UT" revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac" [[projects]] - digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" name = "github.com/pelletier/go-toml" packages = ["."] - pruneopts = "UT" revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" version = "v1.2.0" [[projects]] - digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" name = "github.com/pkg/errors" packages = ["."] - pruneopts = "UT" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] - digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" name = "github.com/pmezard/go-difflib" packages = ["difflib"] - pruneopts = "UT" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] - digest = "1:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0" name = "github.com/prometheus/client_golang" packages = [ "prometheus", - "prometheus/promhttp", + "prometheus/promhttp" ] - pruneopts = "UT" revision = "ae27198cdd90bf12cd134ad79d1366a6cf49f632" [[projects]] branch = "master" - digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" name = "github.com/prometheus/client_model" packages = ["go"] - pruneopts = "UT" revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f" [[projects]] branch = "master" - digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", - "model", + "model" ] - pruneopts = "UT" revision = "c7de2306084e37d54b8be01f3541a8464345e9a5" [[projects]] branch = "master" - digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290" name = "github.com/prometheus/procfs" packages = [ ".", "internal/util", "nfs", - "xfs", + "xfs" ] - pruneopts = "UT" revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92" [[projects]] - digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c" name = "github.com/rcrowley/go-metrics" packages = ["."] - pruneopts = "UT" revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] - digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84" name = "github.com/spf13/afero" packages = [ ".", - "mem", + "mem" ] - pruneopts = "UT" revision = "787d034dfe70e44075ccc060d346146ef53270ad" version = "v1.1.1" [[projects]] - digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f" name = "github.com/spf13/cast" packages = ["."] - pruneopts = "UT" revision = "8965335b8c7107321228e3e3702cab9832751bac" version = "v1.2.0" [[projects]] - digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e" name = "github.com/spf13/cobra" packages = ["."] - pruneopts = "UT" revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b" version = "v0.0.1" [[projects]] branch = "master" - digest = "1:8a020f916b23ff574845789daee6818daf8d25a4852419aae3f0b12378ba432a" name = "github.com/spf13/jwalterweatherman" packages = ["."] - pruneopts = "UT" revision = "14d3d4c518341bea657dd8a226f5121c0ff8c9f2" [[projects]] - digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9" name = "github.com/spf13/pflag" packages = ["."] - pruneopts = "UT" revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" version = "v1.0.2" [[projects]] - digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96" name = "github.com/spf13/viper" packages = ["."] - pruneopts = "UT" revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7" version = "v1.0.0" [[projects]] - digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6" name = "github.com/stretchr/testify" packages = [ "assert", - "require", + "require" ] - pruneopts = "UT" revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" version = "v1.2.1" [[projects]] branch = "master" - digest = "1:f2ffd421680b0a3f7887501b3c6974bcf19217ecd301d0e2c9b681940ec363d5" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -390,41 +306,33 @@ "leveldb/opt", "leveldb/storage", "leveldb/table", - "leveldb/util", + "leveldb/util" ] - pruneopts = "UT" revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd" [[projects]] branch = "master" - digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722" name = "github.com/tendermint/ed25519" packages = [ ".", "edwards25519", - "extra25519", + "extra25519" ] - pruneopts = "UT" revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" [[projects]] - digest = "1:e0a2a4be1e20c305badc2b0a7a9ab7fef6da500763bec23ab81df3b5f9eec9ee" name = "github.com/tendermint/go-amino" packages = ["."] - pruneopts = "UT" revision = "a8328986c1608950fa5d3d1c0472cccc4f8fc02c" version = "v0.12.0-rc0" [[projects]] - digest = "1:d4a15d404afbf591e8be16fcda7f5ac87948d5c7531f9d909fd84cc730ab16e2" name = "github.com/tendermint/iavl" packages = ["."] - pruneopts = "UT" revision = "35f66e53d9b01e83b30de68b931f54b2477a94c9" version = "v0.9.2" [[projects]] - digest = "1:26146cdb2811ce481e72138439b9b1aa17a64d54364f96bb92f97a9ef8ba4f01" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -484,22 +392,24 @@ "state/txindex/kv", "state/txindex/null", "types", - "version", + "version" ] - pruneopts = "UT" revision = "013b9cef642f875634c614019ab13b17570778ad" version = "v0.23.0" [[projects]] - digest = "1:4dcb0dd65feecb068ce23a234d1a07c7868a1e39f52a6defcae0bb371d03abf6" + name = "github.com/tendermint/tmlibs" + packages = ["cli"] + revision = "49596e0a1f48866603813df843c9409fc19805c6" + version = "v0.9.0" + +[[projects]] name = "github.com/zondax/ledger-goclient" packages = ["."] - pruneopts = "UT" revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259" [[projects]] branch = "master" - digest = "1:7a71fffde456d746c52f9cd09c50b034533a3180fb1f6320abb149f2ccc579e5" name = "golang.org/x/crypto" packages = [ "blowfish", @@ -515,13 +425,11 @@ "pbkdf2", "poly1305", "ripemd160", - "salsa20/salsa", + "salsa20/salsa" ] - pruneopts = "UT" revision = "aabede6cba87e37f413b3e60ebfc214f8eeca1b0" [[projects]] - digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" name = "golang.org/x/net" packages = [ "context", @@ -531,24 +439,20 @@ "idna", "internal/timeseries", "netutil", - "trace", + "trace" ] - pruneopts = "UT" revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" [[projects]] branch = "master" - digest = "1:ead82e3e398388679f3ad77633a087ac31a47a6be59ae20841e1d1b3a3fbbd22" name = "golang.org/x/sys" packages = [ "cpu", - "unix", + "unix" ] - pruneopts = "UT" revision = "1a700e749ce29638d0bbcb531cce1094ea096bd3" [[projects]] - digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" name = "golang.org/x/text" packages = [ "collate", @@ -564,22 +468,18 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable", + "unicode/rangetable" ] - pruneopts = "UT" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] branch = "master" - digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] - pruneopts = "UT" revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4" [[projects]] - digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74" name = "google.golang.org/grpc" packages = [ ".", @@ -606,69 +506,20 @@ "stats", "status", "tap", - "transport", + "transport" ] - pruneopts = "UT" revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8" version = "v1.13.0" [[projects]] - digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" name = "gopkg.in/yaml.v2" packages = ["."] - pruneopts = "UT" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - input-imports = [ - "github.com/bartekn/go-bip39", - "github.com/bgentry/speakeasy", - "github.com/btcsuite/btcd/btcec", - "github.com/golang/protobuf/proto", - "github.com/gorilla/mux", - "github.com/mattn/go-isatty", - "github.com/pkg/errors", - "github.com/spf13/cobra", - "github.com/spf13/pflag", - "github.com/spf13/viper", - "github.com/stretchr/testify/assert", - "github.com/stretchr/testify/require", - "github.com/tendermint/go-amino", - "github.com/tendermint/iavl", - "github.com/tendermint/tendermint/abci/server", - "github.com/tendermint/tendermint/abci/types", - "github.com/tendermint/tendermint/cmd/tendermint/commands", - "github.com/tendermint/tendermint/config", - "github.com/tendermint/tendermint/crypto", - "github.com/tendermint/tendermint/crypto/armor", - "github.com/tendermint/tendermint/crypto/ed25519", - "github.com/tendermint/tendermint/crypto/encoding/amino", - "github.com/tendermint/tendermint/crypto/merkle", - "github.com/tendermint/tendermint/crypto/secp256k1", - "github.com/tendermint/tendermint/crypto/tmhash", - "github.com/tendermint/tendermint/crypto/xsalsa20symmetric", - "github.com/tendermint/tendermint/libs/bech32", - "github.com/tendermint/tendermint/libs/cli", - "github.com/tendermint/tendermint/libs/cli/flags", - "github.com/tendermint/tendermint/libs/common", - "github.com/tendermint/tendermint/libs/db", - "github.com/tendermint/tendermint/libs/log", - "github.com/tendermint/tendermint/node", - "github.com/tendermint/tendermint/p2p", - "github.com/tendermint/tendermint/privval", - "github.com/tendermint/tendermint/proxy", - "github.com/tendermint/tendermint/rpc/client", - "github.com/tendermint/tendermint/rpc/core/types", - "github.com/tendermint/tendermint/rpc/lib/client", - "github.com/tendermint/tendermint/rpc/lib/server", - "github.com/tendermint/tendermint/types", - "github.com/tendermint/tendermint/version", - "github.com/zondax/ledger-goclient", - "golang.org/x/crypto/blowfish", - "golang.org/x/crypto/ripemd160", - ] + inputs-digest = "46c912cbb9fa9b2ab3529af18718edada64e59822c8a2e432aec9affa3e24f45" solver-name = "gps-cdcl" solver-version = 1 diff --git a/PENDING.md b/PENDING.md index 7f297c0f37..8ef5fa1e81 100644 --- a/PENDING.md +++ b/PENDING.md @@ -8,6 +8,8 @@ BREAKING CHANGES * Gaia CLI (`gaiacli`) * [x/stake] Validator.Owner renamed to Validator.Operator * [cli] unsafe_reset_all, show_validator, and show_node_id have been renamed to unsafe-reset-all, show-validator, and show-node-id + * [cli] \#1983 --print-response now defaults to true in commands that create and send a transaction + * [cli] \#1983 you can now pass --pubkey or --address to gaiacli keys show to return a plaintext representation of the key's address or public key for use with other commands * Gaia * Make the transient store key use a distinct store key. [#2013](https://github.com/cosmos/cosmos-sdk/pull/2013) diff --git a/client/flags.go b/client/flags.go index 8616f9e78d..b020789057 100644 --- a/client/flags.go +++ b/client/flags.go @@ -52,7 +52,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().Int64(FlagGas, 200000, "gas limit to set per-transaction") c.Flags().Bool(FlagAsync, false, "broadcast transactions asynchronously") c.Flags().Bool(FlagJson, false, "return output in json format") - c.Flags().Bool(FlagPrintResponse, false, "return tx response (only works with async = false)") + c.Flags().Bool(FlagPrintResponse, true, "return tx response (only works with async = false)") } return cmds } diff --git a/client/keys/show.go b/client/keys/show.go index 873c45a4b6..7dbbc3028e 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -4,10 +4,20 @@ import ( "encoding/json" "net/http" - keys "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/gorilla/mux" "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/pkg/errors" + "github.com/tendermint/tmlibs/cli" +) + +const ( + // FlagAddress is the flag for the user's address on the command line. + FlagAddress = "address" + // FlagPublicKey represents the user's public key on the command line. + FlagPublicKey = "pubkey" ) var showKeysCmd = &cobra.Command{ @@ -18,13 +28,38 @@ var showKeysCmd = &cobra.Command{ RunE: func(cmd *cobra.Command, args []string) error { name := args[0] info, err := getKey(name) - if err == nil { - printInfo(info) + if err != nil { + return err } - return err + + showAddress := viper.GetBool(FlagAddress) + showPublicKey := viper.GetBool(FlagPublicKey) + outputSet := cmd.Flag(cli.OutputFlag).Changed + if showAddress && showPublicKey { + return errors.New("cannot use both --address and --pubkey at once") + } + if outputSet && (showAddress || showPublicKey) { + return errors.New("cannot use --output with --address or --pubkey") + } + if showAddress { + printKeyAddress(info) + return nil + } + if showPublicKey { + printPubKey(info) + return nil + } + + printInfo(info) + return nil }, } +func init() { + showKeysCmd.Flags().Bool(FlagAddress, false, "output the address only (overrides --output)") + showKeysCmd.Flags().Bool(FlagPublicKey, false, "output the public key only (overrides --output)") +} + func getKey(name string) (keys.Info, error) { kb, err := GetKeyBase() if err != nil { diff --git a/client/keys/utils.go b/client/keys/utils.go index 907f9eda80..aa1b4bed9c 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -6,7 +6,7 @@ import ( "github.com/spf13/viper" - keys "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/tendermint/tendermint/libs/cli" dbm "github.com/tendermint/tendermint/libs/db" @@ -173,3 +173,19 @@ func printInfos(infos []keys.Info) { func printKeyOutput(ko KeyOutput) { fmt.Printf("%s\t%s\t%s\t%s\n", ko.Name, ko.Type, ko.Address, ko.PubKey) } + +func printKeyAddress(info keys.Info) { + ko, err := Bech32KeyOutput(info) + if err != nil { + panic(err) + } + fmt.Println(ko.Address.String()) +} + +func printPubKey(info keys.Info) { + ko, err := Bech32KeyOutput(info) + if err != nil { + panic(err) + } + fmt.Println(ko.PubKey) +} diff --git a/client/tx/search.go b/client/tx/search.go index adad29d7dd..06b3c09729 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -27,7 +27,20 @@ const ( func SearchTxCmd(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "txs", - Short: "Search for all transactions that match the given tags", + Short: "Search for all transactions that match the given tags.", + Long: strings.TrimSpace(` +Search for transactions that match the given tags. By default, transactions must match ALL tags +passed to the --tags option. To match any transaction, use the --any option. + +For example: + +$ gaiacli tendermint txs --tag test1,test2 + +will match any transaction tagged with both test1,test2. To match a transaction tagged with either +test1 or test2, use: + +$ gaiacli tendermint txs --tag test1,test2 --any +`), RunE: func(cmd *cobra.Command, args []string) error { tags := viper.GetStringSlice(flagTags) @@ -52,7 +65,7 @@ func SearchTxCmd(cdc *wire.Codec) *cobra.Command { // TODO: change this to false once proofs built in cmd.Flags().Bool(client.FlagTrustNode, true, "Don't verify proofs for responses") - cmd.Flags().StringSlice(flagTags, nil, "Tags that must match (may provide multiple)") + cmd.Flags().StringSlice(flagTags, nil, "Comma-separated list of tags that must match") cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") return cmd } diff --git a/server/tm_cmds.go b/server/tm_cmds.go index f852b4029a..c2395e3996 100644 --- a/server/tm_cmds.go +++ b/server/tm_cmds.go @@ -69,7 +69,7 @@ func ShowValidatorCmd(ctx *Context) *cobra.Command { func UnsafeResetAllCmd(ctx *Context) *cobra.Command { return &cobra.Command{ Use: "unsafe-reset-all", - Short: "Reset blockchain database, priv_validator.json file, and the logger", + Short: "Resets the blockchain database, removes address book files, and resets priv_validator.json to the genesis state", RunE: func(cmd *cobra.Command, args []string) error { cfg := ctx.Config tcmd.ResetAll(cfg.DBDir(), cfg.P2P.AddrBookFile(), cfg.PrivValidatorFile(), ctx.Logger) From 1d1a95656a185586d5aeba1787a102fffecbe6e3 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Tue, 31 Jul 2018 18:48:32 -0700 Subject: [PATCH 24/36] custom queriables --- baseapp/baseapp.go | 65 +++++++++++++++++++++++++++++++++--------- baseapp/queryrouter.go | 51 +++++++++++++++++++++++++++++++++ types/queryable.go | 7 +++++ x/gov/queryable.go | 56 ++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 14 deletions(-) create mode 100644 baseapp/queryrouter.go create mode 100644 types/queryable.go create mode 100644 x/gov/queryable.go diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index cf63f1f4d2..22f5927909 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -41,13 +41,14 @@ const ( // BaseApp reflects the ABCI application implementation. type BaseApp struct { // initialized on creation - Logger log.Logger - name string // application name from abci.Info - db dbm.DB // common DB backend - cms sdk.CommitMultiStore // Main (uncached) state - router Router // handle any kind of message - codespacer *sdk.Codespacer // handle module codespacing - txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx + Logger log.Logger + name string // application name from abci.Info + db dbm.DB // common DB backend + cms sdk.CommitMultiStore // Main (uncached) state + router Router // handle any kind of message + queryrouter QueryRouter // router for redirecting query calls + codespacer *sdk.Codespacer // handle module codespacing + txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx anteHandler sdk.AnteHandler // ante handler for fee and auth @@ -84,13 +85,14 @@ var _ abci.Application = (*BaseApp)(nil) // Accepts variable number of option functions, which act on the BaseApp to set configuration choices func NewBaseApp(name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp)) *BaseApp { app := &BaseApp{ - Logger: logger, - name: name, - db: db, - cms: store.NewCommitMultiStore(db), - router: NewRouter(), - codespacer: sdk.NewCodespacer(), - txDecoder: txDecoder, + Logger: logger, + name: name, + db: db, + cms: store.NewCommitMultiStore(db), + router: NewRouter(), + queryrouter: NewQueryRouter(), + codespacer: sdk.NewCodespacer(), + txDecoder: txDecoder, } // Register the undefined & root codespaces, which should not be used by @@ -135,6 +137,31 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) { app.cms.MountStoreWithDB(key, typ, nil) } +<<<<<<< HEAD +======= +// nolint - Set functions +func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) { + app.initChainer = initChainer +} +func (app *BaseApp) SetBeginBlocker(beginBlocker sdk.BeginBlocker) { + app.beginBlocker = beginBlocker +} +func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) { + app.endBlocker = endBlocker +} +func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) { + app.anteHandler = ah +} +func (app *BaseApp) SetAddrPeerFilter(pf sdk.PeerFilter) { + app.addrPeerFilter = pf +} +func (app *BaseApp) SetPubKeyPeerFilter(pf sdk.PeerFilter) { + app.pubkeyPeerFilter = pf +} +func (app *BaseApp) Router() Router { return app.router } +func (app *BaseApp) QueryRouter() QueryRouter { return app.queryrouter } + +>>>>>>> custom queriables // load latest application version func (app *BaseApp) LoadLatestVersion(mainKey sdk.StoreKey) error { err := app.cms.LoadLatestVersion() @@ -291,6 +318,8 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { return handleQueryStore(app, path, req) case "p2p": return handleQueryP2P(app, path, req) + case "custom": + return handleQueryCustom(app, path, req) } msg := "unknown query path" @@ -362,6 +391,14 @@ func handleQueryP2P(app *BaseApp, path []string, req abci.RequestQuery) (res abc return sdk.ErrUnknownRequest(msg).QueryResult() } +func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { + // "/custom" prefix for keeper queries + queryable := app.queryrouter.Route(path[1]) + ctx := app.checkState.ctx + res, err := queryable.Query(ctx, path[2:], req) + return +} + // BeginBlock implements the ABCI application interface. func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { if app.cms.TracingEnabled() { diff --git a/baseapp/queryrouter.go b/baseapp/queryrouter.go new file mode 100644 index 0000000000..0a6b4dfb19 --- /dev/null +++ b/baseapp/queryrouter.go @@ -0,0 +1,51 @@ +package baseapp + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// QueryRouter provides queryables for each query path. +type QueryRouter interface { + AddRoute(r string, h sdk.CustomQueryable) (rtr QueryRouter) + Route(path string) (h sdk.CustomQueryable) +} + +// map a transaction type to a handler and an initgenesis function +type queryroute struct { + r string + h sdk.CustomQueryable +} + +type queryrouter struct { + routes []queryroute +} + +// nolint +// NewRouter - create new router +// TODO either make Function unexported or make return type (router) Exported +func NewQueryRouter() *queryrouter { + return &queryrouter{ + routes: make([]queryroute, 0), + } +} + +// AddRoute - TODO add description +func (rtr *queryrouter) AddRoute(r string, h sdk.CustomQueryable) QueryRouter { + if !isAlphaNumeric(r) { + panic("route expressions can only contain alphanumeric characters") + } + rtr.routes = append(rtr.routes, queryroute{r, h}) + + return rtr +} + +// Route - TODO add description +// TODO handle expressive matches. +func (rtr *queryrouter) Route(path string) (h sdk.CustomQueryable) { + for _, route := range rtr.routes { + if route.r == path { + return route.h + } + } + return nil +} diff --git a/types/queryable.go b/types/queryable.go new file mode 100644 index 0000000000..9ad36b8d28 --- /dev/null +++ b/types/queryable.go @@ -0,0 +1,7 @@ +package types + +import abci "github.com/tendermint/tendermint/abci/types" + +type CustomQueryable interface { + Query(ctx Context, path []string, req abci.RequestQuery) (res []byte, err Error) +} diff --git a/x/gov/queryable.go b/x/gov/queryable.go new file mode 100644 index 0000000000..059d0d1dbb --- /dev/null +++ b/x/gov/queryable.go @@ -0,0 +1,56 @@ +package gov + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + abci "github.com/tendermint/tendermint/abci/types" +) + +type Querier struct { + keeper Keeper +} + +func NewQuerier(keeper Keeper) { + return Querier{ + keeper: keeper, + } +} + +func (keeper Keeper) Query(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case "tally": + return QueryTally(ctx, path[1:], req) + case "proposal": + return handleMsgSubmitProposal(ctx, keeper, msg) + case MsgVote: + return handleMsgVote(ctx, keeper, msg) + default: + errMsg := "Unrecognized gov msg type" + return sdk.ErrUnknownRequest(errMsg).Result() + } +} + +func QueryProposal(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + var proposalID int64 + err := keeper.cdc.UnmarshalBinary(req.Data, proposalID) + if err != nil { + return []byte{}, sdk.ErrUnknownRequest() + } + proposal := keeper.GetProposal(ctx, proposalID) + if proposal == nil { + return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) + } + return keeper.cdc.MustMarshalBinary(proposal), nil +} + +func QueryTally(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + var proposalID int64 + err := keeper.cdc.UnmarshalBinary(req.Data, proposalID) + if err != nil { + return []byte{}, sdk.ErrUnknownRequest() + } + proposal := keeper.GetProposal(ctx, proposalID) + if proposal == nil { + return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) + } + passes, _ := tally(ctx, keeper, proposal) +} From 804baa70f442c5d23c70bf0bdb934a4fb1659790 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Fri, 3 Aug 2018 12:55:00 -0700 Subject: [PATCH 25/36] added querier to gov module --- baseapp/baseapp.go | 14 ++- baseapp/queryrouter.go | 10 +-- types/queryable.go | 5 +- x/gov/keeper.go | 60 +++++++++++++ x/gov/queryable.go | 189 +++++++++++++++++++++++++++++++++-------- 5 files changed, 230 insertions(+), 48 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 22f5927909..8f08d2abfc 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -393,10 +393,18 @@ func handleQueryP2P(app *BaseApp, path []string, req abci.RequestQuery) (res abc func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { // "/custom" prefix for keeper queries - queryable := app.queryrouter.Route(path[1]) + querier := app.queryrouter.Route(path[1]) ctx := app.checkState.ctx - res, err := queryable.Query(ctx, path[2:], req) - return + resBytes, err := querier(ctx, path[2:], req) + if err != nil { + return abci.ResponseQuery{ + Code: uint32(err.ABCICode()), + } + } + return abci.ResponseQuery{ + Code: uint32(sdk.ABCICodeOK), + Value: resBytes, + } } // BeginBlock implements the ABCI application interface. diff --git a/baseapp/queryrouter.go b/baseapp/queryrouter.go index 0a6b4dfb19..ade071f1f7 100644 --- a/baseapp/queryrouter.go +++ b/baseapp/queryrouter.go @@ -6,14 +6,14 @@ import ( // QueryRouter provides queryables for each query path. type QueryRouter interface { - AddRoute(r string, h sdk.CustomQueryable) (rtr QueryRouter) - Route(path string) (h sdk.CustomQueryable) + AddRoute(r string, h sdk.Querier) (rtr QueryRouter) + Route(path string) (h sdk.Querier) } // map a transaction type to a handler and an initgenesis function type queryroute struct { r string - h sdk.CustomQueryable + h sdk.Querier } type queryrouter struct { @@ -30,7 +30,7 @@ func NewQueryRouter() *queryrouter { } // AddRoute - TODO add description -func (rtr *queryrouter) AddRoute(r string, h sdk.CustomQueryable) QueryRouter { +func (rtr *queryrouter) AddRoute(r string, h sdk.Querier) QueryRouter { if !isAlphaNumeric(r) { panic("route expressions can only contain alphanumeric characters") } @@ -41,7 +41,7 @@ func (rtr *queryrouter) AddRoute(r string, h sdk.CustomQueryable) QueryRouter { // Route - TODO add description // TODO handle expressive matches. -func (rtr *queryrouter) Route(path string) (h sdk.CustomQueryable) { +func (rtr *queryrouter) Route(path string) (h sdk.Querier) { for _, route := range rtr.routes { if route.r == path { return route.h diff --git a/types/queryable.go b/types/queryable.go index 9ad36b8d28..9223332bc8 100644 --- a/types/queryable.go +++ b/types/queryable.go @@ -2,6 +2,5 @@ package types import abci "github.com/tendermint/tendermint/abci/types" -type CustomQueryable interface { - Query(ctx Context, path []string, req abci.RequestQuery) (res []byte, err Error) -} +// Type for querier functions on keepers to implement to handle custom queries +type Querier = func(ctx Context, path []string, req abci.RequestQuery) (res []byte, err Error) diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 8a23ad2485..45af4b471d 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -108,6 +108,52 @@ func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposal Proposal) { store.Delete(KeyProposal(proposal.GetProposalID())) } +// nolint: gocyclo +// Get Proposal from store by ProposalID +func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositerAddr sdk.AccAddress, status ProposalStatus, numLatest int64) []Proposal { + + maxProposalID, err := keeper.peekCurrentProposalID(ctx) + if err != nil { + return nil + } + + matchingProposals := []Proposal{} + + if numLatest <= 0 { + numLatest = maxProposalID + } + + for proposalID := maxProposalID - numLatest; proposalID < maxProposalID; proposalID++ { + if voterAddr != nil && len(voterAddr) != 0 { + _, found := keeper.GetVote(ctx, proposalID, voterAddr) + if !found { + continue + } + } + + if depositerAddr != nil && len(depositerAddr) != 0 { + _, found := keeper.GetDeposit(ctx, proposalID, depositerAddr) + if !found { + continue + } + } + + proposal := keeper.GetProposal(ctx, proposalID) + if proposal == nil { + continue + } + + if validProposalStatus(status) { + if proposal.GetStatus() != status { + continue + } + } + + matchingProposals = append(matchingProposals, proposal) + } + return matchingProposals +} + func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk.Error { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyNextProposalID) @@ -131,6 +177,7 @@ func (keeper Keeper) GetLastProposalID(ctx sdk.Context) (proposalID int64) { return } +// Gets the next available ProposalID and increments it func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sdk.Error) { store := ctx.KVStore(keeper.storeKey) bz := store.Get(KeyNextProposalID) @@ -143,6 +190,19 @@ func (keeper Keeper) getNewProposalID(ctx sdk.Context) (proposalID int64, err sd return proposalID, nil } +// Peeks the next available ProposalID without incrementing it +func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, err sdk.Error) { + store := ctx.KVStore(keeper.storeKey) + bz := store.Get(KeyNextProposalID) + if bz == nil { + return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") + } + keeper.cdc.MustUnmarshalBinary(bz, &proposalID) + bz = keeper.cdc.MustMarshalBinary(proposalID + 1) + store.Set(KeyNextProposalID, bz) + return proposalID, nil +} + func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) { proposal.SetVotingStartBlock(ctx.BlockHeight()) proposal.SetStatus(StatusVotingPeriod) diff --git a/x/gov/queryable.go b/x/gov/queryable.go index 059d0d1dbb..c0e66d0bf7 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -5,52 +5,167 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ) -type Querier struct { - keeper Keeper -} - -func NewQuerier(keeper Keeper) { - return Querier{ - keeper: keeper, +func NewQuerier(keeper Keeper) sdk.Querier { + return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { + switch path[0] { + case "proposal": + return queryProposal(ctx, path[1:], req, keeper) + case "deposit": + return queryDeposit(ctx, path[1:], req, keeper) + case "vote": + return queryVote(ctx, path[1:], req, keeper) + case "deposits": + return queryDeposits(ctx, path[1:], req, keeper) + case "votes": + return queryVotes(ctx, path[1:], req, keeper) + case "proposals": + return queryProposals(ctx, path[1:], req, keeper) + case "tally": + return queryTally(ctx, path[1:], req, keeper) + default: + return nil, sdk.ErrUnknownRequest("unknown gov query endpoint") + } } } -func (keeper Keeper) Query(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { - switch path[0] { - case "tally": - return QueryTally(ctx, path[1:], req) - case "proposal": - return handleMsgSubmitProposal(ctx, keeper, msg) - case MsgVote: - return handleMsgVote(ctx, keeper, msg) - default: - errMsg := "Unrecognized gov msg type" - return sdk.ErrUnknownRequest(errMsg).Result() - } +// Params for query 'custom/gov/proposal' +type QueryProposalParams struct { + ProposalID int64 } -func QueryProposal(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { - var proposalID int64 - err := keeper.cdc.UnmarshalBinary(req.Data, proposalID) - if err != nil { - return []byte{}, sdk.ErrUnknownRequest() +func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryProposalParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") } - proposal := keeper.GetProposal(ctx, proposalID) + + proposal := keeper.GetProposal(ctx, params.ProposalID) if proposal == nil { - return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) + return []byte{}, ErrUnknownProposal(DefaultCodespace, params.ProposalID) } return keeper.cdc.MustMarshalBinary(proposal), nil } -func QueryTally(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) { - var proposalID int64 - err := keeper.cdc.UnmarshalBinary(req.Data, proposalID) - if err != nil { - return []byte{}, sdk.ErrUnknownRequest() - } - proposal := keeper.GetProposal(ctx, proposalID) - if proposal == nil { - return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) - } - passes, _ := tally(ctx, keeper, proposal) +// Params for query 'custom/gov/deposit' +type QueryDepositParams struct { + ProposalID int64 + Depositer sdk.AccAddress +} + +func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryDepositParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, params) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + } + + deposit, _ := keeper.GetDeposit(ctx, params.ProposalID, params.Depositer) + return keeper.cdc.MustMarshalBinary(deposit), nil +} + +// Params for query 'custom/gov/vote' +type QueryVoteParams struct { + ProposalID int64 + Voter sdk.AccAddress +} + +func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryVoteParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + } + + vote, _ := keeper.GetVote(ctx, params.ProposalID, params.Voter) + return keeper.cdc.MustMarshalBinary(vote), nil +} + +// Params for query 'custom/gov/deposits' +type QueryDepositsParams struct { + ProposalID int64 +} + +func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryDepositParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + } + + var deposits []Deposit + depositsIterator := keeper.GetDeposits(ctx, params.ProposalID) + for ; depositsIterator.Valid(); depositsIterator.Next() { + deposit := Deposit{} + keeper.cdc.MustUnmarshalBinary(depositsIterator.Value(), &deposit) + deposits = append(deposits, deposit) + } + + return keeper.cdc.MustMarshalBinary(deposits), nil +} + +// Params for query 'custom/gov/votes' +type QueryVotesParams struct { + ProposalID int64 +} + +func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryVotesParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + } + + var votes []Vote + votesIterator := keeper.GetVotes(ctx, params.ProposalID) + for ; votesIterator.Valid(); votesIterator.Next() { + vote := Vote{} + keeper.cdc.MustUnmarshalBinary(votesIterator.Value(), &vote) + votes = append(votes, vote) + } + + return keeper.cdc.MustMarshalBinary(votes), nil +} + +// Params for query 'custom/gov/proposals' +type QueryProposalsParams struct { + Voter sdk.AccAddress + Depositer sdk.AccAddress + ProposalStatus ProposalStatus + NumLatestProposals int64 +} + +func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + var params QueryProposalsParams + err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + } + + proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.NumLatestProposals) + + bz := keeper.cdc.MustMarshalBinary(proposals) + return bz, nil +} + +// Params for query 'custom/gov/tally' +type QueryTallyParams struct { + ProposalID int64 +} + +func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { + // TODO: Dependant on #1914 + + // var proposalID int64 + // err2 := keeper.cdc.UnmarshalBinary(req.Data, proposalID) + // if err2 != nil { + // return []byte{}, sdk.ErrUnknownRequest() + // } + + // proposal := keeper.GetProposal(ctx, proposalID) + // if proposal == nil { + // return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) + // } + // _, tallyResult, _ := tally(ctx, keeper, proposal) + return nil, nil } From cc1d1fbcf71b73db123cac0b62ab4f63aee98f52 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Fri, 3 Aug 2018 14:43:13 -0700 Subject: [PATCH 26/36] moved queryrouter to behind baseapp seal --- baseapp/baseapp.go | 25 ------------------------- baseapp/setters.go | 6 ++++++ 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 8f08d2abfc..6f143b1562 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -137,31 +137,6 @@ func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) { app.cms.MountStoreWithDB(key, typ, nil) } -<<<<<<< HEAD -======= -// nolint - Set functions -func (app *BaseApp) SetInitChainer(initChainer sdk.InitChainer) { - app.initChainer = initChainer -} -func (app *BaseApp) SetBeginBlocker(beginBlocker sdk.BeginBlocker) { - app.beginBlocker = beginBlocker -} -func (app *BaseApp) SetEndBlocker(endBlocker sdk.EndBlocker) { - app.endBlocker = endBlocker -} -func (app *BaseApp) SetAnteHandler(ah sdk.AnteHandler) { - app.anteHandler = ah -} -func (app *BaseApp) SetAddrPeerFilter(pf sdk.PeerFilter) { - app.addrPeerFilter = pf -} -func (app *BaseApp) SetPubKeyPeerFilter(pf sdk.PeerFilter) { - app.pubkeyPeerFilter = pf -} -func (app *BaseApp) Router() Router { return app.router } -func (app *BaseApp) QueryRouter() QueryRouter { return app.queryrouter } - ->>>>>>> custom queriables // load latest application version func (app *BaseApp) LoadLatestVersion(mainKey sdk.StoreKey) error { err := app.cms.LoadLatestVersion() diff --git a/baseapp/setters.go b/baseapp/setters.go index 86a647d32c..ef88b7fe56 100644 --- a/baseapp/setters.go +++ b/baseapp/setters.go @@ -74,6 +74,12 @@ func (app *BaseApp) Router() Router { } return app.router } +func (app *BaseApp) QueryRouter() QueryRouter { + if app.sealed { + panic("QueryRouter() on sealed BaseApp") + } + return app.queryrouter +} func (app *BaseApp) Seal() { app.sealed = true } func (app *BaseApp) IsSealed() bool { return app.sealed } func (app *BaseApp) enforceSeal() { From 13111176584b6a72bd00e4e5509b133387ed587f Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Sun, 5 Aug 2018 01:56:48 -0400 Subject: [PATCH 27/36] added to gov rest --- client/context/query.go | 5 ++ cmd/gaia/app/app.go | 3 + types/account.go | 23 ++++++ x/gov/client/rest/rest.go | 167 +++++++++++++++++--------------------- x/gov/depositsvotes.go | 32 ++++++++ x/gov/queryable.go | 2 +- 6 files changed, 139 insertions(+), 93 deletions(-) diff --git a/client/context/query.go b/client/context/query.go index 081f723b5c..68676f7415 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -31,6 +31,11 @@ func (ctx CLIContext) Query(path string) (res []byte, err error) { return ctx.query(path, nil) } +// Query information about the connected node with a data payload +func (ctx CLIContext) QueryWithData(path string, data []byte) (res []byte, err error) { + return ctx.query(path, data) +} + // QueryStore performs a query from a Tendermint node with the provided key and // store name. func (ctx CLIContext) QueryStore(key cmn.HexBytes, storeName string) (res []byte, err error) { diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index a96efa6b17..f79ffb3065 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -105,6 +105,9 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, baseAppOptio AddRoute("slashing", slashing.NewHandler(app.slashingKeeper)). AddRoute("gov", gov.NewHandler(app.govKeeper)) + app.QueryRouter(). + AddRoute("gov", gov.NewQuerier(app.govKeeper)) + // initialize BaseApp app.SetInitChainer(app.initChainer) app.SetBeginBlocker(app.BeginBlocker) diff --git a/types/account.go b/types/account.go index 92e2988f24..258a095e8b 100644 --- a/types/account.go +++ b/types/account.go @@ -1,6 +1,7 @@ package types import ( + "bytes" "encoding/hex" "encoding/json" "errors" @@ -108,6 +109,17 @@ func (bz AccAddress) Format(s fmt.State, verb rune) { } } +// Returns boolean for whether two AccAddresses are Equal +func (bz AccAddress) Equals(bz2 AccAddress) bool { + return (bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0) +} + +// Returns boolean for whether an AccAddress is empty +func (bz AccAddress) Empty() bool { + bz2 := AccAddress{} + return bz.Equals(bz2) +} + //__________________________________________________________ // AccAddress a wrapper around bytes meant to represent a validator address @@ -192,6 +204,17 @@ func (bz ValAddress) Format(s fmt.State, verb rune) { } } +// Returns boolean for whether two ValAddresses are Equal +func (bz ValAddress) Equals(bz2 ValAddress) bool { + return (bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0) +} + +// Returns boolean for whether an AccAddress is empty +func (bz ValAddress) Empty() bool { + bz2 := ValAddress{} + return bz.Equals(bz2) +} + // Bech32ifyAccPub takes AccountPubKey and returns the bech32 encoded string func Bech32ifyAccPub(pub crypto.PubKey) (string, error) { return bech32.ConvertAndEncode(Bech32PrefixAccPub, pub.Bytes()) diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 22283a652e..cf5481da8c 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -21,6 +21,7 @@ const ( RestDepositer = "depositer" RestVoter = "voter" RestProposalStatus = "status" + RestNumLatest = "latest" storeName = "gov" ) @@ -190,9 +191,13 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName) - if err != nil || len(res) == 0 { - err := errors.Errorf("proposalID [%d] does not exist", proposalID) + params := gov.QueryProposalParams{ + ProposalID: proposalID, + } + + res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(params)) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return @@ -255,19 +260,14 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName) - if err != nil || len(res) == 0 { - res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName) - if err != nil || len(res) == 0 { - w.WriteHeader(http.StatusNotFound) - err := errors.Errorf("proposalID [%d] does not exist", proposalID) - w.Write([]byte(err.Error())) + params := gov.QueryDepositParams{ + ProposalID: proposalID, + Depositer: depositerAddr, + } - return - } - - w.WriteHeader(http.StatusNotFound) - err = errors.Errorf("depositer [%s] did not deposit on proposalID [%d]", bechDepositerAddr, proposalID) + res, err := cliCtx.QueryWithData("custom/gov/deposit", cdc.MustMarshalBinary(params)) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return @@ -283,7 +283,19 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { return } - + if deposit.Empty() { + res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(gov.QueryProposalParams{params.ProposalID})) + if err != nil || len(res) == 0 { + w.WriteHeader(http.StatusNotFound) + err := errors.Errorf("proposalID [%d] does not exist", proposalID) + w.Write([]byte(err.Error())) + return + } + w.WriteHeader(http.StatusNotFound) + err = errors.Errorf("depositer [%s] did not deposit on proposalID [%d]", bechDepositerAddr, proposalID) + w.Write([]byte(err.Error())) + return + } w.Write(output) } } @@ -328,19 +340,14 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName) - if err != nil || len(res) == 0 { - res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName) - if err != nil || len(res) == 0 { - w.WriteHeader(http.StatusNotFound) - err := errors.Errorf("proposalID [%d] does not exist", proposalID) - w.Write([]byte(err.Error())) + params := gov.QueryVoteParams{ + Voter: voterAddr, + ProposalID: proposalID, + } - return - } - - w.WriteHeader(http.StatusNotFound) - err = errors.Errorf("voter [%s] did not vote on proposalID [%d]", bechVoterAddr, proposalID) + res, err := cliCtx.QueryWithData("custom/gov/vote", cdc.MustMarshalBinary(params)) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return @@ -356,7 +363,19 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { return } - + if vote.Empty() { + res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(gov.QueryProposalParams{params.ProposalID})) + if err != nil || len(res) == 0 { + w.WriteHeader(http.StatusNotFound) + err := errors.Errorf("proposalID [%d] does not exist", proposalID) + w.Write([]byte(err.Error())) + return + } + w.WriteHeader(http.StatusNotFound) + err = errors.Errorf("voter [%s] did not deposit on proposalID [%d]", bechVoterAddr, proposalID) + w.Write([]byte(err.Error())) + return + } w.Write(output) } } @@ -387,38 +406,19 @@ func queryVotesOnProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName) - if err != nil || len(res) == 0 { - err := errors.Errorf("proposalID [%d] does not exist", proposalID) - w.Write([]byte(err.Error())) - - return + params := gov.QueryVotesParams{ + ProposalID: proposalID, } - var proposal gov.Proposal - cdc.MustUnmarshalBinary(res, &proposal) - - if proposal.GetStatus() != gov.StatusVotingPeriod { - err := errors.Errorf("proposal is not in Voting Period", proposalID) - w.Write([]byte(err.Error())) - - return - } - - res2, err := cliCtx.QuerySubspace(gov.KeyVotesSubspace(proposalID), storeName) + res, err := cliCtx.QueryWithData("custom/gov/votes", cdc.MustMarshalBinary(params)) if err != nil { - err = errors.New("ProposalID doesn't exist") + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } var votes []gov.Vote - - for i := 0; i < len(res2); i++ { - var vote gov.Vote - cdc.MustUnmarshalBinary(res2[i].Value, &vote) - votes = append(votes, vote) - } + cdc.MustUnmarshalBinary(res, &votes) output, err := wire.MarshalJSONIndent(cdc, votes) if err != nil { @@ -439,11 +439,13 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { bechVoterAddr := r.URL.Query().Get(RestVoter) bechDepositerAddr := r.URL.Query().Get(RestDepositer) strProposalStatus := r.URL.Query().Get(RestProposalStatus) + strNumLatest := r.URL.Query().Get(RestNumLatest) var err error var voterAddr sdk.AccAddress var depositerAddr sdk.AccAddress var proposalStatus gov.ProposalStatus + var numLatest int64 if len(bechVoterAddr) != 0 { voterAddr, err = sdk.AccAddressFromBech32(bechVoterAddr) @@ -476,54 +478,35 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { return } } + if len(strNumLatest) != 0 { + numLatest, err = strconv.ParseInt(strNumLatest, 10, 64) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + err := errors.Errorf("'%s' is not a valid int64", strNumLatest) + w.Write([]byte(err.Error())) + return + } + } cliCtx := context.NewCLIContext().WithCodec(cdc) - res, err := cliCtx.QueryStore(gov.KeyNextProposalID, storeName) + params := gov.QueryProposalsParams{ + Depositer: depositerAddr, + Voter: voterAddr, + ProposalStatus: proposalStatus, + NumLatestProposals: numLatest, + } + + res, err := cliCtx.QueryWithData("custom/gov/proposals", cdc.MustMarshalBinary(params)) if err != nil { - err = errors.New("no proposals exist yet and proposalID has not been set") + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } - var maxProposalID int64 - cdc.MustUnmarshalBinary(res, &maxProposalID) - - matchingProposals := []gov.Proposal{} - - for proposalID := int64(0); proposalID < maxProposalID; proposalID++ { - if voterAddr != nil { - res, err = cliCtx.QueryStore(gov.KeyVote(proposalID, voterAddr), storeName) - if err != nil || len(res) == 0 { - continue - } - } - - if depositerAddr != nil { - res, err = cliCtx.QueryStore(gov.KeyDeposit(proposalID, depositerAddr), storeName) - if err != nil || len(res) == 0 { - continue - } - } - - res, err = cliCtx.QueryStore(gov.KeyProposal(proposalID), storeName) - if err != nil || len(res) == 0 { - continue - } - - var proposal gov.Proposal - cdc.MustUnmarshalBinary(res, &proposal) - - if len(strProposalStatus) != 0 { - if proposal.GetStatus() != proposalStatus { - continue - } - } - - matchingProposals = append(matchingProposals, proposal) - } - + var matchingProposals []gov.Proposal + cdc.MustUnmarshalBinary(res, &matchingProposals) output, err := wire.MarshalJSONIndent(cdc, matchingProposals) if err != nil { w.WriteHeader(http.StatusBadRequest) diff --git a/x/gov/depositsvotes.go b/x/gov/depositsvotes.go index 19ed97f69d..5a8b6bf249 100644 --- a/x/gov/depositsvotes.go +++ b/x/gov/depositsvotes.go @@ -15,6 +15,22 @@ type Vote struct { Option VoteOption `json:"option"` // option from OptionSet chosen by the voter } +// Returns whether 2 votes are equal +func (voteA Vote) Equals(voteB Vote) bool { + if voteA.Voter.Equals(voteB.Voter) && + voteA.ProposalID == voteB.ProposalID && + voteA.Option == voteB.Option { + return true + } + return false +} + +// Returns whether a vote is empty +func (voteA Vote) Empty() bool { + voteB := Vote{} + return voteA.Equals(voteB) +} + // Deposit type Deposit struct { Depositer sdk.AccAddress `json:"depositer"` // Address of the depositer @@ -22,6 +38,22 @@ type Deposit struct { Amount sdk.Coins `json:"amount"` // Deposit amount } +// Returns whether 2 deposits are equal +func (depositA Deposit) Equals(depositB Deposit) bool { + if depositA.Depositer.Equals(depositB.Depositer) && + depositA.ProposalID == depositB.ProposalID && + depositA.Amount.IsEqual(depositB.Amount) { + return true + } + return false +} + +// Returns whether a deposit is empty +func (depositA Deposit) Empty() bool { + depositB := Deposit{} + return depositA.Equals(depositB) +} + // Type that represents VoteOption as a byte type VoteOption byte diff --git a/x/gov/queryable.go b/x/gov/queryable.go index c0e66d0bf7..13bdfd08dc 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -55,7 +55,7 @@ type QueryDepositParams struct { func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryDepositParams - err2 := keeper.cdc.UnmarshalBinary(req.Data, params) + err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) if err2 != nil { return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") } From 0eed9d1b60375dae6ff0d0d7104a5cb727988cac Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Mon, 6 Aug 2018 05:09:16 -0400 Subject: [PATCH 28/36] PENDING --- PENDING.md | 1 + docs/sdk/modules.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/PENDING.md b/PENDING.md index c4b04238cd..53bdb91cac 100644 --- a/PENDING.md +++ b/PENDING.md @@ -33,6 +33,7 @@ FEATURES * Gaia * SDK + * [querier] added custom querier functionality, so ABCI query requests can be handled by keepers * Tendermint diff --git a/docs/sdk/modules.md b/docs/sdk/modules.md index b9249cf3e9..7df8fb2bb0 100644 --- a/docs/sdk/modules.md +++ b/docs/sdk/modules.md @@ -6,7 +6,7 @@ The Cosmos SDK has all the necessary pre-built modules to add functionality on top of a `BaseApp`, which is the template to build a blockchain dApp in Cosmos. In this context, a `module` is a fundamental unit in the Cosmos SDK. -Each module is an extension of the `BaseApp`'s functionalities that defines transactions, handles application state and manages the state transition logic. Each module also contains handlers for messages and transactions, as well as REST and CLI for secure user interactions. +Each module is an extension of the `BaseApp`'s functionalities that defines transactions, handles application state and manages the state transition logic. Each module also contains handlers for messages and transactions, queriers for handling query requests, as well as REST and CLI for secure user interactions. Some of the most important modules in the SDK are: From 0134c3b7f1d96278308e229ace4a956101a351b8 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Wed, 15 Aug 2018 17:59:15 -0700 Subject: [PATCH 29/36] Address Anton's comments --- baseapp/baseapp.go | 12 ++++++--- baseapp/setters.go | 2 +- x/gov/client/rest/rest.go | 57 ++++++++++++--------------------------- x/gov/client/rest/util.go | 14 ++++++++++ x/gov/depositsvotes.go | 14 ++-------- x/gov/keeper.go | 2 -- 6 files changed, 42 insertions(+), 59 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 6f143b1562..aadea1561a 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -46,7 +46,7 @@ type BaseApp struct { db dbm.DB // common DB backend cms sdk.CommitMultiStore // Main (uncached) state router Router // handle any kind of message - queryrouter QueryRouter // router for redirecting query calls + queryRouter QueryRouter // router for redirecting query calls codespacer *sdk.Codespacer // handle module codespacing txDecoder sdk.TxDecoder // unmarshal []byte into sdk.Tx @@ -90,7 +90,7 @@ func NewBaseApp(name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecod db: db, cms: store.NewCommitMultiStore(db), router: NewRouter(), - queryrouter: NewQueryRouter(), + queryRouter: NewQueryRouter(), codespacer: sdk.NewCodespacer(), txDecoder: txDecoder, } @@ -268,6 +268,7 @@ func (app *BaseApp) FilterPeerByPubKey(info string) abci.ResponseQuery { return abci.ResponseQuery{} } +// Splits a string path using the delimter '/'. i.e. "this/is/funny" becomes []string{"this", "is", "funny"} func splitPath(requestPath string) (path []string) { path = strings.Split(requestPath, "/") // first element is empty string @@ -367,9 +368,12 @@ func handleQueryP2P(app *BaseApp, path []string, req abci.RequestQuery) (res abc } func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { - // "/custom" prefix for keeper queries - querier := app.queryrouter.Route(path[1]) + // path[0] should be "custom" because "/custom" prefix is required for keeper queries. + // the queryRouter routes using path[1]. For example, in the path "custom/gov/proposal", queryRouter routes using "gov" + querier := app.queryRouter.Route(path[1]) ctx := app.checkState.ctx + // Passes the rest of the path as an argument to the querier. + // For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path resBytes, err := querier(ctx, path[2:], req) if err != nil { return abci.ResponseQuery{ diff --git a/baseapp/setters.go b/baseapp/setters.go index ef88b7fe56..4bcae0ed7c 100644 --- a/baseapp/setters.go +++ b/baseapp/setters.go @@ -78,7 +78,7 @@ func (app *BaseApp) QueryRouter() QueryRouter { if app.sealed { panic("QueryRouter() on sealed BaseApp") } - return app.queryrouter + return app.queryRouter } func (app *BaseApp) Seal() { app.sealed = true } func (app *BaseApp) IsSealed() bool { return app.sealed } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index cf5481da8c..7e5843b3de 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -3,7 +3,6 @@ package rest import ( "fmt" "net/http" - "strconv" "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" @@ -98,16 +97,13 @@ func depositHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFu return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - err := errors.Errorf("proposalID [%d] is not positive", proposalID) - w.Write([]byte(err.Error())) - + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } var req depositReq - err = buildReq(w, r, cdc, &req) + err := buildReq(w, r, cdc, &req) if err != nil { return } @@ -140,15 +136,13 @@ func voteHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - err := errors.Errorf("proposalID [%d] is not positive", proposalID) - w.Write([]byte(err.Error())) + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } var req voteReq - err = buildReq(w, r, cdc, &req) + err := buildReq(w, r, cdc, &req) if err != nil { return } @@ -181,11 +175,8 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - err := errors.Errorf("proposalID [%d] is not positive", proposalID) - w.Write([]byte(err.Error())) - + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } @@ -232,12 +223,8 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - err := errors.Errorf("proposalID [%d] is not positive", proposalID) - w.Write([]byte(err.Error())) - + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } @@ -313,12 +300,8 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - err := errors.Errorf("proposalID [%s] is not positive", proposalID) - w.Write([]byte(err.Error())) - + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } @@ -395,12 +378,8 @@ func queryVotesOnProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { return } - proposalID, err := strconv.ParseInt(strProposalID, 10, 64) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - err := errors.Errorf("proposalID [%s] is not positive", proposalID) - w.Write([]byte(err.Error())) - + proposalID, ok := parseInt64OrReturnBadRequest(strProposalID, w) + if !ok { return } @@ -442,6 +421,7 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { strNumLatest := r.URL.Query().Get(RestNumLatest) var err error + var ok bool var voterAddr sdk.AccAddress var depositerAddr sdk.AccAddress var proposalStatus gov.ProposalStatus @@ -479,11 +459,8 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { } } if len(strNumLatest) != 0 { - numLatest, err = strconv.ParseInt(strNumLatest, 10, 64) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - err := errors.Errorf("'%s' is not a valid int64", strNumLatest) - w.Write([]byte(err.Error())) + numLatest, ok = parseInt64OrReturnBadRequest(strNumLatest, w) + if !ok { return } } diff --git a/x/gov/client/rest/util.go b/x/gov/client/rest/util.go index 58d96b591f..de94a8324c 100644 --- a/x/gov/client/rest/util.go +++ b/x/gov/client/rest/util.go @@ -1,8 +1,10 @@ package rest import ( + "fmt" "io/ioutil" "net/http" + "strconv" "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" @@ -100,3 +102,15 @@ func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq base w.Write(output) } + +func parseInt64OrReturnBadRequest(s string, w http.ResponseWriter) (n int64, ok bool) { + var err error + n, err = strconv.ParseInt(s, 10, 64) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + err := fmt.Errorf("'%s' is not a valid int64", s) + w.Write([]byte(err.Error())) + return 0, false + } + return n, true + } \ No newline at end of file diff --git a/x/gov/depositsvotes.go b/x/gov/depositsvotes.go index 5a8b6bf249..b5c65931fc 100644 --- a/x/gov/depositsvotes.go +++ b/x/gov/depositsvotes.go @@ -17,12 +17,7 @@ type Vote struct { // Returns whether 2 votes are equal func (voteA Vote) Equals(voteB Vote) bool { - if voteA.Voter.Equals(voteB.Voter) && - voteA.ProposalID == voteB.ProposalID && - voteA.Option == voteB.Option { - return true - } - return false + return voteA.Voter.Equals(voteB.Voter) && voteA.ProposalID == voteB.ProposalID && voteA.Option == voteB.Option } // Returns whether a vote is empty @@ -40,12 +35,7 @@ type Deposit struct { // Returns whether 2 deposits are equal func (depositA Deposit) Equals(depositB Deposit) bool { - if depositA.Depositer.Equals(depositB.Depositer) && - depositA.ProposalID == depositB.ProposalID && - depositA.Amount.IsEqual(depositB.Amount) { - return true - } - return false + return depositA.Depositer.Equals(depositB.Depositer) && depositA.ProposalID == depositB.ProposalID && depositA.Amount.IsEqual(depositB.Amount) } // Returns whether a deposit is empty diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 45af4b471d..19d842f1f3 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -198,8 +198,6 @@ func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, e return -1, ErrInvalidGenesis(keeper.codespace, "InitialProposalID never set") } keeper.cdc.MustUnmarshalBinary(bz, &proposalID) - bz = keeper.cdc.MustMarshalBinary(proposalID + 1) - store.Set(KeyNextProposalID, bz) return proposalID, nil } From 97f7b88a9fd9f4cd72e2d2c7db5593cdf0c5fde8 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Thu, 16 Aug 2018 18:35:17 -0700 Subject: [PATCH 30/36] addressed Jae's comments --- baseapp/baseapp.go | 4 ++++ baseapp/queryrouter.go | 30 ++++++++++-------------------- types/account.go | 16 ++++++++++++++-- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index aadea1561a..828414f1e2 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -370,6 +370,9 @@ func handleQueryP2P(app *BaseApp, path []string, req abci.RequestQuery) (res abc func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { // path[0] should be "custom" because "/custom" prefix is required for keeper queries. // the queryRouter routes using path[1]. For example, in the path "custom/gov/proposal", queryRouter routes using "gov" + if path[1] == "" { + sdk.ErrUnknownRequest("No route for custom query specified").QueryResult() + } querier := app.queryRouter.Route(path[1]) ctx := app.checkState.ctx // Passes the rest of the path as an argument to the querier. @@ -378,6 +381,7 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res if err != nil { return abci.ResponseQuery{ Code: uint32(err.ABCICode()), + Log: err.ABCILog(), } } return abci.ResponseQuery{ diff --git a/baseapp/queryrouter.go b/baseapp/queryrouter.go index ade071f1f7..23cfad0721 100644 --- a/baseapp/queryrouter.go +++ b/baseapp/queryrouter.go @@ -10,14 +10,8 @@ type QueryRouter interface { Route(path string) (h sdk.Querier) } -// map a transaction type to a handler and an initgenesis function -type queryroute struct { - r string - h sdk.Querier -} - type queryrouter struct { - routes []queryroute + routes map[string]sdk.Querier } // nolint @@ -25,27 +19,23 @@ type queryrouter struct { // TODO either make Function unexported or make return type (router) Exported func NewQueryRouter() *queryrouter { return &queryrouter{ - routes: make([]queryroute, 0), + routes: map[string]sdk.Querier{}, } } -// AddRoute - TODO add description -func (rtr *queryrouter) AddRoute(r string, h sdk.Querier) QueryRouter { +// AddRoute - Adds an sdk.Querier to the route provided. Panics on duplicate +func (rtr *queryrouter) AddRoute(r string, q sdk.Querier) QueryRouter { if !isAlphaNumeric(r) { panic("route expressions can only contain alphanumeric characters") } - rtr.routes = append(rtr.routes, queryroute{r, h}) - + if rtr.routes[r] != nil { + panic("route has already been initialized") + } + rtr.routes[r] = q return rtr } -// Route - TODO add description -// TODO handle expressive matches. +// Returns the sdk.Querier for a certain route path func (rtr *queryrouter) Route(path string) (h sdk.Querier) { - for _, route := range rtr.routes { - if route.r == path { - return route.h - } - } - return nil + return rtr.routes[path] } diff --git a/types/account.go b/types/account.go index 258a095e8b..9f19c26287 100644 --- a/types/account.go +++ b/types/account.go @@ -111,13 +111,19 @@ func (bz AccAddress) Format(s fmt.State, verb rune) { // Returns boolean for whether two AccAddresses are Equal func (bz AccAddress) Equals(bz2 AccAddress) bool { + if bz.Empty() && bz2.Empty() { + return true + } return (bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0) } // Returns boolean for whether an AccAddress is empty func (bz AccAddress) Empty() bool { + if bz == nil { + return true + } bz2 := AccAddress{} - return bz.Equals(bz2) + return (bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0) } //__________________________________________________________ @@ -206,13 +212,19 @@ func (bz ValAddress) Format(s fmt.State, verb rune) { // Returns boolean for whether two ValAddresses are Equal func (bz ValAddress) Equals(bz2 ValAddress) bool { + if bz.Empty() && bz2.Empty() { + return true + } return (bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0) } // Returns boolean for whether an AccAddress is empty func (bz ValAddress) Empty() bool { + if bz == nil { + return true + } bz2 := ValAddress{} - return bz.Equals(bz2) + return (bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0) } // Bech32ifyAccPub takes AccountPubKey and returns the bech32 encoded string From 44bf69e5647890f264b49c1620635f831bd32bb3 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Fri, 17 Aug 2018 00:16:17 -0700 Subject: [PATCH 31/36] run queries against cachewrapped commit state, not checkstate --- baseapp/baseapp.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 828414f1e2..365d7b31f2 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -374,7 +374,11 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res sdk.ErrUnknownRequest("No route for custom query specified").QueryResult() } querier := app.queryRouter.Route(path[1]) - ctx := app.checkState.ctx + if querier == nil { + sdk.ErrUnknownRequest(fmt.Sprintf("no custom querier found for route %s", path[1])).QueryResult() + } + + ctx := sdk.NewContext(app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger) // Passes the rest of the path as an argument to the querier. // For example, in the path "custom/gov/proposal/test", the gov querier gets []string{"proposal", "test"} as the path resBytes, err := querier(ctx, path[2:], req) From 50dd53fec3bbb7694b7addb2511f948cee90c411 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Fri, 17 Aug 2018 00:40:30 -0700 Subject: [PATCH 32/36] added tally query route to gov --- x/gov/queryable.go | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/x/gov/queryable.go b/x/gov/queryable.go index 13bdfd08dc..28e32eb675 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -156,16 +156,25 @@ type QueryTallyParams struct { func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { // TODO: Dependant on #1914 - // var proposalID int64 - // err2 := keeper.cdc.UnmarshalBinary(req.Data, proposalID) - // if err2 != nil { - // return []byte{}, sdk.ErrUnknownRequest() - // } + var proposalID int64 + err2 := keeper.cdc.UnmarshalBinary(req.Data, proposalID) + if err2 != nil { + return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + } - // proposal := keeper.GetProposal(ctx, proposalID) - // if proposal == nil { - // return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) - // } - // _, tallyResult, _ := tally(ctx, keeper, proposal) - return nil, nil + proposal := keeper.GetProposal(ctx, proposalID) + if proposal == nil { + return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) + } + + if proposal.GetStatus() == StatusDepositPeriod { + return keeper.cdc.MustMarshalBinaryBare(EmptyTallyResult()), nil + } + + if proposal.GetStatus() == StatusPassed || proposal.GetStatus() == StatusRejected { + return keeper.cdc.MustMarshalBinaryBare(proposal.GetTallyResult()), nil + } + + _, tallyResult, _ := tally(ctx, keeper, proposal) + return keeper.cdc.MustMarshalBinaryBare(tallyResult), nil } From 5ae20d2d511aafd89008ea43433c5366d5f12e78 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Wed, 22 Aug 2018 00:10:11 -0700 Subject: [PATCH 33/36] address Chris's comments --- baseapp/setters.go | 3 - types/account.go | 8 +-- x/gov/client/rest/rest.go | 140 +++++++++++++++++--------------------- x/gov/proposals.go | 4 ++ x/gov/queryable.go | 85 ++++++++++++++++------- 5 files changed, 129 insertions(+), 111 deletions(-) diff --git a/baseapp/setters.go b/baseapp/setters.go index 4bcae0ed7c..a8b1591a73 100644 --- a/baseapp/setters.go +++ b/baseapp/setters.go @@ -75,9 +75,6 @@ func (app *BaseApp) Router() Router { return app.router } func (app *BaseApp) QueryRouter() QueryRouter { - if app.sealed { - panic("QueryRouter() on sealed BaseApp") - } return app.queryRouter } func (app *BaseApp) Seal() { app.sealed = true } diff --git a/types/account.go b/types/account.go index 9f19c26287..00076b5299 100644 --- a/types/account.go +++ b/types/account.go @@ -114,7 +114,7 @@ func (bz AccAddress) Equals(bz2 AccAddress) bool { if bz.Empty() && bz2.Empty() { return true } - return (bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0) + return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0 } // Returns boolean for whether an AccAddress is empty @@ -123,7 +123,7 @@ func (bz AccAddress) Empty() bool { return true } bz2 := AccAddress{} - return (bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0) + return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0 } //__________________________________________________________ @@ -215,7 +215,7 @@ func (bz ValAddress) Equals(bz2 ValAddress) bool { if bz.Empty() && bz2.Empty() { return true } - return (bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0) + return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0 } // Returns boolean for whether an AccAddress is empty @@ -224,7 +224,7 @@ func (bz ValAddress) Empty() bool { return true } bz2 := ValAddress{} - return (bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0) + return bytes.Compare(bz.Bytes(), bz2.Bytes()) == 0 } // Bech32ifyAccPub takes AccountPubKey and returns the bech32 encoded string diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 7e5843b3de..5cdc7bda2b 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -186,7 +186,14 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { ProposalID: proposalID, } - res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(params)) + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) @@ -194,18 +201,7 @@ func queryProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { return } - var proposal gov.Proposal - cdc.MustUnmarshalBinary(res, &proposal) - - output, err := wire.MarshalJSONIndent(cdc, proposal) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - - return - } - - w.Write(output) + w.Write(res) } } @@ -252,7 +248,14 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { Depositer: depositerAddr, } - res, err := cliCtx.QueryWithData("custom/gov/deposit", cdc.MustMarshalBinary(params)) + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + + res, err := cliCtx.QueryWithData("custom/gov/deposit", bz) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) @@ -261,15 +264,7 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { } var deposit gov.Deposit - cdc.MustUnmarshalBinary(res, &deposit) - - output, err := wire.MarshalJSONIndent(cdc, deposit) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - - return - } + cdc.UnmarshalJSON(res, &deposit) if deposit.Empty() { res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(gov.QueryProposalParams{params.ProposalID})) if err != nil || len(res) == 0 { @@ -283,7 +278,8 @@ func queryDepositHandlerFn(cdc *wire.Codec) http.HandlerFunc { w.Write([]byte(err.Error())) return } - w.Write(output) + + w.Write(res) } } @@ -327,8 +323,14 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { Voter: voterAddr, ProposalID: proposalID, } + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } - res, err := cliCtx.QueryWithData("custom/gov/vote", cdc.MustMarshalBinary(params)) + res, err := cliCtx.QueryWithData("custom/gov/vote", bz) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) @@ -337,17 +339,15 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { } var vote gov.Vote - cdc.MustUnmarshalBinary(res, &vote) - - output, err := wire.MarshalJSONIndent(cdc, vote) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - - return - } + cdc.UnmarshalJSON(res, &vote) if vote.Empty() { - res, err := cliCtx.QueryWithData("custom/gov/proposal", cdc.MustMarshalBinary(gov.QueryProposalParams{params.ProposalID})) + bz, err := cdc.MarshalJSON(gov.QueryProposalParams{params.ProposalID}) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } + res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) if err != nil || len(res) == 0 { w.WriteHeader(http.StatusNotFound) err := errors.Errorf("proposalID [%d] does not exist", proposalID) @@ -359,7 +359,7 @@ func queryVoteHandlerFn(cdc *wire.Codec) http.HandlerFunc { w.Write([]byte(err.Error())) return } - w.Write(output) + w.Write(res) } } @@ -388,26 +388,21 @@ func queryVotesOnProposalHandlerFn(cdc *wire.Codec) http.HandlerFunc { params := gov.QueryVotesParams{ ProposalID: proposalID, } + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return + } - res, err := cliCtx.QueryWithData("custom/gov/votes", cdc.MustMarshalBinary(params)) + res, err := cliCtx.QueryWithData("custom/gov/votes", bz) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } - var votes []gov.Vote - cdc.MustUnmarshalBinary(res, &votes) - - output, err := wire.MarshalJSONIndent(cdc, votes) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - - return - } - - w.Write(output) + w.Write(res) } } @@ -420,25 +415,21 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { strProposalStatus := r.URL.Query().Get(RestProposalStatus) strNumLatest := r.URL.Query().Get(RestNumLatest) - var err error - var ok bool - var voterAddr sdk.AccAddress - var depositerAddr sdk.AccAddress - var proposalStatus gov.ProposalStatus - var numLatest int64 + params := gov.QueryProposalsParams{} if len(bechVoterAddr) != 0 { - voterAddr, err = sdk.AccAddressFromBech32(bechVoterAddr) + voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr) if err != nil { w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("'%s' needs to be bech32 encoded", RestVoter) w.Write([]byte(err.Error())) return } + params.Voter = voterAddr } if len(bechDepositerAddr) != 0 { - depositerAddr, err = sdk.AccAddressFromBech32(bechDepositerAddr) + depositerAddr, err := sdk.AccAddressFromBech32(bechDepositerAddr) if err != nil { w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("'%s' needs to be bech32 encoded", RestDepositer) @@ -446,10 +437,11 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { return } + params.Depositer = depositerAddr } if len(strProposalStatus) != 0 { - proposalStatus, err = gov.ProposalStatusFromString(strProposalStatus) + proposalStatus, err := gov.ProposalStatusFromString(strProposalStatus) if err != nil { w.WriteHeader(http.StatusBadRequest) err := errors.Errorf("'%s' is not a valid Proposal Status", strProposalStatus) @@ -457,24 +449,26 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { return } + params.ProposalStatus = proposalStatus } if len(strNumLatest) != 0 { - numLatest, ok = parseInt64OrReturnBadRequest(strNumLatest, w) + numLatest, ok := parseInt64OrReturnBadRequest(strNumLatest, w) if !ok { return } + params.NumLatestProposals = numLatest + } + + bz, err := cdc.MarshalJSON(params) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(err.Error())) + return } cliCtx := context.NewCLIContext().WithCodec(cdc) - params := gov.QueryProposalsParams{ - Depositer: depositerAddr, - Voter: voterAddr, - ProposalStatus: proposalStatus, - NumLatestProposals: numLatest, - } - - res, err := cliCtx.QueryWithData("custom/gov/proposals", cdc.MustMarshalBinary(params)) + res, err := cliCtx.QueryWithData("custom/gov/proposals", bz) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) @@ -482,16 +476,6 @@ func queryProposalsWithParameterFn(cdc *wire.Codec) http.HandlerFunc { return } - var matchingProposals []gov.Proposal - cdc.MustUnmarshalBinary(res, &matchingProposals) - output, err := wire.MarshalJSONIndent(cdc, matchingProposals) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - - return - } - - w.Write(output) + w.Write(res) } } diff --git a/x/gov/proposals.go b/x/gov/proposals.go index f05dabd08d..c52ab1e875 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -110,6 +110,7 @@ type ProposalKind byte //nolint const ( + ProposalTypeNil ProposalKind = 0x00 ProposalTypeText ProposalKind = 0x01 ProposalTypeParameterChange ProposalKind = 0x02 ProposalTypeSoftwareUpgrade ProposalKind = 0x03 @@ -203,6 +204,7 @@ type ProposalStatus byte //nolint const ( + StatusNil ProposalStatus = 0x00 StatusDepositPeriod ProposalStatus = 0x01 StatusVotingPeriod ProposalStatus = 0x02 StatusPassed ProposalStatus = 0x03 @@ -220,6 +222,8 @@ func ProposalStatusFromString(str string) (ProposalStatus, error) { return StatusPassed, nil case "Rejected": return StatusRejected, nil + case "": + return StatusNil, nil default: return ProposalStatus(0xff), errors.Errorf("'%s' is not a valid proposal status", str) } diff --git a/x/gov/queryable.go b/x/gov/queryable.go index 28e32eb675..e64d506d1e 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -1,7 +1,10 @@ package gov import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/wire" abci "github.com/tendermint/tendermint/abci/types" ) @@ -35,16 +38,21 @@ type QueryProposalParams struct { func queryProposal(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryProposalParams - err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) } proposal := keeper.GetProposal(ctx, params.ProposalID) if proposal == nil { return []byte{}, ErrUnknownProposal(DefaultCodespace, params.ProposalID) } - return keeper.cdc.MustMarshalBinary(proposal), nil + + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, proposal) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil } // Params for query 'custom/gov/deposit' @@ -55,13 +63,17 @@ type QueryDepositParams struct { func queryDeposit(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryDepositParams - err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) } deposit, _ := keeper.GetDeposit(ctx, params.ProposalID, params.Depositer) - return keeper.cdc.MustMarshalBinary(deposit), nil + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, deposit) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil } // Params for query 'custom/gov/vote' @@ -72,13 +84,17 @@ type QueryVoteParams struct { func queryVote(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryVoteParams - err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) } vote, _ := keeper.GetVote(ctx, params.ProposalID, params.Voter) - return keeper.cdc.MustMarshalBinary(vote), nil + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, vote) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil } // Params for query 'custom/gov/deposits' @@ -88,9 +104,9 @@ type QueryDepositsParams struct { func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryDepositParams - err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) } var deposits []Deposit @@ -101,7 +117,11 @@ func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper deposits = append(deposits, deposit) } - return keeper.cdc.MustMarshalBinary(deposits), nil + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, deposits) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil } // Params for query 'custom/gov/votes' @@ -111,9 +131,10 @@ type QueryVotesParams struct { func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryVotesParams - err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) + if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) } var votes []Vote @@ -124,7 +145,11 @@ func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke votes = append(votes, vote) } - return keeper.cdc.MustMarshalBinary(votes), nil + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, votes) + if err2 != nil { + panic("could not marshal result to JSON") + } + return bz, nil } // Params for query 'custom/gov/proposals' @@ -137,14 +162,17 @@ type QueryProposalsParams struct { func queryProposals(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Keeper) (res []byte, err sdk.Error) { var params QueryProposalsParams - err2 := keeper.cdc.UnmarshalBinary(req.Data, ¶ms) + err2 := keeper.cdc.UnmarshalJSON(req.Data, ¶ms) if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) } proposals := keeper.GetProposalsFiltered(ctx, params.Voter, params.Depositer, params.ProposalStatus, params.NumLatestProposals) - bz := keeper.cdc.MustMarshalBinary(proposals) + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, proposals) + if err2 != nil { + panic("could not marshal result to JSON") + } return bz, nil } @@ -157,9 +185,9 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke // TODO: Dependant on #1914 var proposalID int64 - err2 := keeper.cdc.UnmarshalBinary(req.Data, proposalID) + err2 := keeper.cdc.UnmarshalJSON(req.Data, proposalID) if err2 != nil { - return []byte{}, sdk.ErrUnknownRequest("incorrectly formatted request data") + return []byte{}, sdk.ErrUnknownRequest(fmt.Sprintf("incorrectly formatted request data - %s", err2.Error())) } proposal := keeper.GetProposal(ctx, proposalID) @@ -167,14 +195,19 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke return []byte{}, ErrUnknownProposal(DefaultCodespace, proposalID) } + var tallyResult TallyResult + if proposal.GetStatus() == StatusDepositPeriod { - return keeper.cdc.MustMarshalBinaryBare(EmptyTallyResult()), nil + tallyResult = EmptyTallyResult() + } else if proposal.GetStatus() == StatusPassed || proposal.GetStatus() == StatusRejected { + tallyResult = proposal.GetTallyResult() + } else { + _, tallyResult, _ = tally(ctx, keeper, proposal) } - if proposal.GetStatus() == StatusPassed || proposal.GetStatus() == StatusRejected { - return keeper.cdc.MustMarshalBinaryBare(proposal.GetTallyResult()), nil + bz, err2 := wire.MarshalJSONIndent(keeper.cdc, tallyResult) + if err2 != nil { + panic("could not marshal result to JSON") } - - _, tallyResult, _ := tally(ctx, keeper, proposal) - return keeper.cdc.MustMarshalBinaryBare(tallyResult), nil + return bz, nil } From 7f43d3b2900652a62f365bfad5dafb4b1bdd1e8f Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Wed, 22 Aug 2018 00:17:37 -0700 Subject: [PATCH 34/36] fixed GetLastProposalID function --- x/gov/keeper.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 19d842f1f3..e8dbbc4a7f 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -167,12 +167,10 @@ func (keeper Keeper) setInitialProposalID(ctx sdk.Context, proposalID int64) sdk // Get the last used proposal ID func (keeper Keeper) GetLastProposalID(ctx sdk.Context) (proposalID int64) { - store := ctx.KVStore(keeper.storeKey) - bz := store.Get(KeyNextProposalID) - if bz == nil { + proposalID, err := keeper.peekCurrentProposalID(ctx) + if err != nil { return 0 } - keeper.cdc.MustUnmarshalBinary(bz, &proposalID) proposalID-- return } From ba56c570c07ebf7c9c874211eaff0a6696154001 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 22 Aug 2018 12:40:15 +0200 Subject: [PATCH 35/36] Update Gopkg.lock --- Gopkg.lock | 198 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 178 insertions(+), 20 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index e94ad3ec0d..73a2ff60fd 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,57 +2,76 @@ [[projects]] + digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0" name = "github.com/bartekn/go-bip39" packages = ["."] + pruneopts = "UT" revision = "a05967ea095d81c8fe4833776774cfaff8e5036c" [[projects]] branch = "master" + digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" name = "github.com/beorn7/perks" packages = ["quantile"] + pruneopts = "UT" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] + digest = "1:1343a2963481a305ca4d051e84bc2abd16b601ee22ed324f8d605de1adb291b0" name = "github.com/bgentry/speakeasy" packages = ["."] + pruneopts = "UT" revision = "4aabc24848ce5fd31929f7d1e4ea74d3709c14cd" version = "v0.1.0" [[projects]] branch = "master" + digest = "1:70f6b224a59b2fa453debffa85c77f71063d8754b90c8c4fbad5794e2c382b0f" name = "github.com/brejski/hid" packages = ["."] + pruneopts = "UT" revision = "06112dcfcc50a7e0e4fd06e17f9791e788fdaafc" [[projects]] branch = "master" + digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8" name = "github.com/btcsuite/btcd" packages = ["btcec"] + pruneopts = "UT" revision = "f899737d7f2764dc13e4d01ff00108ec58f766a9" [[projects]] + digest = "1:386de157f7d19259a7f9c81f26ce011223ce0f090353c1152ffdf730d7d10ac2" name = "github.com/btcsuite/btcutil" packages = ["bech32"] + pruneopts = "UT" revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" [[projects]] + digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "UT" revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" version = "v1.1.1" [[projects]] + digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b" name = "github.com/ebuchman/fail-test" packages = ["."] + pruneopts = "UT" revision = "95f809107225be108efcf10a3509e4ea6ceef3c4" [[projects]] + digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" name = "github.com/fsnotify/fsnotify" packages = ["."] + pruneopts = "UT" revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" version = "v1.4.7" [[projects]] + digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11" name = "github.com/go-kit/kit" packages = [ "log", @@ -61,24 +80,30 @@ "metrics", "metrics/discard", "metrics/internal/lv", - "metrics/prometheus" + "metrics/prometheus", ] + pruneopts = "UT" revision = "4dc7be5d2d12881735283bcab7352178e190fc71" version = "v0.6.0" [[projects]] + digest = "1:31a18dae27a29aa074515e43a443abfd2ba6deb6d69309d8d7ce789c45f34659" name = "github.com/go-logfmt/logfmt" packages = ["."] + pruneopts = "UT" revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" version = "v0.3.0" [[projects]] + digest = "1:c4a2528ccbcabf90f9f3c464a5fc9e302d592861bbfd0b7135a7de8a943d0406" name = "github.com/go-stack/stack" packages = ["."] + pruneopts = "UT" revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc" version = "v1.7.0" [[projects]] + digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e" name = "github.com/gogo/protobuf" packages = [ "gogoproto", @@ -86,213 +111,272 @@ "proto", "protoc-gen-gogo/descriptor", "sortkeys", - "types" + "types", ] + pruneopts = "UT" revision = "636bf0302bc95575d69441b25a2603156ffdddf1" version = "v1.1.1" [[projects]] + digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260" name = "github.com/golang/protobuf" packages = [ "proto", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/timestamp" + "ptypes/timestamp", ] + pruneopts = "UT" revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009" name = "github.com/golang/snappy" packages = ["."] + pruneopts = "UT" revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" [[projects]] + digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1" name = "github.com/gorilla/context" packages = ["."] + pruneopts = "UT" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] + digest = "1:e73f5b0152105f18bc131fba127d9949305c8693f8a762588a82a48f61756f5f" name = "github.com/gorilla/mux" packages = ["."] + pruneopts = "UT" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" [[projects]] + digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e" name = "github.com/gorilla/websocket" packages = ["."] + pruneopts = "UT" revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" version = "v1.2.0" [[projects]] branch = "master" + digest = "1:a361611b8c8c75a1091f00027767f7779b29cb37c456a71b8f2604c88057ab40" name = "github.com/hashicorp/hcl" packages = [ ".", "hcl/ast", "hcl/parser", + "hcl/printer", "hcl/scanner", "hcl/strconv", "hcl/token", "json/parser", "json/scanner", - "json/token" + "json/token", ] + pruneopts = "UT" revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168" [[projects]] + digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" name = "github.com/inconshreveable/mousetrap" packages = ["."] + pruneopts = "UT" revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" version = "v1.0" [[projects]] branch = "master" + digest = "1:39b27d1381a30421f9813967a5866fba35dc1d4df43a6eefe3b7a5444cb07214" name = "github.com/jmhodges/levigo" packages = ["."] + pruneopts = "UT" revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9" [[projects]] branch = "master" + digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72" name = "github.com/kr/logfmt" packages = ["."] + pruneopts = "UT" revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" [[projects]] + digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7" name = "github.com/magiconair/properties" packages = ["."] + pruneopts = "UT" revision = "c2353362d570a7bfa228149c62842019201cfb71" version = "v1.8.0" [[projects]] + digest = "1:d4d17353dbd05cb52a2a52b7fe1771883b682806f68db442b436294926bbfafb" name = "github.com/mattn/go-isatty" packages = ["."] + pruneopts = "UT" revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" version = "v0.0.3" [[projects]] + digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" name = "github.com/matttproud/golang_protobuf_extensions" packages = ["pbutil"] + pruneopts = "UT" revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" version = "v1.0.1" [[projects]] branch = "master" + digest = "1:5ab79470a1d0fb19b041a624415612f8236b3c06070161a910562f2b2d064355" name = "github.com/mitchellh/mapstructure" packages = ["."] + pruneopts = "UT" revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac" [[projects]] + digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" name = "github.com/pelletier/go-toml" packages = ["."] + pruneopts = "UT" revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" version = "v1.2.0" [[projects]] + digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "UT" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "UT" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] + digest = "1:c1a04665f9613e082e1209cf288bf64f4068dcd6c87a64bf1c4ff006ad422ba0" name = "github.com/prometheus/client_golang" packages = [ "prometheus", - "prometheus/promhttp" + "prometheus/promhttp", ] + pruneopts = "UT" revision = "ae27198cdd90bf12cd134ad79d1366a6cf49f632" [[projects]] branch = "master" + digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" name = "github.com/prometheus/client_model" packages = ["go"] + pruneopts = "UT" revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f" [[projects]] branch = "master" + digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5" name = "github.com/prometheus/common" packages = [ "expfmt", "internal/bitbucket.org/ww/goautoneg", - "model" + "model", ] + pruneopts = "UT" revision = "c7de2306084e37d54b8be01f3541a8464345e9a5" [[projects]] branch = "master" + digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290" name = "github.com/prometheus/procfs" packages = [ ".", "internal/util", "nfs", - "xfs" + "xfs", ] + pruneopts = "UT" revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92" [[projects]] + digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c" name = "github.com/rcrowley/go-metrics" packages = ["."] + pruneopts = "UT" revision = "e2704e165165ec55d062f5919b4b29494e9fa790" [[projects]] + digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84" name = "github.com/spf13/afero" packages = [ ".", - "mem" + "mem", ] + pruneopts = "UT" revision = "787d034dfe70e44075ccc060d346146ef53270ad" version = "v1.1.1" [[projects]] + digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f" name = "github.com/spf13/cast" packages = ["."] + pruneopts = "UT" revision = "8965335b8c7107321228e3e3702cab9832751bac" version = "v1.2.0" [[projects]] + digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e" name = "github.com/spf13/cobra" packages = ["."] + pruneopts = "UT" revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b" version = "v0.0.1" [[projects]] branch = "master" + digest = "1:8a020f916b23ff574845789daee6818daf8d25a4852419aae3f0b12378ba432a" name = "github.com/spf13/jwalterweatherman" packages = ["."] + pruneopts = "UT" revision = "14d3d4c518341bea657dd8a226f5121c0ff8c9f2" [[projects]] + digest = "1:dab83a1bbc7ad3d7a6ba1a1cc1760f25ac38cdf7d96a5cdd55cd915a4f5ceaf9" name = "github.com/spf13/pflag" packages = ["."] + pruneopts = "UT" revision = "9a97c102cda95a86cec2345a6f09f55a939babf5" version = "v1.0.2" [[projects]] + digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96" name = "github.com/spf13/viper" packages = ["."] + pruneopts = "UT" revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7" version = "v1.0.0" [[projects]] + digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6" name = "github.com/stretchr/testify" packages = [ "assert", - "require" + "require", ] + pruneopts = "UT" revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" version = "v1.2.1" [[projects]] branch = "master" + digest = "1:f2ffd421680b0a3f7887501b3c6974bcf19217ecd301d0e2c9b681940ec363d5" name = "github.com/syndtr/goleveldb" packages = [ "leveldb", @@ -306,33 +390,41 @@ "leveldb/opt", "leveldb/storage", "leveldb/table", - "leveldb/util" + "leveldb/util", ] + pruneopts = "UT" revision = "ae2bd5eed72d46b28834ec3f60db3a3ebedd8dbd" [[projects]] branch = "master" + digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722" name = "github.com/tendermint/ed25519" packages = [ ".", "edwards25519", - "extra25519" + "extra25519", ] + pruneopts = "UT" revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057" [[projects]] + digest = "1:e0a2a4be1e20c305badc2b0a7a9ab7fef6da500763bec23ab81df3b5f9eec9ee" name = "github.com/tendermint/go-amino" packages = ["."] + pruneopts = "UT" revision = "a8328986c1608950fa5d3d1c0472cccc4f8fc02c" version = "v0.12.0-rc0" [[projects]] + digest = "1:d4a15d404afbf591e8be16fcda7f5ac87948d5c7531f9d909fd84cc730ab16e2" name = "github.com/tendermint/iavl" packages = ["."] + pruneopts = "UT" revision = "35f66e53d9b01e83b30de68b931f54b2477a94c9" version = "v0.9.2" [[projects]] + digest = "1:26146cdb2811ce481e72138439b9b1aa17a64d54364f96bb92f97a9ef8ba4f01" name = "github.com/tendermint/tendermint" packages = [ "abci/client", @@ -392,24 +484,30 @@ "state/txindex/kv", "state/txindex/null", "types", - "version" + "version", ] + pruneopts = "UT" revision = "013b9cef642f875634c614019ab13b17570778ad" version = "v0.23.0" [[projects]] + digest = "1:bf6d9a827ea3cad964c2f863302e4f6823170d0b5ed16f72cf1184a7c615067e" name = "github.com/tendermint/tmlibs" packages = ["cli"] + pruneopts = "UT" revision = "49596e0a1f48866603813df843c9409fc19805c6" version = "v0.9.0" [[projects]] + digest = "1:4dcb0dd65feecb068ce23a234d1a07c7868a1e39f52a6defcae0bb371d03abf6" name = "github.com/zondax/ledger-goclient" packages = ["."] + pruneopts = "UT" revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259" [[projects]] branch = "master" + digest = "1:27507554c6d4f060d8d700c31c624a43d3a92baa634e178ddc044bdf7d13b44a" name = "golang.org/x/crypto" packages = [ "blowfish", @@ -425,11 +523,13 @@ "pbkdf2", "poly1305", "ripemd160", - "salsa20/salsa" + "salsa20/salsa", ] - revision = "aabede6cba87e37f413b3e60ebfc214f8eeca1b0" + pruneopts = "UT" + revision = "614d502a4dac94afa3a6ce146bd1736da82514c6" [[projects]] + digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" name = "golang.org/x/net" packages = [ "context", @@ -439,20 +539,24 @@ "idna", "internal/timeseries", "netutil", - "trace" + "trace", ] + pruneopts = "UT" revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" [[projects]] branch = "master" + digest = "1:a0e12bc26f317c0e2d497baf767285e1790e526e8dd46553c5a67fcbc8692157" name = "golang.org/x/sys" packages = [ "cpu", - "unix" + "unix", ] - revision = "1a700e749ce29638d0bbcb531cce1094ea096bd3" + pruneopts = "UT" + revision = "3b58ed4ad3395d483fc92d5d14123ce2c3581fec" [[projects]] + digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" name = "golang.org/x/text" packages = [ "collate", @@ -468,18 +572,22 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable" + "unicode/rangetable", ] + pruneopts = "UT" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c" name = "google.golang.org/genproto" packages = ["googleapis/rpc/status"] + pruneopts = "UT" revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4" [[projects]] + digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74" name = "google.golang.org/grpc" packages = [ ".", @@ -506,20 +614,70 @@ "stats", "status", "tap", - "transport" + "transport", ] + pruneopts = "UT" revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8" version = "v1.13.0" [[projects]] + digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "UT" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "46c912cbb9fa9b2ab3529af18718edada64e59822c8a2e432aec9affa3e24f45" + input-imports = [ + "github.com/bartekn/go-bip39", + "github.com/bgentry/speakeasy", + "github.com/btcsuite/btcd/btcec", + "github.com/golang/protobuf/proto", + "github.com/gorilla/mux", + "github.com/mattn/go-isatty", + "github.com/pkg/errors", + "github.com/spf13/cobra", + "github.com/spf13/pflag", + "github.com/spf13/viper", + "github.com/stretchr/testify/assert", + "github.com/stretchr/testify/require", + "github.com/tendermint/go-amino", + "github.com/tendermint/iavl", + "github.com/tendermint/tendermint/abci/server", + "github.com/tendermint/tendermint/abci/types", + "github.com/tendermint/tendermint/cmd/tendermint/commands", + "github.com/tendermint/tendermint/config", + "github.com/tendermint/tendermint/crypto", + "github.com/tendermint/tendermint/crypto/armor", + "github.com/tendermint/tendermint/crypto/ed25519", + "github.com/tendermint/tendermint/crypto/encoding/amino", + "github.com/tendermint/tendermint/crypto/merkle", + "github.com/tendermint/tendermint/crypto/secp256k1", + "github.com/tendermint/tendermint/crypto/tmhash", + "github.com/tendermint/tendermint/crypto/xsalsa20symmetric", + "github.com/tendermint/tendermint/libs/bech32", + "github.com/tendermint/tendermint/libs/cli", + "github.com/tendermint/tendermint/libs/cli/flags", + "github.com/tendermint/tendermint/libs/common", + "github.com/tendermint/tendermint/libs/db", + "github.com/tendermint/tendermint/libs/log", + "github.com/tendermint/tendermint/node", + "github.com/tendermint/tendermint/p2p", + "github.com/tendermint/tendermint/privval", + "github.com/tendermint/tendermint/proxy", + "github.com/tendermint/tendermint/rpc/client", + "github.com/tendermint/tendermint/rpc/core/types", + "github.com/tendermint/tendermint/rpc/lib/client", + "github.com/tendermint/tendermint/rpc/lib/server", + "github.com/tendermint/tendermint/types", + "github.com/tendermint/tendermint/version", + "github.com/tendermint/tmlibs/cli", + "github.com/zondax/ledger-goclient", + "golang.org/x/crypto/blowfish", + "golang.org/x/crypto/ripemd160", + ] solver-name = "gps-cdcl" solver-version = 1 From 879f78cfc0970c4cab8b4031549e42f4a9a4a704 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 22 Aug 2018 17:56:13 +0200 Subject: [PATCH 36/36] Rename revoke(d) to jail(ed) --- PENDING.md | 2 +- client/keys/show.go | 2 +- client/lcd/lcd_test.go | 2 +- cmd/gaia/app/sim_test.go | 2 +- cmd/gaia/cmd/gaiacli/main.go | 2 +- docs/light/api.md | 2 +- docs/spec/slashing/state.md | 2 +- docs/spec/staking/end_block.md | 2 +- docs/spec/staking/state.md | 2 +- docs/validators/validator-setup.md | 10 +++++----- examples/basecoin/cmd/basecli/main.go | 2 +- examples/democoin/mock/validator.go | 6 +++--- types/stake.go | 6 +++--- x/gov/client/cli/tx.go | 2 +- x/gov/client/cli/tx_test.go | 4 ++-- x/gov/client/rest/util.go | 10 +++++----- x/gov/tally_test.go | 4 ++-- x/slashing/app_test.go | 8 ++++---- x/slashing/client/cli/tx.go | 10 +++++----- x/slashing/client/rest/tx.go | 14 +++++++------- x/slashing/errors.go | 12 ++++++------ x/slashing/handler.go | 24 +++++++++++------------ x/slashing/handler_test.go | 10 +++++----- x/slashing/keeper.go | 16 +++++++-------- x/slashing/keeper_test.go | 28 +++++++++++++-------------- x/slashing/msg.go | 18 ++++++++--------- x/slashing/msg_test.go | 4 ++-- x/slashing/signing_info.go | 4 ++-- x/slashing/simulation/msgs.go | 10 +++++----- x/slashing/tick_test.go | 2 +- x/slashing/wire.go | 2 +- x/stake/handler.go | 4 ++-- x/stake/handler_test.go | 12 ++++++------ x/stake/keeper/delegation.go | 6 +++--- x/stake/keeper/key.go | 10 +++++----- x/stake/keeper/slash.go | 24 +++++++++++------------ x/stake/keeper/slash_test.go | 16 +++++++-------- x/stake/keeper/validator.go | 20 +++++++++---------- x/stake/stake.go | 2 +- x/stake/types/errors.go | 4 ++-- x/stake/types/validator.go | 18 ++++++++--------- 41 files changed, 170 insertions(+), 170 deletions(-) diff --git a/PENDING.md b/PENDING.md index 7ef999a7ff..cf68b69b57 100644 --- a/PENDING.md +++ b/PENDING.md @@ -13,10 +13,10 @@ BREAKING CHANGES * [cli] \#2061 changed proposalID in governance REST endpoints to proposal-id * [cli] \#2014 `gaiacli advanced` no longer exists - to access `ibc`, `rest-server`, and `validator-set` commands use `gaiacli ibc`, `gaiacli rest-server`, and `gaiacli tendermint`, respectively - * Gaia * Make the transient store key use a distinct store key. [#2013](https://github.com/cosmos/cosmos-sdk/pull/2013) * [x/stake] \#1901 Validator type's Owner field renamed to Operator; Validator's GetOwner() renamed accordingly to comply with the SDK's Validator interface. + * [x/stake, x/slashing] [#1305](https://github.com/cosmos/cosmos-sdk/issues/1305) - Rename "revoked" to "jailed" * SDK * [core] \#1807 Switch from use of rational to decimal diff --git a/client/keys/show.go b/client/keys/show.go index 7dbbc3028e..e9d692ece5 100644 --- a/client/keys/show.go +++ b/client/keys/show.go @@ -7,9 +7,9 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/gorilla/mux" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/pkg/errors" "github.com/tendermint/tmlibs/cli" ) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index fbbf6a6cff..2e7ab0af7c 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -595,7 +595,7 @@ func TestVote(t *testing.T) { require.Equal(t, gov.OptionYes, vote.Option) } -func TestUnrevoke(t *testing.T) { +func TestUnjail(t *testing.T) { _, password := "test", "1234567890" addr, _ := CreateAddr(t, "test", password, GetKeyBase(t)) cleanup, pks, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 051508e1a9..5fa75a9313 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -98,7 +98,7 @@ func testAndRunTxs(app *GaiaApp) []simulation.TestAndRunTx { stakesim.SimulateMsgCompleteUnbonding(app.stakeKeeper), stakesim.SimulateMsgBeginRedelegate(app.accountMapper, app.stakeKeeper), stakesim.SimulateMsgCompleteRedelegate(app.stakeKeeper), - slashingsim.SimulateMsgUnrevoke(app.slashingKeeper), + slashingsim.SimulateMsgUnjail(app.slashingKeeper), } } diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 25c670bdd6..df0fd3c113 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -95,7 +95,7 @@ func main() { stakecmd.GetCmdDelegate(cdc), stakecmd.GetCmdUnbond("stake", cdc), stakecmd.GetCmdRedelegate("stake", cdc), - slashingcmd.GetCmdUnrevoke(cdc), + slashingcmd.GetCmdUnjail(cdc), )...) rootCmd.AddCommand( stakeCmd, diff --git a/docs/light/api.md b/docs/light/api.md index 4507c9eb84..6c5e8aa5f0 100644 --- a/docs/light/api.md +++ b/docs/light/api.md @@ -802,7 +802,7 @@ The GovernanceAPI exposes all functionality needed for casting votes on plain te ## ICS23 - SlashingAPI -The SlashingAPI exposes all functionalities needed to slash (*i.e* penalize) validators and delegators in Proof-of-Stake. The penalization is a fine of the staking coin and jail time, defined by governance parameters. During the jail period, the penalized validator is `Revoked`. +The SlashingAPI exposes all functionalities needed to slash (*i.e* penalize) validators and delegators in Proof-of-Stake. The penalization is a fine of the staking coin and jail time, defined by governance parameters. During the jail period, the penalized validator is "jailed". ### GET /slashing/validator/{validatorAddr}/signing-info diff --git a/docs/spec/slashing/state.md b/docs/spec/slashing/state.md index 1df9d5022d..8bbb22c76a 100644 --- a/docs/spec/slashing/state.md +++ b/docs/spec/slashing/state.md @@ -47,5 +47,5 @@ type ValidatorSigningInfo struct { Where: * `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 +* `JailedUntil` is set whenever the candidate is jailed due to downtime * `SignedBlocksCounter` is a counter kept to avoid unnecessary array reads. `SignedBlocksBitArray.Sum() == SignedBlocksCounter` always. diff --git a/docs/spec/staking/end_block.md b/docs/spec/staking/end_block.md index 0eb557fecc..c2fe143baf 100644 --- a/docs/spec/staking/end_block.md +++ b/docs/spec/staking/end_block.md @@ -4,7 +4,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 +validators either being jailed due to inactivity/unexpected behaviour (covered in slashing) or changed in validator power. Determining which validator set changes must be made occurs during staking transactions (and slashing transactions) - during end-block the already accounted changes are applied and diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 46f18ed786..58bd67cd5e 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -71,7 +71,7 @@ validator. ```golang type Validator struct { ConsensusPubKey crypto.PubKey // Tendermint consensus pubkey of validator - Revoked bool // has the validator been revoked? + Jailed bool // has the validator been jailed? Status sdk.BondStatus // validator status (bonded/unbonding/unbonded) Tokens sdk.Dec // delegated tokens (incl. self-delegation) diff --git a/docs/validators/validator-setup.md b/docs/validators/validator-setup.md index e56c3c6917..6fa4fefd21 100644 --- a/docs/validators/validator-setup.md +++ b/docs/validators/validator-setup.md @@ -72,12 +72,12 @@ gaiacli stake signing-information \ --chain-id= ``` -### Unrevoke Validator +### Unjail Validator -When a validator is `Revoked` for downtime, you must submit an `Unrevoke` transaction in order to be able to get block proposer rewards again (depends on the zone fee distribution). +When a validator is "jailed" for downtime, you must submit an `Unjail` transaction in order to be able to get block proposer rewards again (depends on the zone fee distribution). ```bash -gaiacli stake unrevoke \ +gaiacli stake unjail \ --from= \ --chain-id= --validator= \ @@ -113,11 +113,11 @@ gaiad start Wait for your full node to catch up to the latest block. Next, run the following command. Note that `` is the address of your validator account, and `` is the name of the validator account. You can find this info by running `gaiacli keys list`. ```bash -gaiacli stake unrevoke --chain-id= --name= +gaiacli stake unjail --chain-id= --name= ``` ::: danger Warning -If you don't wait for `gaiad` to sync before running `unrevoke`, you will receive an error message telling you your validator is still jailed. +If you don't wait for `gaiad` to sync before running `unjail`, you will receive an error message telling you your validator is still jailed. ::: Lastly, check your validator again to see if your voting power is back. diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 15bfd8035b..23183c87a4 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -72,7 +72,7 @@ func main() { stakecmd.GetCmdDelegate(cdc), stakecmd.GetCmdUnbond("stake", cdc), stakecmd.GetCmdRedelegate("stake", cdc), - slashingcmd.GetCmdUnrevoke(cdc), + slashingcmd.GetCmdUnjail(cdc), )...) // add proxy, version and key info diff --git a/examples/democoin/mock/validator.go b/examples/democoin/mock/validator.go index f76f14803e..bb936097cc 100644 --- a/examples/democoin/mock/validator.go +++ b/examples/democoin/mock/validator.go @@ -44,7 +44,7 @@ func (v Validator) GetDelegatorShares() sdk.Dec { } // Implements sdk.Validator -func (v Validator) GetRevoked() bool { +func (v Validator) GetJailed() bool { return false } @@ -127,11 +127,11 @@ func (vs *ValidatorSet) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int6 } // Implements sdk.ValidatorSet -func (vs *ValidatorSet) Revoke(ctx sdk.Context, pubkey crypto.PubKey) { +func (vs *ValidatorSet) Jail(ctx sdk.Context, pubkey crypto.PubKey) { panic("not implemented") } // Implements sdk.ValidatorSet -func (vs *ValidatorSet) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) { +func (vs *ValidatorSet) Unjail(ctx sdk.Context, pubkey crypto.PubKey) { panic("not implemented") } diff --git a/types/stake.go b/types/stake.go index 69efed2c99..d872277256 100644 --- a/types/stake.go +++ b/types/stake.go @@ -37,7 +37,7 @@ func (b BondStatus) Equal(b2 BondStatus) bool { // validator for a delegated proof of stake system type Validator interface { - GetRevoked() bool // whether the validator is revoked + GetJailed() bool // whether the validator is jailed GetMoniker() string // moniker of the validator GetStatus() BondStatus // status of the validator GetOperator() AccAddress // owner AccAddress to receive/return validators coins @@ -73,8 +73,8 @@ type ValidatorSet interface { // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction Slash(Context, crypto.PubKey, int64, int64, Dec) - Revoke(Context, crypto.PubKey) // revoke a validator - Unrevoke(Context, crypto.PubKey) // unrevoke a validator + Jail(Context, crypto.PubKey) // jail a validator + Unjail(Context, crypto.PubKey) // unjail a validator } //_______________________________________________________________________________ diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 44ca104cf4..2a7e71aac8 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -12,11 +12,11 @@ import ( authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context" "github.com/cosmos/cosmos-sdk/x/gov" + "encoding/json" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" "io/ioutil" - "encoding/json" "strings" ) diff --git a/x/gov/client/cli/tx_test.go b/x/gov/client/cli/tx_test.go index 0ddf992d9b..e3aed05ffa 100644 --- a/x/gov/client/cli/tx_test.go +++ b/x/gov/client/cli/tx_test.go @@ -1,10 +1,10 @@ package cli import ( - "testing" "github.com/spf13/viper" - "io/ioutil" "github.com/stretchr/testify/require" + "io/ioutil" + "testing" ) func TestParseSubmitProposalFlags(t *testing.T) { diff --git a/x/gov/client/rest/util.go b/x/gov/client/rest/util.go index de94a8324c..e6aa83cc4b 100644 --- a/x/gov/client/rest/util.go +++ b/x/gov/client/rest/util.go @@ -107,10 +107,10 @@ func parseInt64OrReturnBadRequest(s string, w http.ResponseWriter) (n int64, ok var err error n, err = strconv.ParseInt(s, 10, 64) if err != nil { - w.WriteHeader(http.StatusBadRequest) - err := fmt.Errorf("'%s' is not a valid int64", s) - w.Write([]byte(err.Error())) - return 0, false + w.WriteHeader(http.StatusBadRequest) + err := fmt.Errorf("'%s' is not a valid int64", s) + w.Write([]byte(err.Error())) + return 0, false } return n, true - } \ No newline at end of file +} diff --git a/x/gov/tally_test.go b/x/gov/tally_test.go index 730885266f..157f594401 100644 --- a/x/gov/tally_test.go +++ b/x/gov/tally_test.go @@ -354,7 +354,7 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) { require.False(t, tallyResults.Equals(EmptyTallyResult())) } -func TestTallyRevokedValidator(t *testing.T) { +func TestTallyJailedValidator(t *testing.T) { mapp, keeper, sk, addrs, _, _ := getMockApp(t, 10) mapp.BeginBlock(abci.RequestBeginBlock{}) ctx := mapp.BaseApp.NewContext(false, abci.Header{}) @@ -368,7 +368,7 @@ func TestTallyRevokedValidator(t *testing.T) { val2, found := sk.GetValidator(ctx, addrs[1]) require.True(t, found) - sk.Revoke(ctx, val2.PubKey) + sk.Jail(ctx, val2.PubKey) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index e018c43e8b..86d350a84b 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -110,12 +110,12 @@ func TestSlashingMsgs(t *testing.T) { require.Equal(t, addr1, validator.Operator) require.Equal(t, sdk.Bonded, validator.Status) require.True(sdk.DecEq(t, sdk.NewDec(10), validator.BondedTokens())) - unrevokeMsg := MsgUnrevoke{ValidatorAddr: sdk.AccAddress(validator.PubKey.Address())} + unjailMsg := MsgUnjail{ValidatorAddr: sdk.AccAddress(validator.PubKey.Address())} // no signing info yet checkValidatorSigningInfo(t, mapp, keeper, sdk.ValAddress(addr1), false) - // unrevoke should fail with unknown validator - res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unrevokeMsg}, []int64{0}, []int64{1}, false, priv1) - require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotRevoked), res.Code) + // unjail should fail with unknown validator + res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unjailMsg}, []int64{0}, []int64{1}, false, priv1) + require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotJailed), res.Code) } diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index 5085f5aacc..5831ef3100 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -14,12 +14,12 @@ import ( "github.com/spf13/cobra" ) -// GetCmdUnrevoke implements the create unrevoke validator command. -func GetCmdUnrevoke(cdc *wire.Codec) *cobra.Command { +// GetCmdUnjail implements the create unjail validator command. +func GetCmdUnjail(cdc *wire.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "unrevoke", + Use: "unjail", Args: cobra.ExactArgs(0), - Short: "unrevoke validator previously revoked for downtime", + Short: "unjail validator previously jailed for downtime", RunE: func(cmd *cobra.Command, args []string) error { txCtx := authctx.NewTxContextFromCLI().WithCodec(cdc) cliCtx := context.NewCLIContext(). @@ -32,7 +32,7 @@ func GetCmdUnrevoke(cdc *wire.Codec) *cobra.Command { return err } - msg := slashing.NewMsgUnrevoke(validatorAddr) + msg := slashing.NewMsgUnjail(validatorAddr) return utils.SendTx(txCtx, cliCtx, []sdk.Msg{msg}) }, diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 2ecec51ea8..6e45230b01 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -19,13 +19,13 @@ import ( func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec, kb keys.Keybase) { r.HandleFunc( - "/slashing/unrevoke", - unrevokeRequestHandlerFn(cdc, kb, cliCtx), + "/slashing/unjail", + unjailRequestHandlerFn(cdc, kb, cliCtx), ).Methods("POST") } -// Unrevoke TX body -type UnrevokeBody struct { +// Unjail TX body +type UnjailBody struct { LocalAccountName string `json:"name"` Password string `json:"password"` ChainID string `json:"chain_id"` @@ -35,9 +35,9 @@ type UnrevokeBody struct { ValidatorAddr string `json:"validator_addr"` } -func unrevokeRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { +func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - var m UnrevokeBody + var m UnjailBody body, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -79,7 +79,7 @@ func unrevokeRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C Gas: m.Gas, } - msg := slashing.NewMsgUnrevoke(validatorAddr) + msg := slashing.NewMsgUnjail(validatorAddr) txBytes, err := txCtx.BuildAndSign(m.LocalAccountName, m.Password, []sdk.Msg{msg}) if err != nil { diff --git a/x/slashing/errors.go b/x/slashing/errors.go index 3139c1662f..4573d5e145 100644 --- a/x/slashing/errors.go +++ b/x/slashing/errors.go @@ -12,9 +12,9 @@ const ( // Default slashing codespace DefaultCodespace sdk.CodespaceType = 10 - CodeInvalidValidator CodeType = 101 - CodeValidatorJailed CodeType = 102 - CodeValidatorNotRevoked CodeType = 103 + CodeInvalidValidator CodeType = 101 + CodeValidatorJailed CodeType = 102 + CodeValidatorNotJailed CodeType = 103 ) func ErrNoValidatorForAddress(codespace sdk.CodespaceType) sdk.Error { @@ -24,8 +24,8 @@ func ErrBadValidatorAddr(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidValidator, "validator does not exist for that address") } func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeValidatorJailed, "validator jailed, cannot yet be unrevoked") + return sdk.NewError(codespace, CodeValidatorJailed, "validator still jailed, cannot yet be unjailed") } -func ErrValidatorNotRevoked(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeValidatorNotRevoked, "validator not revoked, cannot be unrevoked") +func ErrValidatorNotJailed(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeValidatorNotJailed, "validator not jailed, cannot be unjailed") } diff --git a/x/slashing/handler.go b/x/slashing/handler.go index 0cb64ab409..d79ea73c2d 100644 --- a/x/slashing/handler.go +++ b/x/slashing/handler.go @@ -8,17 +8,17 @@ func NewHandler(k Keeper) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { // NOTE msg already has validate basic run switch msg := msg.(type) { - case MsgUnrevoke: - return handleMsgUnrevoke(ctx, msg, k) + case MsgUnjail: + return handleMsgUnjail(ctx, msg, k) default: return sdk.ErrTxDecode("invalid message parse in staking module").Result() } } } -// Validators must submit a transaction to unrevoke itself after -// having been revoked (and thus unbonded) for downtime -func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result { +// Validators must submit a transaction to unjail itself after +// having been jailed (and thus unbonded) for downtime +func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result { // Validator must exist validator := k.validatorSet.Validator(ctx, msg.ValidatorAddr) @@ -26,8 +26,8 @@ func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result { return ErrNoValidatorForAddress(k.codespace).Result() } - if !validator.GetRevoked() { - return ErrValidatorNotRevoked(k.codespace).Result() + if !validator.GetJailed() { + return ErrValidatorNotJailed(k.codespace).Result() } addr := sdk.ValAddress(validator.GetPubKey().Address()) @@ -38,19 +38,19 @@ func handleMsgUnrevoke(ctx sdk.Context, msg MsgUnrevoke, k Keeper) sdk.Result { return ErrNoValidatorForAddress(k.codespace).Result() } - // Cannot be unrevoked until out of jail + // Cannot be unjailed until out of jail if ctx.BlockHeader().Time.Before(info.JailedUntil) { return ErrValidatorJailed(k.codespace).Result() } - // Update the starting height (so the validator can't be immediately revoked again) + // Update the starting height (so the validator can't be immediately jailed again) info.StartHeight = ctx.BlockHeight() k.setValidatorSigningInfo(ctx, addr, info) - // Unrevoke the validator - k.validatorSet.Unrevoke(ctx, validator.GetPubKey()) + // Unjail the validator + k.validatorSet.Unjail(ctx, validator.GetPubKey()) - tags := sdk.NewTags("action", []byte("unrevoke"), "validator", []byte(msg.ValidatorAddr.String())) + tags := sdk.NewTags("action", []byte("unjail"), "validator", []byte(msg.ValidatorAddr.String())) return sdk.Result{ Tags: tags, diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index 41cb5d1996..4408d3c09d 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/x/stake" ) -func TestCannotUnrevokeUnlessRevoked(t *testing.T) { +func TestCannotUnjailUnlessJailed(t *testing.T) { // initial setup ctx, ck, sk, _, keeper := createTestInput(t) slh := NewHandler(keeper) @@ -22,8 +22,8 @@ func TestCannotUnrevokeUnlessRevoked(t *testing.T) { require.Equal(t, ck.GetCoins(ctx, addr), sdk.Coins{{sk.GetParams(ctx).BondDenom, initCoins.Sub(amt)}}) require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) - // assert non-revoked validator can't be unrevoked - got = slh(ctx, NewMsgUnrevoke(addr)) - require.False(t, got.IsOK(), "allowed unrevoke of non-revoked validator") - require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotRevoked), got.Code) + // assert non-jailed validator can't be unjailed + got = slh(ctx, NewMsgUnjail(addr)) + require.False(t, got.IsOK(), "allowed unjail of non-jailed validator") + require.Equal(t, sdk.ToABCICode(DefaultCodespace, CodeValidatorNotJailed), got.Code) } diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 373423b2d7..6d8e47cbe2 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -59,10 +59,10 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio // Slash validator k.validatorSet.Slash(ctx, pubkey, infractionHeight, power, k.SlashFractionDoubleSign(ctx)) - // Revoke validator - k.validatorSet.Revoke(ctx, pubkey) - // Jail validator + k.validatorSet.Jail(ctx, pubkey) + + // Set validator jail duration signInfo, found := k.getValidatorSigningInfo(ctx, address) if !found { panic(fmt.Sprintf("Expected signing info for validator %s but not found", address)) @@ -113,16 +113,16 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx) if height > minHeight && signInfo.SignedBlocksCounter < k.MinSignedPerWindow(ctx) { validator := k.validatorSet.ValidatorByPubKey(ctx, pubkey) - if validator != nil && !validator.GetRevoked() { - // Downtime confirmed, slash, revoke, and jail the validator + if validator != nil && !validator.GetJailed() { + // Downtime confirmed: slash and jail the validator logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d", pubkey.Address(), minHeight, k.MinSignedPerWindow(ctx))) k.validatorSet.Slash(ctx, pubkey, height, power, k.SlashFractionDowntime(ctx)) - k.validatorSet.Revoke(ctx, pubkey) + k.validatorSet.Jail(ctx, pubkey) signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeUnbondDuration(ctx)) } else { - // Validator was (a) not found or (b) already revoked, don't slash - logger.Info(fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already revoked", + // Validator was (a) not found or (b) already jailed, don't slash + logger.Info(fmt.Sprintf("Validator %s would have been slashed for downtime, but was either not found in store or already jailed", pubkey.Address())) } } diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index 5ebfec21ad..808c82014e 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -39,10 +39,10 @@ func TestHandleDoubleSign(t *testing.T) { // double sign less than max age keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt) - // should be revoked - require.True(t, sk.Validator(ctx, addr).GetRevoked()) - // unrevoke to measure power - sk.Unrevoke(ctx, val) + // should be jailed + require.True(t, sk.Validator(ctx, addr).GetJailed()) + // unjail to measure power + sk.Unjail(ctx, val) // power should be reduced require.Equal(t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))), sk.Validator(ctx, addr).GetPower()) ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.MaxEvidenceAge(ctx))}) @@ -112,17 +112,17 @@ func TestHandleAbsentValidator(t *testing.T) { require.Equal(t, int64(0), info.StartHeight) require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter) - // validator should have been revoked + // validator should have been jailed validator, _ = sk.GetValidatorByPubKey(ctx, val) require.Equal(t, sdk.Unbonded, validator.GetStatus()) // unrevocation should fail prior to jail expiration - got = slh(ctx, NewMsgUnrevoke(addr)) + got = slh(ctx, NewMsgUnjail(addr)) require.False(t, got.IsOK()) // unrevocation should succeed after jail expiration ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.DowntimeUnbondDuration(ctx))}) - got = slh(ctx, NewMsgUnrevoke(addr)) + got = slh(ctx, NewMsgUnjail(addr)) require.True(t, got.IsOK()) // validator should be rebonded now @@ -140,7 +140,7 @@ func TestHandleAbsentValidator(t *testing.T) { require.Equal(t, height, info.StartHeight) require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter) - // validator should not be immediately revoked again + // validator should not be immediately jailed again height++ ctx = ctx.WithBlockHeight(height) keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) @@ -154,7 +154,7 @@ func TestHandleAbsentValidator(t *testing.T) { keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } - // validator should be revoked again after 500 unsigned blocks + // validator should be jailed again after 500 unsigned blocks nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1 for ; height <= nextHeight; height++ { ctx = ctx.WithBlockHeight(height) @@ -166,7 +166,7 @@ func TestHandleAbsentValidator(t *testing.T) { // Test a new validator entering the validator set // Ensure that SigningInfo.StartHeight is set correctly -// and that they are not immediately revoked +// and that they are not immediately jailed func TestHandleNewValidator(t *testing.T) { // initial setup ctx, ck, sk, _, keeper := createTestInput(t) @@ -194,16 +194,16 @@ func TestHandleNewValidator(t *testing.T) { require.Equal(t, int64(1), info.SignedBlocksCounter) require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) - // validator should be bonded still, should not have been revoked or slashed + // validator should be bonded still, should not have been jailed or slashed validator, _ := sk.GetValidatorByPubKey(ctx, val) require.Equal(t, sdk.Bonded, validator.GetStatus()) pool := sk.GetPool(ctx) require.Equal(t, int64(100), pool.BondedTokens.RoundInt64()) } -// Test a revoked validator being "down" twice +// Test a jailed validator being "down" twice // Ensure that they're only slashed once -func TestHandleAlreadyRevoked(t *testing.T) { +func TestHandleAlreadyJailed(t *testing.T) { // initial setup ctx, _, sk, _, keeper := createTestInput(t) @@ -228,7 +228,7 @@ func TestHandleAlreadyRevoked(t *testing.T) { keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } - // validator should have been revoked and slashed + // validator should have been jailed and slashed validator, _ := sk.GetValidatorByPubKey(ctx, val) require.Equal(t, sdk.Unbonded, validator.GetStatus()) diff --git a/x/slashing/msg.go b/x/slashing/msg.go index 4b1483dce9..e22eca7bdd 100644 --- a/x/slashing/msg.go +++ b/x/slashing/msg.go @@ -11,25 +11,25 @@ var cdc = wire.NewCodec() const MsgType = "slashing" // verify interface at compile time -var _ sdk.Msg = &MsgUnrevoke{} +var _ sdk.Msg = &MsgUnjail{} -// MsgUnrevoke - struct for unrevoking revoked validator -type MsgUnrevoke struct { +// MsgUnjail - struct for unjailing jailed validator +type MsgUnjail struct { ValidatorAddr sdk.AccAddress `json:"address"` // address of the validator owner } -func NewMsgUnrevoke(validatorAddr sdk.AccAddress) MsgUnrevoke { - return MsgUnrevoke{ +func NewMsgUnjail(validatorAddr sdk.AccAddress) MsgUnjail { + return MsgUnjail{ ValidatorAddr: validatorAddr, } } //nolint -func (msg MsgUnrevoke) Type() string { return MsgType } -func (msg MsgUnrevoke) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.ValidatorAddr} } +func (msg MsgUnjail) Type() string { return MsgType } +func (msg MsgUnjail) GetSigners() []sdk.AccAddress { return []sdk.AccAddress{msg.ValidatorAddr} } // get the bytes for the message signer to sign on -func (msg MsgUnrevoke) GetSignBytes() []byte { +func (msg MsgUnjail) GetSignBytes() []byte { b, err := cdc.MarshalJSON(msg) if err != nil { panic(err) @@ -38,7 +38,7 @@ func (msg MsgUnrevoke) GetSignBytes() []byte { } // quick validity check -func (msg MsgUnrevoke) ValidateBasic() sdk.Error { +func (msg MsgUnjail) ValidateBasic() sdk.Error { if msg.ValidatorAddr == nil { return ErrBadValidatorAddr(DefaultCodespace) } diff --git a/x/slashing/msg_test.go b/x/slashing/msg_test.go index 287eb6c5c5..ef2e673c86 100644 --- a/x/slashing/msg_test.go +++ b/x/slashing/msg_test.go @@ -8,9 +8,9 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestMsgUnrevokeGetSignBytes(t *testing.T) { +func TestMsgUnjailGetSignBytes(t *testing.T) { addr := sdk.AccAddress("abcd") - msg := NewMsgUnrevoke(addr) + msg := NewMsgUnjail(addr) bytes := msg.GetSignBytes() require.Equal(t, string(bytes), `{"address":"cosmosaccaddr1v93xxeqhyqz5v"}`) } diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index a21917e796..25a83e833d 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -60,9 +60,9 @@ func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil t // Signing info for a validator type ValidatorSigningInfo struct { - StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unrevoked + StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unjailed IndexOffset int64 `json:"index_offset"` // index offset into signed block bit array - JailedUntil time.Time `json:"jailed_until"` // timestamp validator cannot be unrevoked until + JailedUntil time.Time `json:"jailed_until"` // timestamp validator cannot be unjailed until SignedBlocksCounter int64 `json:"signed_blocks_counter"` // signed blocks counter (to avoid scanning the array every time) } diff --git a/x/slashing/simulation/msgs.go b/x/slashing/simulation/msgs.go index b6a093674b..8ef24493be 100644 --- a/x/slashing/simulation/msgs.go +++ b/x/slashing/simulation/msgs.go @@ -15,20 +15,20 @@ import ( "github.com/cosmos/cosmos-sdk/x/slashing" ) -// SimulateMsgUnrevoke -func SimulateMsgUnrevoke(k slashing.Keeper) simulation.TestAndRunTx { +// SimulateMsgUnjail +func SimulateMsgUnjail(k slashing.Keeper) simulation.TestAndRunTx { return func(t *testing.T, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, err sdk.Error) { key := simulation.RandomKey(r, keys) address := sdk.AccAddress(key.PubKey().Address()) - msg := slashing.NewMsgUnrevoke(address) + msg := slashing.NewMsgUnjail(address) require.Nil(t, msg.ValidateBasic(), "expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) ctx, write := ctx.CacheContext() result := slashing.NewHandler(k)(ctx, msg) if result.IsOK() { write() } - event(fmt.Sprintf("slashing/MsgUnrevoke/%v", result.IsOK())) - action = fmt.Sprintf("TestMsgUnrevoke: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) + event(fmt.Sprintf("slashing/MsgUnjail/%v", result.IsOK())) + action = fmt.Sprintf("TestMsgUnjail: ok %v, msg %s", result.IsOK(), msg.GetSignBytes()) return action, nil } } diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index b230f9c940..78d2695494 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -77,7 +77,7 @@ func TestBeginBlocker(t *testing.T) { BeginBlocker(ctx, req, keeper) } - // validator should be revoked + // validator should be jailed validator, found := sk.GetValidatorByPubKey(ctx, pk) require.True(t, found) require.Equal(t, sdk.Unbonded, validator.GetStatus()) diff --git a/x/slashing/wire.go b/x/slashing/wire.go index 465a06587e..208acda06f 100644 --- a/x/slashing/wire.go +++ b/x/slashing/wire.go @@ -6,7 +6,7 @@ import ( // Register concrete types on wire codec func RegisterWire(cdc *wire.Codec) { - cdc.RegisterConcrete(MsgUnrevoke{}, "cosmos-sdk/MsgUnrevoke", nil) + cdc.RegisterConcrete(MsgUnjail{}, "cosmos-sdk/MsgUnjail", nil) } var cdcEmpty = wire.NewCodec() diff --git a/x/stake/handler.go b/x/stake/handler.go index 8f7475de9b..c8be6a835a 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -136,8 +136,8 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) if msg.Delegation.Denom != k.GetParams(ctx).BondDenom { return ErrBadDenom(k.Codespace()).Result() } - if validator.Revoked == true { - return ErrValidatorRevoked(k.Codespace()).Result() + if validator.Jailed { + return ErrValidatorJailed(k.Codespace()).Result() } _, err := k.Delegate(ctx, msg.DelegatorAddr, msg.Delegation, validator, true) if err != nil { diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index 9ff72090e1..6ab54e48c2 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -80,9 +80,9 @@ func TestValidatorByPowerIndex(t *testing.T) { got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) - // slash and revoke the first validator + // slash and jail the first validator keeper.Slash(ctx, keep.PKs[0], 0, initBond, sdk.NewDecWithPrec(5, 1)) - keeper.Revoke(ctx, keep.PKs[0]) + keeper.Jail(ctx, keep.PKs[0]) validator, found = keeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.Equal(t, sdk.Unbonded, validator.Status) // ensure is unbonded @@ -387,7 +387,7 @@ func TestMultipleMsgCreateValidator(t *testing.T) { require.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot) } - // unbond them all by revoking delegation + // unbond them all by removing delegation for i, validatorAddr := range validatorAddrs { _, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) @@ -449,7 +449,7 @@ func TestMultipleMsgDelegate(t *testing.T) { } } -func TestRevokeValidator(t *testing.T) { +func TestJailValidator(t *testing.T) { ctx, _, keeper := keep.CreateTestInput(t, false, 1000) validatorAddr, delegatorAddr := keep.Addrs[0], keep.Addrs[1] _ = setInstantUnbondPeriod(keeper, ctx) @@ -476,9 +476,9 @@ func TestRevokeValidator(t *testing.T) { validator, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) - require.True(t, validator.Revoked, "%v", validator) + require.True(t, validator.Jailed, "%v", validator) - // test that this address cannot yet be bonded too because is revoked + // test that this address cannot yet be bonded too because is jailed got = handleMsgDelegate(ctx, msgDelegate, keeper) require.False(t, got.IsOK(), "expected error, got %v", got) diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index 4256df4b59..2bad79c20f 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -283,9 +283,9 @@ func (k Keeper) unbond(ctx sdk.Context, delegatorAddr, validatorAddr sdk.AccAddr if delegation.Shares.IsZero() { // if the delegation is the operator of the validator then - // trigger a revoke validator - if bytes.Equal(delegation.DelegatorAddr, validator.Operator) && validator.Revoked == false { - validator.Revoked = true + // trigger a jail validator + if bytes.Equal(delegation.DelegatorAddr, validator.Operator) && validator.Jailed == false { + validator.Jailed = true } k.RemoveDelegation(ctx, delegation) } else { diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index 502ec26544..958de62ed5 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -73,11 +73,11 @@ func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte { potentialPower := validator.Tokens powerBytes := []byte(potentialPower.ToLeftPadded(maxDigitsForAccount)) // power big-endian (more powerful validators first) - revokedBytes := make([]byte, 1) - if validator.Revoked { - revokedBytes[0] = byte(0x00) + jailedBytes := make([]byte, 1) + if validator.Jailed { + jailedBytes[0] = byte(0x00) } else { - revokedBytes[0] = byte(0x01) + jailedBytes[0] = byte(0x01) } // heightBytes and counterBytes represent strings like powerBytes does @@ -88,7 +88,7 @@ func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte { return append(append(append(append( ValidatorsByPowerIndexKey, - revokedBytes...), + jailedBytes...), powerBytes...), heightBytes...), counterBytes...) diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index 471886c26b..aa6fe974d8 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -115,31 +115,31 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in return } -// revoke a validator -func (k Keeper) Revoke(ctx sdk.Context, pubkey crypto.PubKey) { - k.setRevoked(ctx, pubkey, true) +// jail a validator +func (k Keeper) Jail(ctx sdk.Context, pubkey crypto.PubKey) { + k.setJailed(ctx, pubkey, true) logger := ctx.Logger().With("module", "x/stake") - logger.Info(fmt.Sprintf("Validator %s revoked", pubkey.Address())) + logger.Info(fmt.Sprintf("Validator %s jailed", pubkey.Address())) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return } -// unrevoke a validator -func (k Keeper) Unrevoke(ctx sdk.Context, pubkey crypto.PubKey) { - k.setRevoked(ctx, pubkey, false) +// unjail a validator +func (k Keeper) Unjail(ctx sdk.Context, pubkey crypto.PubKey) { + k.setJailed(ctx, pubkey, false) logger := ctx.Logger().With("module", "x/stake") - logger.Info(fmt.Sprintf("Validator %s unrevoked", pubkey.Address())) + logger.Info(fmt.Sprintf("Validator %s unjailed", pubkey.Address())) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return } -// set the revoked flag on a validator -func (k Keeper) setRevoked(ctx sdk.Context, pubkey crypto.PubKey, revoked bool) { +// set the jailed flag on a validator +func (k Keeper) setJailed(ctx sdk.Context, pubkey crypto.PubKey, jailed bool) { validator, found := k.GetValidatorByPubKey(ctx, pubkey) if !found { - panic(fmt.Errorf("Validator with pubkey %s not found, cannot set revoked to %v", pubkey, revoked)) + panic(fmt.Errorf("Validator with pubkey %s not found, cannot set jailed to %v", pubkey, jailed)) } - validator.Revoked = revoked + validator.Jailed = jailed k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it return } diff --git a/x/stake/keeper/slash_test.go b/x/stake/keeper/slash_test.go index 444858f290..156f0602e9 100644 --- a/x/stake/keeper/slash_test.go +++ b/x/stake/keeper/slash_test.go @@ -34,7 +34,7 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) { return ctx, keeper, params } -// tests Revoke, Unrevoke +// tests Jail, Unjail func TestRevocation(t *testing.T) { // setup ctx, keeper, _ := setupHelper(t, 10) @@ -44,19 +44,19 @@ func TestRevocation(t *testing.T) { // initial state val, found := keeper.GetValidator(ctx, addr) require.True(t, found) - require.False(t, val.GetRevoked()) + require.False(t, val.GetJailed()) - // test revoke - keeper.Revoke(ctx, pk) + // test jail + keeper.Jail(ctx, pk) val, found = keeper.GetValidator(ctx, addr) require.True(t, found) - require.True(t, val.GetRevoked()) + require.True(t, val.GetJailed()) - // test unrevoke - keeper.Unrevoke(ctx, pk) + // test unjail + keeper.Unjail(ctx, pk) val, found = keeper.GetValidator(ctx, addr) require.True(t, found) - require.False(t, val.GetRevoked()) + require.False(t, val.GetJailed()) } diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index c828cc38ef..e933a6d236 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -205,7 +205,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type pool := k.GetPool(ctx) oldValidator, oldFound := k.GetValidator(ctx, validator.Operator) - validator = k.updateForRevoking(ctx, oldFound, oldValidator, validator) + validator = k.updateForJailing(ctx, oldFound, oldValidator, validator) powerIncreasing := k.getPowerIncreasing(ctx, oldFound, oldValidator, validator) validator.BondHeight, validator.BondIntraTxCounter = k.bondIncrement(ctx, oldFound, oldValidator, validator) valPower := k.updateValidatorPower(ctx, oldFound, oldValidator, validator, pool) @@ -216,7 +216,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type // perform the following: // a) update Tendermint // b) check if the cliff validator needs to be updated - case powerIncreasing && !validator.Revoked && + case powerIncreasing && !validator.Jailed && (oldFound && oldValidator.Status == sdk.Bonded): bz := k.cdc.MustMarshalBinary(validator.ABCIValidator()) @@ -293,8 +293,8 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr)) } - if currVal.Status != sdk.Bonded || currVal.Revoked { - panic(fmt.Sprintf("unexpected revoked or unbonded validator for address: %s\n", ownerAddr)) + if currVal.Status != sdk.Bonded || currVal.Jailed { + panic(fmt.Sprintf("unexpected jailed or unbonded validator for address: %s\n", ownerAddr)) } newCliffVal = currVal @@ -319,11 +319,11 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato } } -func (k Keeper) updateForRevoking(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator { - if newValidator.Revoked && oldFound && oldValidator.Status == sdk.Bonded { +func (k Keeper) updateForJailing(ctx sdk.Context, oldFound bool, oldValidator, newValidator types.Validator) types.Validator { + if newValidator.Jailed && oldFound && oldValidator.Status == sdk.Bonded { newValidator = k.unbondValidator(ctx, newValidator) - // need to also clear the cliff validator spot because the revoke has + // need to also clear the cliff validator spot because the jail has // opened up a new spot which will be filled when // updateValidatorsBonded is called k.clearCliffValidator(ctx) @@ -417,7 +417,7 @@ func (k Keeper) UpdateBondedValidators( } // increment bondedValidatorsCount / get the validator to bond - if !validator.Revoked { + if !validator.Jailed { if validator.Status != sdk.Bonded { validatorToBond = validator if newValidatorBonded { @@ -529,11 +529,11 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) { validator = k.bondValidator(ctx, validator) } - if validator.Revoked { + if validator.Jailed { // we should no longer consider jailed validators as they are ranked // lower than any non-jailed/bonded validators if validator.Status == sdk.Bonded { - panic(fmt.Sprintf("revoked validator cannot be bonded for address: %s\n", ownerAddr)) + panic(fmt.Sprintf("jailed validator cannot be bonded for address: %s\n", ownerAddr)) } break diff --git a/x/stake/stake.go b/x/stake/stake.go index c5185433dc..2782957ceb 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -94,7 +94,7 @@ var ( ErrNoValidatorFound = types.ErrNoValidatorFound ErrValidatorOwnerExists = types.ErrValidatorOwnerExists ErrValidatorPubKeyExists = types.ErrValidatorPubKeyExists - ErrValidatorRevoked = types.ErrValidatorRevoked + ErrValidatorJailed = types.ErrValidatorJailed ErrBadRemoveValidator = types.ErrBadRemoveValidator ErrDescriptionLength = types.ErrDescriptionLength ErrCommissionNegative = types.ErrCommissionNegative diff --git a/x/stake/types/errors.go b/x/stake/types/errors.go index d44ed411c5..0c1d9192d7 100644 --- a/x/stake/types/errors.go +++ b/x/stake/types/errors.go @@ -44,8 +44,8 @@ func ErrValidatorPubKeyExists(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidValidator, "validator already exist for this pubkey, must use new validator pubkey") } -func ErrValidatorRevoked(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidValidator, "validator for this address is currently revoked") +func ErrValidatorJailed(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidValidator, "validator for this address is currently jailed") } func ErrBadRemoveValidator(codespace sdk.CodespaceType) sdk.Error { diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index 4b2cf46a63..f8404b5963 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -22,7 +22,7 @@ import ( type Validator struct { Operator sdk.AccAddress `json:"operator"` // sender of BondTx - UnbondTx returns here PubKey crypto.PubKey `json:"pub_key"` // pubkey of validator - Revoked bool `json:"revoked"` // has the validator been revoked from bonded status? + Jailed bool `json:"jailed"` // has the validator been jailed from bonded status? Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) Tokens sdk.Dec `json:"tokens"` // delegated tokens (incl. self-delegation) @@ -47,7 +47,7 @@ func NewValidator(operator sdk.AccAddress, pubKey crypto.PubKey, description Des return Validator{ Operator: operator, PubKey: pubKey, - Revoked: false, + Jailed: false, Status: sdk.Unbonded, Tokens: sdk.ZeroDec(), DelegatorShares: sdk.ZeroDec(), @@ -66,7 +66,7 @@ func NewValidator(operator sdk.AccAddress, pubKey crypto.PubKey, description Des // what's kept in the store value type validatorValue struct { PubKey crypto.PubKey - Revoked bool + Jailed bool Status sdk.BondStatus Tokens sdk.Dec DelegatorShares sdk.Dec @@ -85,7 +85,7 @@ type validatorValue struct { func MustMarshalValidator(cdc *wire.Codec, validator Validator) []byte { val := validatorValue{ PubKey: validator.PubKey, - Revoked: validator.Revoked, + Jailed: validator.Jailed, Status: validator.Status, Tokens: validator.Tokens, DelegatorShares: validator.DelegatorShares, @@ -127,7 +127,7 @@ func UnmarshalValidator(cdc *wire.Codec, operatorAddr, value []byte) (validator return Validator{ Operator: operatorAddr, PubKey: storeValue.PubKey, - Revoked: storeValue.Revoked, + Jailed: storeValue.Jailed, Tokens: storeValue.Tokens, Status: storeValue.Status, DelegatorShares: storeValue.DelegatorShares, @@ -155,7 +155,7 @@ func (v Validator) HumanReadableString() (string, error) { resp := "Validator \n" resp += fmt.Sprintf("Operator: %s\n", v.Operator) resp += fmt.Sprintf("Validator: %s\n", bechVal) - resp += fmt.Sprintf("Revoked: %v\n", v.Revoked) + resp += fmt.Sprintf("Jailed: %v\n", v.Jailed) resp += fmt.Sprintf("Status: %s\n", sdk.BondStatusToString(v.Status)) resp += fmt.Sprintf("Tokens: %s\n", v.Tokens.String()) resp += fmt.Sprintf("Delegator Shares: %s\n", v.DelegatorShares.String()) @@ -177,7 +177,7 @@ func (v Validator) HumanReadableString() (string, error) { type BechValidator struct { Operator sdk.AccAddress `json:"operator"` // in bech32 PubKey string `json:"pub_key"` // in bech32 - Revoked bool `json:"revoked"` // has the validator been revoked from bonded status? + Jailed bool `json:"jailed"` // has the validator been jailed from bonded status? Status sdk.BondStatus `json:"status"` // validator status (bonded/unbonding/unbonded) Tokens sdk.Dec `json:"tokens"` // delegated tokens (incl. self-delegation) @@ -207,7 +207,7 @@ func (v Validator) Bech32Validator() (BechValidator, error) { return BechValidator{ Operator: v.Operator, PubKey: bechValPubkey, - Revoked: v.Revoked, + Jailed: v.Jailed, Status: v.Status, Tokens: v.Tokens, @@ -429,7 +429,7 @@ func (v Validator) BondedTokens() sdk.Dec { var _ sdk.Validator = Validator{} // nolint - for sdk.Validator -func (v Validator) GetRevoked() bool { return v.Revoked } +func (v Validator) GetJailed() bool { return v.Jailed } func (v Validator) GetMoniker() string { return v.Description.Moniker } func (v Validator) GetStatus() sdk.BondStatus { return v.Status } func (v Validator) GetOperator() sdk.AccAddress { return v.Operator }