From dc75eb4eb8465ec1b299a2dae783809550febfe7 Mon Sep 17 00:00:00 2001 From: frog power 4000 Date: Thu, 24 Jan 2019 17:44:31 -0500 Subject: [PATCH 01/99] Merge PR #3372: Limit unbonding delegation / redelegations --- PENDING.md | 1 + docs/spec/staking/messages.md | 4 ++ docs/spec/staking/state.md | 1 + x/staking/keeper/delegation.go | 31 +++++++++ x/staking/keeper/delegation_test.go | 102 +++++++++++++++++++++++++++- x/staking/keeper/params.go | 19 ++++-- x/staking/types/errors.go | 10 ++- x/staking/types/params.go | 23 +++++-- 8 files changed, 177 insertions(+), 14 deletions(-) diff --git a/PENDING.md b/PENDING.md index 4d87301f0..0a01c3eb6 100644 --- a/PENDING.md +++ b/PENDING.md @@ -22,6 +22,7 @@ FEATURES * Gaia * SDK + * \#3270 [x/staking] limit number of ongoing unbonding delegations /redelegations per pair/trio * Tendermint diff --git a/docs/spec/staking/messages.md b/docs/spec/staking/messages.md index 7af6e6ccc..39bf43c92 100644 --- a/docs/spec/staking/messages.md +++ b/docs/spec/staking/messages.md @@ -101,6 +101,8 @@ This message is expected to fail if: - the delegation doesn't exist - the validator doesn't exist - the delegation has less shares than `SharesAmount` + - existing `UnbondingDelegation` has maximum entries as defined by + params.MaxEntries When this message is processed the following actions occur: - validator's `DelegatorShares` and the delegation's `Shares` are both reduced @@ -143,6 +145,8 @@ This message is expected to fail if: - the delegation has less shares than `SharesAmount` - the source validator has a receiving redelegation which is not matured (aka. the redelegation may be transitive) + - existing `Redelegation` has maximum entries as defined by + params.MaxEntries When this message is processed the following actions occur: - the source validator's `DelegatorShares` and the delegations `Shares` are diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index 5e8f4d327..cd49b7326 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -28,6 +28,7 @@ and defines overall functioning of the staking module. type Params struct { UnbondingTime time.Duration // time duration of unbonding MaxValidators uint16 // maximum number of validators + MaxEntries uint16 // max entries for either unbonding delegation or redelegation (per pair/trio) BondDenom string // bondable coin denomination } ``` diff --git a/x/staking/keeper/delegation.go b/x/staking/keeper/delegation.go index 5a15b4d03..162d87aa4 100644 --- a/x/staking/keeper/delegation.go +++ b/x/staking/keeper/delegation.go @@ -153,6 +153,17 @@ func (k Keeper) IterateUnbondingDelegations(ctx sdk.Context, fn func(index int64 } } +// HasMaxUnbondingDelegationEntries - unbonding delegation has maximum number of entries +func (k Keeper) HasMaxUnbondingDelegationEntries(ctx sdk.Context, + delegatorAddr sdk.AccAddress, validatorAddr sdk.ValAddress) bool { + + ubd, found := k.GetUnbondingDelegation(ctx, delegatorAddr, validatorAddr) + if !found { + return false + } + return len(ubd.Entries) >= int(k.MaxEntries(ctx)) +} + // set the unbonding delegation and associated index func (k Keeper) SetUnbondingDelegation(ctx sdk.Context, ubd types.UnbondingDelegation) { store := ctx.KVStore(k.storeKey) @@ -313,6 +324,18 @@ func (k Keeper) HasReceivingRedelegation(ctx sdk.Context, return false } +// HasMaxRedelegationEntries - redelegation has maximum number of entries +func (k Keeper) HasMaxRedelegationEntries(ctx sdk.Context, + delegatorAddr sdk.AccAddress, validatorSrcAddr, + validatorDstAddr sdk.ValAddress) bool { + + red, found := k.GetRedelegation(ctx, delegatorAddr, validatorSrcAddr, validatorDstAddr) + if !found { + return false + } + return len(red.Entries) >= int(k.MaxEntries(ctx)) +} + // set a redelegation and associated index func (k Keeper) SetRedelegation(ctx sdk.Context, red types.Redelegation) { store := ctx.KVStore(k.storeKey) @@ -580,6 +603,10 @@ func (k Keeper) Undelegate(ctx sdk.Context, delAddr sdk.AccAddress, return completionTime, nil } + if k.HasMaxUnbondingDelegationEntries(ctx, delAddr, valAddr) { + return time.Time{}, types.ErrMaxUnbondingDelegationEntries(k.Codespace()) + } + ubd := k.SetUnbondingDelegationEntry(ctx, delAddr, valAddr, height, completionTime, balance) @@ -637,6 +664,10 @@ func (k Keeper) BeginRedelegation(ctx sdk.Context, delAddr sdk.AccAddress, return time.Time{}, types.ErrTransitiveRedelegation(k.Codespace()) } + if k.HasMaxRedelegationEntries(ctx, delAddr, valSrcAddr, valDstAddr) { + return time.Time{}, types.ErrMaxRedelegationEntries(k.Codespace()) + } + returnAmount, err := k.unbond(ctx, delAddr, valSrcAddr, sharesAmount) if err != nil { return time.Time{}, err diff --git a/x/staking/keeper/delegation_test.go b/x/staking/keeper/delegation_test.go index 3b4243319..590e72826 100644 --- a/x/staking/keeper/delegation_test.go +++ b/x/staking/keeper/delegation_test.go @@ -198,7 +198,7 @@ func TestUnbondDelegation(t *testing.T) { amount, err := keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewDec(6)) require.NoError(t, err) - require.Equal(t, int64(6), amount.Int64()) // shares to be added to an unbonding delegation / redelegation + require.Equal(t, int64(6), amount.Int64()) // shares to be added to an unbonding delegation delegation, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) @@ -212,6 +212,53 @@ func TestUnbondDelegation(t *testing.T) { require.Equal(t, int64(4), pool.BondedTokens.Int64()) } +func TestUnbondingDelegationsMaxEntries(t *testing.T) { + ctx, _, keeper := CreateTestInput(t, false, 0) + pool := keeper.GetPool(ctx) + pool.NotBondedTokens = sdk.NewInt(10) + + // create a validator and a delegator to that validator + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + + pool = keeper.GetPool(ctx) + require.Equal(t, int64(10), pool.BondedTokens.Int64()) + require.Equal(t, int64(10), validator.BondedTokens().Int64()) + + delegation := types.Delegation{ + DelegatorAddr: addrDels[0], + ValidatorAddr: addrVals[0], + Shares: issuedShares, + } + keeper.SetDelegation(ctx, delegation) + + maxEntries := keeper.MaxEntries(ctx) + + // should all pass + var completionTime time.Time + for i := uint16(0); i < maxEntries; i++ { + var err error + completionTime, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) + require.NoError(t, err) + } + + // an additional unbond should fail due to max entries + _, err := keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) + require.Error(t, err) + + // mature unbonding delegations + ctx = ctx.WithBlockTime(completionTime) + err = keeper.CompleteUnbonding(ctx, addrDels[0], addrVals[0]) + require.NoError(t, err) + + // unbonding should work again + _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(1)) + require.NoError(t, err) +} + // test removing all self delegation from a validator which should // shift it from the bonded to unbonded state func TestUndelegateSelfDelegation(t *testing.T) { @@ -593,6 +640,59 @@ func TestRedelegateToSameValidator(t *testing.T) { } +func TestRedelegationMaxEntries(t *testing.T) { + ctx, _, keeper := CreateTestInput(t, false, 0) + pool := keeper.GetPool(ctx) + pool.NotBondedTokens = sdk.NewInt(20) + + // create a validator with a self-delegation + validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + keeper.SetPool(ctx, pool) + validator = TestingUpdateValidator(keeper, ctx, validator, true) + pool = keeper.GetPool(ctx) + val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) + selfDelegation := types.Delegation{ + DelegatorAddr: val0AccAddr, + ValidatorAddr: addrVals[0], + Shares: issuedShares, + } + keeper.SetDelegation(ctx, selfDelegation) + + // create a second validator + validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) + validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10)) + require.Equal(t, int64(10), issuedShares.RoundInt64()) + pool.BondedTokens = pool.BondedTokens.Add(sdk.NewInt(10)) + keeper.SetPool(ctx, pool) + validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) + require.Equal(t, sdk.Bonded, validator2.Status) + + maxEntries := keeper.MaxEntries(ctx) + + // redelegations should pass + var completionTime time.Time + for i := uint16(0); i < maxEntries; i++ { + var err error + completionTime, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) + require.NoError(t, err) + } + + // an additional redelegation should fail due to max entries + _, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) + require.Error(t, err) + + // mature redelegations + ctx = ctx.WithBlockTime(completionTime) + err = keeper.CompleteRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1]) + require.NoError(t, err) + + // redelegation should work again + _, err = keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(1)) + require.NoError(t, err) +} + func TestRedelegateSelfDelegation(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) diff --git a/x/staking/keeper/params.go b/x/staking/keeper/params.go index 2d3fdf296..ffc5d45af 100644 --- a/x/staking/keeper/params.go +++ b/x/staking/keeper/params.go @@ -30,6 +30,13 @@ func (k Keeper) MaxValidators(ctx sdk.Context) (res uint16) { return } +// MaxEntries - Maximum number of simultaneous unbonding +// delegations or redelegations (per pair/trio) +func (k Keeper) MaxEntries(ctx sdk.Context) (res uint16) { + k.paramstore.Get(ctx, types.KeyMaxEntries, &res) + return +} + // BondDenom - Bondable coin denomination func (k Keeper) BondDenom(ctx sdk.Context) (res string) { k.paramstore.Get(ctx, types.KeyBondDenom, &res) @@ -37,11 +44,13 @@ func (k Keeper) BondDenom(ctx sdk.Context) (res string) { } // Get all parameteras as types.Params -func (k Keeper) GetParams(ctx sdk.Context) (res types.Params) { - res.UnbondingTime = k.UnbondingTime(ctx) - res.MaxValidators = k.MaxValidators(ctx) - res.BondDenom = k.BondDenom(ctx) - return +func (k Keeper) GetParams(ctx sdk.Context) types.Params { + return types.NewParams( + k.UnbondingTime(ctx), + k.MaxValidators(ctx), + k.MaxEntries(ctx), + k.BondDenom(ctx), + ) } // set the params diff --git a/x/staking/types/errors.go b/x/staking/types/errors.go index 2ed4a2411..337c9d043 100644 --- a/x/staking/types/errors.go +++ b/x/staking/types/errors.go @@ -149,8 +149,9 @@ func ErrNoUnbondingDelegation(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "no unbonding delegation found") } -func ErrExistingUnbondingDelegation(codespace sdk.CodespaceType) sdk.Error { - return sdk.NewError(codespace, CodeInvalidDelegation, "existing unbonding delegation found") +func ErrMaxUnbondingDelegationEntries(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, + "too many unbonding delegation entries in this delegator/validator duo, please wait for some entries to mature") } func ErrBadRedelegationAddr(codespace sdk.CodespaceType) sdk.Error { @@ -178,6 +179,11 @@ func ErrTransitiveRedelegation(codespace sdk.CodespaceType) sdk.Error { "redelegation to this validator already in progress, first redelegation to this validator must complete before next redelegation") } +func ErrMaxRedelegationEntries(codespace sdk.CodespaceType) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDelegation, + "too many redelegation entries in this delegator/src-validator/dst-validator trio, please wait for some entries to mature") +} + func ErrDelegatorShareExRateInvalid(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidDelegation, "cannot delegate to validators with invalid (zero) ex-rate") diff --git a/x/staking/types/params.go b/x/staking/types/params.go index 7120085e0..1f95b160f 100644 --- a/x/staking/types/params.go +++ b/x/staking/types/params.go @@ -28,6 +28,7 @@ const ( var ( KeyUnbondingTime = []byte("UnbondingTime") KeyMaxValidators = []byte("MaxValidators") + KeyMaxEntries = []byte("KeyMaxEntries") KeyBondDenom = []byte("BondDenom") ) @@ -37,14 +38,27 @@ var _ params.ParamSet = (*Params)(nil) type Params struct { UnbondingTime time.Duration `json:"unbonding_time"` // time duration of unbonding MaxValidators uint16 `json:"max_validators"` // maximum number of validators + MaxEntries uint16 `json:"max_entries"` // max entries for either unbonding delegation or redelegation (per pair/trio) BondDenom string `json:"bond_denom"` // bondable coin denomination } +func NewParams(unbondingTime time.Duration, maxValidators, maxEntries uint16, + bondDenom string) Params { + + return Params{ + UnbondingTime: unbondingTime, + MaxValidators: maxValidators, + MaxEntries: maxEntries, + BondDenom: bondDenom, + } +} + // Implements params.ParamSet func (p *Params) KeyValuePairs() params.KeyValuePairs { return params.KeyValuePairs{ {KeyUnbondingTime, &p.UnbondingTime}, {KeyMaxValidators, &p.MaxValidators}, + {KeyMaxEntries, &p.MaxEntries}, {KeyBondDenom, &p.BondDenom}, } } @@ -58,11 +72,7 @@ func (p Params) Equal(p2 Params) bool { // DefaultParams returns a default set of parameters. func DefaultParams() Params { - return Params{ - UnbondingTime: defaultUnbondingTime, - MaxValidators: 100, - BondDenom: DefaultBondDenom, - } + return NewParams(defaultUnbondingTime, 100, 7, DefaultBondDenom) } // String returns a human readable string representation of the parameters. @@ -70,8 +80,9 @@ func (p Params) String() string { return fmt.Sprintf(`Params: Unbonding Time: %s) Max Validators: %d) + Max Entries: %d) Bonded Coin Denomination: %s`, p.UnbondingTime, - p.MaxValidators, p.BondDenom) + p.MaxValidators, p.MaxEntries, p.BondDenom) } // unmarshal the current staking params value from store key or panic From 6b02e08160e8c40f52922e7a20f39ab136b52d36 Mon Sep 17 00:00:00 2001 From: frog power 4000 Date: Fri, 25 Jan 2019 06:12:04 -0500 Subject: [PATCH 02/99] Merge PR #3394: Update staking string types --- x/staking/types/params.go | 8 ++++---- x/staking/types/pool.go | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/x/staking/types/params.go b/x/staking/types/params.go index 1f95b160f..9a7ca2748 100644 --- a/x/staking/types/params.go +++ b/x/staking/types/params.go @@ -78,10 +78,10 @@ func DefaultParams() Params { // String returns a human readable string representation of the parameters. func (p Params) String() string { return fmt.Sprintf(`Params: - Unbonding Time: %s) - Max Validators: %d) - Max Entries: %d) - Bonded Coin Denomination: %s`, p.UnbondingTime, + Unbonding Time: %s + Max Validators: %d + Max Entries: %d + Bonded Coin Denom: %s`, p.UnbondingTime, p.MaxValidators, p.MaxEntries, p.BondDenom) } diff --git a/x/staking/types/pool.go b/x/staking/types/pool.go index 3f5628520..840e4f609 100644 --- a/x/staking/types/pool.go +++ b/x/staking/types/pool.go @@ -71,10 +71,10 @@ func (p Pool) bondedTokensToNotBonded(bondedTokens sdk.Int) Pool { // String returns a human readable string representation of a pool. func (p Pool) String() string { return fmt.Sprintf(`Pool: - Loose Tokens: %s + Loose Tokens: %s Bonded Tokens: %s - Token Supply: %s - Bonded Ratio: %v`, p.NotBondedTokens, + Token Supply: %s + Bonded Ratio: %v`, p.NotBondedTokens, p.BondedTokens, p.TokenSupply(), p.BondedRatio()) } From afb04b117e29b80ad9ac54c827fa3eae3d0973d0 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Fri, 25 Jan 2019 19:13:17 +0800 Subject: [PATCH 03/99] Merge PR #3387: Add missing iterator close --- store/list.go | 3 +-- x/gov/genesis.go | 2 ++ x/gov/keeper.go | 8 ++------ x/gov/querier.go | 2 ++ x/staking/keeper/sdk_types.go | 6 +++--- x/staking/keeper/test_common.go | 1 + x/staking/keeper/val_state_change.go | 2 ++ 7 files changed, 13 insertions(+), 11 deletions(-) diff --git a/store/list.go b/store/list.go index b38f11b80..542a77bf9 100644 --- a/store/list.go +++ b/store/list.go @@ -84,6 +84,7 @@ func (m List) Push(value interface{}) { // CONTRACT: No writes may happen within a domain while iterating over it. func (m List) Iterate(ptr interface{}, fn func(uint64) bool) { iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01}) + defer iter.Close() for ; iter.Valid(); iter.Next() { v := iter.Value() m.cdc.MustUnmarshalBinaryLengthPrefixed(v, ptr) @@ -100,8 +101,6 @@ func (m List) Iterate(ptr interface{}, fn func(uint64) bool) { break } } - - iter.Close() } func subspace(prefix []byte) (start, end []byte) { diff --git a/x/gov/genesis.go b/x/gov/genesis.go index e06d79b21..9c44b2f00 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -184,12 +184,14 @@ func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState { for _, proposal := range proposals { proposalID := proposal.GetProposalID() depositsIterator := k.GetDeposits(ctx, proposalID) + defer depositsIterator.Close() for ; depositsIterator.Valid(); depositsIterator.Next() { var deposit Deposit k.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) deposits = append(deposits, DepositWithMetadata{proposalID, deposit}) } votesIterator := k.GetVotes(ctx, proposalID) + defer votesIterator.Close() for ; votesIterator.Valid(); votesIterator.Next() { var vote Vote k.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 19fd65734..f59cdfd15 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -422,7 +422,7 @@ func (keeper Keeper) GetDeposits(ctx sdk.Context, proposalID uint64) sdk.Iterato func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) depositsIterator := keeper.GetDeposits(ctx, proposalID) - + defer depositsIterator.Close() for ; depositsIterator.Valid(); depositsIterator.Next() { deposit := &Deposit{} keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit) @@ -434,15 +434,13 @@ func (keeper Keeper) RefundDeposits(ctx sdk.Context, proposalID uint64) { store.Delete(depositsIterator.Key()) } - - depositsIterator.Close() } // Deletes all the deposits on a specific proposal without refunding them func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) depositsIterator := keeper.GetDeposits(ctx, proposalID) - + defer depositsIterator.Close() for ; depositsIterator.Valid(); depositsIterator.Next() { deposit := &Deposit{} keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), deposit) @@ -454,8 +452,6 @@ func (keeper Keeper) DeleteDeposits(ctx sdk.Context, proposalID uint64) { store.Delete(depositsIterator.Key()) } - - depositsIterator.Close() } // ===================================================== diff --git a/x/gov/querier.go b/x/gov/querier.go index bc4eb0d2b..b9f0fbf7f 100644 --- a/x/gov/querier.go +++ b/x/gov/querier.go @@ -181,6 +181,7 @@ func queryDeposits(ctx sdk.Context, path []string, req abci.RequestQuery, keeper var deposits []Deposit depositsIterator := keeper.GetDeposits(ctx, params.ProposalID) + defer depositsIterator.Close() for ; depositsIterator.Valid(); depositsIterator.Next() { deposit := Deposit{} keeper.cdc.MustUnmarshalBinaryLengthPrefixed(depositsIterator.Value(), &deposit) @@ -238,6 +239,7 @@ func queryVotes(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke var votes []Vote votesIterator := keeper.GetVotes(ctx, params.ProposalID) + defer votesIterator.Close() for ; votesIterator.Valid(); votesIterator.Next() { vote := Vote{} keeper.cdc.MustUnmarshalBinaryLengthPrefixed(votesIterator.Value(), &vote) diff --git a/x/staking/keeper/sdk_types.go b/x/staking/keeper/sdk_types.go index 7e610360a..8c5bad161 100644 --- a/x/staking/keeper/sdk_types.go +++ b/x/staking/keeper/sdk_types.go @@ -14,6 +14,7 @@ var _ sdk.ValidatorSet = Keeper{} func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, ValidatorsKey) + defer iterator.Close() i := int64(0) for ; iterator.Valid(); iterator.Next() { validator := types.MustUnmarshalValidator(k.cdc, iterator.Value()) @@ -23,7 +24,6 @@ func (k Keeper) IterateValidators(ctx sdk.Context, fn func(index int64, validato } i++ } - iterator.Close() } // iterate through the bonded validator set and perform the provided function @@ -52,6 +52,7 @@ func (k Keeper) IterateBondedValidatorsByPower(ctx sdk.Context, fn func(index in // iterate through the active validator set and perform the provided function func (k Keeper) IterateLastValidators(ctx sdk.Context, fn func(index int64, validator sdk.Validator) (stop bool)) { iterator := k.LastValidatorsIterator(ctx) + defer iterator.Close() i := int64(0) for ; iterator.Valid(); iterator.Next() { address := AddressFromLastValidatorPowerKey(iterator.Key()) @@ -66,7 +67,6 @@ func (k Keeper) IterateLastValidators(ctx sdk.Context, fn func(index int64, vali } i++ } - iterator.Close() } // get the sdk.validator for a particular address @@ -134,6 +134,7 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.AccAddress, store := ctx.KVStore(k.storeKey) delegatorPrefixKey := GetDelegationsKey(delAddr) iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + defer iterator.Close() for i := int64(0); iterator.Valid(); iterator.Next() { del := types.MustUnmarshalDelegation(k.cdc, iterator.Value()) stop := fn(i, del) @@ -142,5 +143,4 @@ func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.AccAddress, } i++ } - iterator.Close() } diff --git a/x/staking/keeper/test_common.go b/x/staking/keeper/test_common.go index 057b82a2a..b8c106bf8 100644 --- a/x/staking/keeper/test_common.go +++ b/x/staking/keeper/test_common.go @@ -215,6 +215,7 @@ func TestingUpdateValidator(keeper Keeper, ctx sdk.Context, validator types.Vali { // Remove any existing power key for validator. store := ctx.KVStore(keeper.storeKey) iterator := sdk.KVStorePrefixIterator(store, ValidatorsByPowerIndexKey) + defer iterator.Close() deleted := false for ; iterator.Valid(); iterator.Next() { valAddr := parseValidatorPowerRankKey(iterator.Key()) diff --git a/x/staking/keeper/val_state_change.go b/x/staking/keeper/val_state_change.go index 3ddb995fc..70ddb656b 100644 --- a/x/staking/keeper/val_state_change.go +++ b/x/staking/keeper/val_state_change.go @@ -36,6 +36,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // Iterate over validators, highest power to lowest. iterator := sdk.KVStoreReversePrefixIterator(store, ValidatorsByPowerIndexKey) + defer iterator.Close() count := 0 for ; iterator.Valid() && count < int(maxValidators); iterator.Next() { @@ -254,6 +255,7 @@ func (k Keeper) getLastValidatorsByAddr(ctx sdk.Context) validatorsByAddr { last := make(validatorsByAddr) store := ctx.KVStore(k.storeKey) iterator := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) + defer iterator.Close() for ; iterator.Valid(); iterator.Next() { var valAddr [sdk.AddrLen]byte copy(valAddr[:], iterator.Key()[1:]) From 9440c3c2728d900d2837916ae7d9758d6cd3dbc4 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Fri, 25 Jan 2019 03:13:56 -0800 Subject: [PATCH 04/99] Merge PR #3385: Docs Hotfix --- docs/.vuepress/config.js | 2 +- docs/gaia/ledger.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 94aaf953c..a007b9fe9 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -37,7 +37,7 @@ module.exports = { title: "Gaia", collapsable: false, children: [ - "/gaia/what-is-gaia" + "/gaia/what-is-gaia", "/gaia/installation", "/gaia/join-testnet", "/gaia/validators/validator-setup", diff --git a/docs/gaia/ledger.md b/docs/gaia/ledger.md index ce03c88b2..71c41603e 100644 --- a/docs/gaia/ledger.md +++ b/docs/gaia/ledger.md @@ -15,15 +15,15 @@ Once you have the Cosmos app installed on your Ledger, and the Ledger is accessible from the machine you are using `gaiacli` from you can create a new account key using the Ledger: ```bash -$ gaiacli keys add {{ .Key.Name }} --ledger +$ gaiacli keys add { .Key.Name } --ledger NAME: TYPE: ADDRESS: PUBKEY: -{{ .Key.Name }} ledger cosmos1aw64xxr80lwqqdk8u2xhlrkxqaxamkr3e2g943 cosmospub1addwnpepqvhs678gh9aqrjc2tg2vezw86csnvgzqq530ujkunt5tkuc7lhjkz5mj629 +{ .Key.Name } ledger cosmos1aw64xxr80lwqqdk8u2xhlrkxqaxamkr3e2g943 cosmospub1addwnpepqvhs678gh9aqrjc2tg2vezw86csnvgzqq530ujkunt5tkuc7lhjkz5mj629 ``` This key will only be accessible while the Ledger is plugged in and unlocked. To send some coins with this key, run the following: ```bash -$ gaiacli tx send --from {{ .Key.Name }} --to {{ .Destination.AccAddr }} --chain-id=gaia-7000 +$ gaiacli tx send --from { .Key.Name } --to { .Destination.AccAddr } --chain-id=gaia-7000 ``` You will be asked to review and confirm the transaction on the Ledger. Once you do this you should see the result in the console! Now you can use your Ledger to manage your Atoms and Stake! From 7988686e7584b81259660b2e40bd3211f8013ab2 Mon Sep 17 00:00:00 2001 From: gamarin2 Date: Mon, 28 Jan 2019 18:49:13 +0100 Subject: [PATCH 05/99] Update docs get_vendor_deps -> vendor-deps (#3402) --- Makefile | 2 +- docs/gaia/installation.md | 2 +- docs/gaia/join-testnet.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 2f8086fd7..de0666dd6 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ GOTOOLS = \ github.com/rakyll/statik GOBIN ?= $(GOPATH)/bin -all: devtools get_vendor_deps install test_lint test +all: devtools install test_lint test # The below include contains the tools target. include scripts/Makefile diff --git a/docs/gaia/installation.md b/docs/gaia/installation.md index 59d6d35a7..de48bf27d 100644 --- a/docs/gaia/installation.md +++ b/docs/gaia/installation.md @@ -28,7 +28,7 @@ mkdir -p $GOPATH/src/github.com/cosmos cd $GOPATH/src/github.com/cosmos git clone https://github.com/cosmos/cosmos-sdk cd cosmos-sdk && git checkout master -make tools && make get_vendor_deps && make install +make tools install ``` > *NOTE*: If you have issues at this step, please check that you have the latest stable version of GO installed. diff --git a/docs/gaia/join-testnet.md b/docs/gaia/join-testnet.md index 2cb2fedf8..91b3afef6 100644 --- a/docs/gaia/join-testnet.md +++ b/docs/gaia/join-testnet.md @@ -76,7 +76,7 @@ Now it is time to upgrade the software: ```bash cd $GOPATH/src/github.com/cosmos/cosmos-sdk git fetch --all && git checkout master -make update_tools && make get_vendor_deps && make install +make update_tools install ``` ::: tip From f494a92f928b985f305b6b6f8e403442fe39c482 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 28 Jan 2019 09:54:56 -0800 Subject: [PATCH 06/99] Implement GenesisState.Sanitize() (#3397) --- PENDING.md | 7 +++--- cmd/gaia/app/app.go | 8 ++----- cmd/gaia/app/genesis.go | 11 +++++++++ cmd/gaia/app/genesis_test.go | 39 ++++++++++++++++++++++++++++--- cmd/gaia/cli_test/cli_test.go | 39 ++++++++++++++++++++++++++++--- cmd/gaia/cli_test/test_helpers.go | 17 ++++++++++++++ cmd/gaia/init/collect.go | 2 +- cmd/gaia/init/genesis_accts.go | 16 ++++++++----- cmd/gaia/init/gentx.go | 2 +- cmd/gaia/init/testnet.go | 2 +- cmd/gaia/init/utils.go | 3 ++- 11 files changed, 121 insertions(+), 25 deletions(-) diff --git a/PENDING.md b/PENDING.md index 0a01c3eb6..3f5a4146c 100644 --- a/PENDING.md +++ b/PENDING.md @@ -15,11 +15,12 @@ BREAKING CHANGES FEATURES -* Gaia REST API (`gaiacli advanced rest-server`) +* Gaia REST API * Gaia CLI (`gaiacli`) * Gaia + - [\#3397](https://github.com/cosmos/cosmos-sdk/pull/3397) Implement genesis file sanitization to avoid failures at chain init. * SDK * \#3270 [x/staking] limit number of ongoing unbonding delegations /redelegations per pair/trio @@ -29,7 +30,7 @@ FEATURES IMPROVEMENTS -* Gaia REST API (`gaiacli advanced rest-server`) +* Gaia REST API * Gaia CLI (`gaiacli`) @@ -42,7 +43,7 @@ IMPROVEMENTS BUG FIXES -* Gaia REST API (`gaiacli advanced rest-server`) +* Gaia REST API * Gaia CLI (`gaiacli`) diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 8b5ce4e73..e99d0b808 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -228,10 +228,7 @@ func (app *GaiaApp) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.R // initialize store from a genesis state func (app *GaiaApp) initFromGenesisState(ctx sdk.Context, genesisState GenesisState) []abci.ValidatorUpdate { - // sort by account number to maintain consistency - sort.Slice(genesisState.Accounts, func(i, j int) bool { - return genesisState.Accounts[i].AccountNumber < genesisState.Accounts[j].AccountNumber - }) + genesisState.Sanitize() // load the accounts for _, gacc := range genesisState.Accounts { @@ -256,8 +253,7 @@ func (app *GaiaApp) initFromGenesisState(ctx sdk.Context, genesisState GenesisSt mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData) // validate genesis state - err = GaiaValidateGenesisState(genesisState) - if err != nil { + if err := GaiaValidateGenesisState(genesisState); err != nil { panic(err) // TODO find a way to do this w/o panics } diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index cfda1c6c4..b0010323b 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -58,6 +58,17 @@ func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, } } +// Sanitize sorts accounts and coin sets. +func (gs GenesisState) Sanitize() { + sort.Slice(gs.Accounts, func(i, j int) bool { + return gs.Accounts[i].AccountNumber < gs.Accounts[j].AccountNumber + }) + + for _, acc := range gs.Accounts { + acc.Coins = acc.Coins.Sort() + } +} + // GenesisAccount defines an account initialized at genesis. type GenesisAccount struct { Address sdk.AccAddress `json:"address"` diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go index 1383794a3..be0c9dba4 100644 --- a/cmd/gaia/app/genesis_test.go +++ b/cmd/gaia/app/genesis_test.go @@ -5,12 +5,12 @@ import ( "testing" "time" - "github.com/tendermint/tendermint/crypto/secp256k1" - tmtypes "github.com/tendermint/tendermint/types" - "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/crypto/secp256k1" + tmtypes "github.com/tendermint/tendermint/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -142,3 +142,36 @@ func TestNewDefaultGenesisAccount(t *testing.T) { require.Equal(t, sdk.NewInt(1000), acc.Coins.AmountOf("footoken")) require.Equal(t, sdk.NewInt(150), acc.Coins.AmountOf(bondDenom)) } + +func TestGenesisStateSanitize(t *testing.T) { + genesisState := makeGenesisState(t, nil) + require.Nil(t, GaiaValidateGenesisState(genesisState)) + + addr1 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) + authAcc1 := auth.NewBaseAccountWithAddress(addr1) + authAcc1.SetCoins(sdk.Coins{ + sdk.NewInt64Coin("bcoin", 150), + sdk.NewInt64Coin("acoin", 150), + }) + authAcc1.SetAccountNumber(1) + genAcc1 := NewGenesisAccount(&authAcc1) + + addr2 := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) + authAcc2 := auth.NewBaseAccountWithAddress(addr2) + authAcc2.SetCoins(sdk.Coins{ + sdk.NewInt64Coin("acoin", 150), + sdk.NewInt64Coin("bcoin", 150), + }) + genAcc2 := NewGenesisAccount(&authAcc2) + + genesisState.Accounts = []GenesisAccount{genAcc1, genAcc2} + require.True(t, genesisState.Accounts[0].AccountNumber > genesisState.Accounts[1].AccountNumber) + require.Equal(t, genesisState.Accounts[0].Coins[0].Denom, "bcoin") + require.Equal(t, genesisState.Accounts[0].Coins[1].Denom, "acoin") + require.Equal(t, genesisState.Accounts[1].Address, addr2) + genesisState.Sanitize() + require.False(t, genesisState.Accounts[0].AccountNumber > genesisState.Accounts[1].AccountNumber) + require.Equal(t, genesisState.Accounts[1].Address, addr1) + require.Equal(t, genesisState.Accounts[1].Coins[0].Denom, "acoin") + require.Equal(t, genesisState.Accounts[1].Coins[1].Denom, "bcoin") +} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 2d6864ec9..1d1655602 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -909,10 +909,7 @@ func TestGaiadCollectGentxs(t *testing.T) { f.UnsafeResetAll() // Initialize keys - f.KeysDelete(keyFoo) - f.KeysDelete(keyBar) f.KeysAdd(keyFoo) - f.KeysAdd(keyBar) // Configure json output f.CLIConfig("output", "json") @@ -932,6 +929,42 @@ func TestGaiadCollectGentxs(t *testing.T) { f.Cleanup(gentxDir) } +func TestGaiadAddGenesisAccount(t *testing.T) { + t.Parallel() + f := NewFixtures(t) + + // Reset testing path + f.UnsafeResetAll() + + // Initialize keys + f.KeysDelete(keyFoo) + f.KeysDelete(keyBar) + f.KeysDelete(keyBaz) + f.KeysAdd(keyFoo) + f.KeysAdd(keyBar) + f.KeysAdd(keyBaz) + + // Configure json output + f.CLIConfig("output", "json") + + // Run init + f.GDInit(keyFoo) + + // Add account to genesis.json + bazCoins := sdk.Coins{ + sdk.NewInt64Coin("acoin", 1000000), + sdk.NewInt64Coin("bcoin", 1000000), + } + + f.AddGenesisAccount(f.KeyAddress(keyFoo), startCoins) + f.AddGenesisAccount(f.KeyAddress(keyBar), bazCoins) + genesisState := f.GenesisState() + require.Equal(t, genesisState.Accounts[0].Address, f.KeyAddress(keyFoo)) + require.Equal(t, genesisState.Accounts[1].Address, f.KeyAddress(keyBar)) + require.True(t, genesisState.Accounts[0].Coins.IsEqual(startCoins)) + require.True(t, genesisState.Accounts[1].Coins.IsEqual(bazCoins)) +} + func TestSlashingGetParams(t *testing.T) { t.Parallel() f := InitFixtures(t) diff --git a/cmd/gaia/cli_test/test_helpers.go b/cmd/gaia/cli_test/test_helpers.go index 757198506..54f592f10 100644 --- a/cmd/gaia/cli_test/test_helpers.go +++ b/cmd/gaia/cli_test/test_helpers.go @@ -16,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + appInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/tests" @@ -76,6 +77,22 @@ func NewFixtures(t *testing.T) *Fixtures { } } +// GenesisFile returns the path of the genesis file +func (f Fixtures) GenesisFile() string { + return filepath.Join(f.GDHome, "config", "genesis.json") +} + +// GenesisFile returns the application's genesis state +func (f Fixtures) GenesisState() app.GenesisState { + cdc := codec.New() + genDoc, err := appInit.LoadGenesisDoc(cdc, f.GenesisFile()) + require.NoError(f.T, err) + + var appState app.GenesisState + require.NoError(f.T, cdc.UnmarshalJSON(genDoc.AppState, &appState)) + return appState +} + // InitFixtures is called at the beginning of a test // and initializes a chain with 1 validator func InitFixtures(t *testing.T) (f *Fixtures) { diff --git a/cmd/gaia/init/collect.go b/cmd/gaia/init/collect.go index e630d494c..d0842df2c 100644 --- a/cmd/gaia/init/collect.go +++ b/cmd/gaia/init/collect.go @@ -44,7 +44,7 @@ func CollectGenTxsCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { return err } - genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) + genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile()) if err != nil { return err } diff --git a/cmd/gaia/init/genesis_accts.go b/cmd/gaia/init/genesis_accts.go index bb9c4af0c..8b580584f 100644 --- a/cmd/gaia/init/genesis_accts.go +++ b/cmd/gaia/init/genesis_accts.go @@ -1,7 +1,6 @@ package init import ( - "encoding/json" "fmt" "github.com/spf13/cobra" @@ -49,7 +48,7 @@ func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command if !common.FileExists(genFile) { return fmt.Errorf("%s does not exist, run `gaiad init` first", genFile) } - genDoc, err := loadGenesisDoc(cdc, genFile) + genDoc, err := LoadGenesisDoc(cdc, genFile) if err != nil { return err } @@ -59,7 +58,12 @@ func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command return err } - appStateJSON, err := addGenesisAccount(cdc, appState, addr, coins) + appState, err = addGenesisAccount(cdc, appState, addr, coins) + if err != nil { + return err + } + + appStateJSON, err := cdc.MarshalJSON(appState) if err != nil { return err } @@ -73,15 +77,15 @@ func AddGenesisAccountCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command return cmd } -func addGenesisAccount(cdc *codec.Codec, appState app.GenesisState, addr sdk.AccAddress, coins sdk.Coins) (json.RawMessage, error) { +func addGenesisAccount(cdc *codec.Codec, appState app.GenesisState, addr sdk.AccAddress, coins sdk.Coins) (app.GenesisState, error) { for _, stateAcc := range appState.Accounts { if stateAcc.Address.Equals(addr) { - return nil, fmt.Errorf("the application state already contains account %v", addr) + return appState, fmt.Errorf("the application state already contains account %v", addr) } } acc := auth.NewBaseAccountWithAddress(addr) acc.Coins = coins appState.Accounts = append(appState.Accounts, app.NewGenesisAccount(&acc)) - return cdc.MarshalJSON(appState) + return appState, nil } diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 25de93aab..142a9801e 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -66,7 +66,7 @@ following delegation and commission default parameters: return err } - genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) + genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile()) if err != nil { return err } diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index 111a58032..c69384a2d 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -295,7 +295,7 @@ func collectGenFiles( nodeID, valPubKey := nodeIDs[i], valPubKeys[i] initCfg := newInitConfig(chainID, gentxsDir, moniker, nodeID, valPubKey) - genDoc, err := loadGenesisDoc(cdc, config.GenesisFile()) + genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile()) if err != nil { return err } diff --git a/cmd/gaia/init/utils.go b/cmd/gaia/init/utils.go index 149b4d10e..4e2f2f26f 100644 --- a/cmd/gaia/init/utils.go +++ b/cmd/gaia/init/utils.go @@ -88,7 +88,8 @@ func InitializeNodeValidatorFiles( return nodeID, valPubKey, nil } -func loadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) { +// LoadGenesisDoc reads and unmarshals GenesisDoc from the given file. +func LoadGenesisDoc(cdc *amino.Codec, genFile string) (genDoc types.GenesisDoc, err error) { genContents, err := ioutil.ReadFile(genFile) if err != nil { return genDoc, err From 818fd1648194225ba00405dce63f8e72afd6f82d Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 28 Jan 2019 11:37:05 -0800 Subject: [PATCH 07/99] Fixes #3393 --- x/slashing/client/cli/query.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/x/slashing/client/cli/query.go b/x/slashing/client/cli/query.go index fa5f882cc..5354e6e23 100644 --- a/x/slashing/client/cli/query.go +++ b/x/slashing/client/cli/query.go @@ -25,15 +25,20 @@ func GetCmdQuerySigningInfo(storeName string, cdc *codec.Codec) *cobra.Command { return err } - key := slashing.GetValidatorSigningInfoKey(sdk.ConsAddress(pk.Address())) + consAddr := sdk.ConsAddress(pk.Address()) + key := slashing.GetValidatorSigningInfoKey(consAddr) res, err := cliCtx.QueryStore(key, storeName) if err != nil { return err } + if len(res) == 0 { + return fmt.Errorf("Validator %s not found in slashing store", consAddr) + } + var signingInfo slashing.ValidatorSigningInfo - cdc.MustUnmarshalBinaryLengthPrefixed(res, signingInfo) + cdc.MustUnmarshalBinaryLengthPrefixed(res, &signingInfo) return cliCtx.PrintOutput(signingInfo) }, } From df86585f6b5e20ff1c800cbea3a6cf6aa9e760b7 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 28 Jan 2019 11:46:47 -0800 Subject: [PATCH 08/99] Add validate-genesis command (#3399) --- PENDING.md | 1 + cmd/gaia/cli_test/cli_test.go | 11 +++++++ cmd/gaia/cli_test/test_helpers.go | 6 ++++ cmd/gaia/cmd/gaiad/main.go | 1 + cmd/gaia/init/validate_genesis.go | 52 +++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+) create mode 100644 cmd/gaia/init/validate_genesis.go diff --git a/PENDING.md b/PENDING.md index 3f5a4146c..aaa663f10 100644 --- a/PENDING.md +++ b/PENDING.md @@ -5,6 +5,7 @@ BREAKING CHANGES * Gaia REST API (`gaiacli advanced rest-server`) * Gaia CLI (`gaiacli`) + - [#3399](https://github.com/cosmos/cosmos-sdk/pull/3399) Add `gaiad validate-genesis` command to facilitate checking of genesis files * Gaia diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 1d1655602..51d26833f 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -978,3 +978,14 @@ func TestSlashingGetParams(t *testing.T) { require.Equal(t, int64(100), params.SignedBlocksWindow) require.Equal(t, sdk.NewDecWithPrec(5, 1), params.MinSignedPerWindow) } + +func TestValidateGenesis(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + // start gaiad server + proc := f.GDStart() + defer proc.Stop(false) + + f.ValidateGenesis() +} diff --git a/cmd/gaia/cli_test/test_helpers.go b/cmd/gaia/cli_test/test_helpers.go index 54f592f10..1be900475 100644 --- a/cmd/gaia/cli_test/test_helpers.go +++ b/cmd/gaia/cli_test/test_helpers.go @@ -196,6 +196,12 @@ func (f *Fixtures) GDStart(flags ...string) *tests.Process { return proc } +// ValidateGenesis runs gaiad validate-genesi +func (f *Fixtures) ValidateGenesis() { + cmd := fmt.Sprintf("gaiad validate-genesis --home=%s", f.GDHome) + executeWriteCheckErr(f.T, cmd) +} + //___________________________________________________________________________________ // gaiacli keys diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index 4014bef73..0d6e8a92f 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -43,6 +43,7 @@ func main() { rootCmd.AddCommand(gaiaInit.TestnetFilesCmd(ctx, cdc)) rootCmd.AddCommand(gaiaInit.GenTxCmd(ctx, cdc)) rootCmd.AddCommand(gaiaInit.AddGenesisAccountCmd(ctx, cdc)) + rootCmd.AddCommand(gaiaInit.ValidateGenesisCmd(ctx, cdc)) rootCmd.AddCommand(client.NewCompletionCmd(rootCmd, true)) server.AddCommands(ctx, cdc, rootCmd, newApp, exportAppStateAndTMValidators) diff --git a/cmd/gaia/init/validate_genesis.go b/cmd/gaia/init/validate_genesis.go new file mode 100644 index 000000000..ed0f6e0a0 --- /dev/null +++ b/cmd/gaia/init/validate_genesis.go @@ -0,0 +1,52 @@ +package init + +import ( + "fmt" + "os" + + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/server" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/tendermint/tendermint/types" +) + +// Validate genesis command takes +func ValidateGenesisCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { + return &cobra.Command{ + Use: "validate-genesis [file]", + Args: cobra.RangeArgs(0, 1), + Short: "validates the genesis file at the default location or at the location passed as an arg", + RunE: func(cmd *cobra.Command, args []string) (err error) { + + // Load default if passed no args, otherwise load passed file + var genesis string + if len(args) == 0 { + genesis = ctx.Config.GenesisFile() + } else { + genesis = args[0] + } + + //nolint + fmt.Fprintf(os.Stderr, "validating genesis file at %s\n", genesis) + + var genDoc types.GenesisDoc + if genDoc, err = loadGenesisDoc(cdc, genesis); err != nil { + return errors.Errorf("Error loading genesis doc from %s: %s", genesis, err.Error()) + } + + var genstate app.GenesisState + if err = cdc.UnmarshalJSON(genDoc.AppState, &genstate); err != nil { + return errors.Errorf("Error unmarshaling genesis doc %s: %s", genesis, err.Error()) + } + + if err = app.GaiaValidateGenesisState(genstate); err != nil { + return errors.Errorf("Error validating genesis file %s: %s", genesis, err.Error()) + } + + fmt.Printf("File at %s is a valid genesis file for gaiad\n", genesis) + return nil + }, + } +} From 9e21ca25ced2787dd5220582eaf2dbbbbff19a03 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Mon, 28 Jan 2019 14:50:27 -0500 Subject: [PATCH 09/99] Remove Pubkey field from BaseAccount#String (#3407) --- x/auth/account.go | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/x/auth/account.go b/x/auth/account.go index e67e081ae..2bf2fce0c 100644 --- a/x/auth/account.go +++ b/x/auth/account.go @@ -83,12 +83,20 @@ type BaseAccount struct { // String implements fmt.Stringer func (acc BaseAccount) String() string { - return fmt.Sprintf(`Account %s: + var pubkey string + + if acc.PubKey != nil { + pubkey = sdk.MustBech32ifyAccPub(acc.PubKey) + } + + return fmt.Sprintf(`Account: + Address: %s + Pubkey: %s Coins: %s - PubKey: %s AccountNumber: %d - Sequence: %d`, acc.Address, acc.Coins, - acc.PubKey.Address(), acc.AccountNumber, acc.Sequence) + Sequence: %d`, + acc.Address, pubkey, acc.Coins, acc.AccountNumber, acc.Sequence, + ) } // Prototype function for BaseAccount @@ -183,8 +191,15 @@ type BaseVestingAccount struct { // String implements fmt.Stringer func (bva BaseVestingAccount) String() string { + var pubkey string + + if bva.PubKey != nil { + pubkey = sdk.MustBech32ifyAccPub(bva.PubKey) + } + return fmt.Sprintf(`Vesting Account: - Address: %s + Address: %s + Pubkey: %s Coins: %s AccountNumber: %d Sequence: %d @@ -192,10 +207,8 @@ func (bva BaseVestingAccount) String() string { DelegatedFree: %s DelegatedVesting: %s EndTime: %d `, - bva.Address, bva.Coins, - bva.AccountNumber, bva.Sequence, - bva.OriginalVesting, bva.DelegatedFree, - bva.DelegatedVesting, bva.EndTime, + bva.Address, pubkey, bva.Coins, bva.AccountNumber, bva.Sequence, + bva.OriginalVesting, bva.DelegatedFree, bva.DelegatedVesting, bva.EndTime, ) } @@ -395,8 +408,15 @@ func NewContinuousVestingAccount( } func (cva ContinuousVestingAccount) String() string { + var pubkey string + + if cva.PubKey != nil { + pubkey = sdk.MustBech32ifyAccPub(cva.PubKey) + } + return fmt.Sprintf(`Continuous Vesting Account: - Address: %s + Address: %s + Pubkey: %s Coins: %s AccountNumber: %d Sequence: %d @@ -405,10 +425,9 @@ func (cva ContinuousVestingAccount) String() string { DelegatedVesting: %s StartTime: %d EndTime: %d `, - cva.Address, cva.Coins, - cva.AccountNumber, cva.Sequence, - cva.OriginalVesting, cva.DelegatedFree, - cva.DelegatedVesting, cva.StartTime, cva.EndTime, + cva.Address, pubkey, cva.Coins, cva.AccountNumber, cva.Sequence, + cva.OriginalVesting, cva.DelegatedFree, cva.DelegatedVesting, + cva.StartTime, cva.EndTime, ) } From 75c24fda1cedd4d49e989f8b8f432d41a3316752 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 28 Jan 2019 12:40:08 -0800 Subject: [PATCH 10/99] Add test/pending --- PENDING.md | 1 + cmd/gaia/cli_test/cli_test.go | 4 ++++ cmd/gaia/cli_test/test_helpers.go | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/PENDING.md b/PENDING.md index 3f5a4146c..bcb7fcd15 100644 --- a/PENDING.md +++ b/PENDING.md @@ -46,6 +46,7 @@ BUG FIXES * Gaia REST API * Gaia CLI (`gaiacli`) + - [\#3417](https://github.com/cosmos/cosmos-sdk/pull/3417) Fix `q slashing signing-info` panic by ensuring safety of user input and properly returning not found error * Gaia diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 1d1655602..092e09ec5 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -977,4 +977,8 @@ func TestSlashingGetParams(t *testing.T) { require.Equal(t, time.Duration(120000000000), params.MaxEvidenceAge) require.Equal(t, int64(100), params.SignedBlocksWindow) require.Equal(t, sdk.NewDecWithPrec(5, 1), params.MinSignedPerWindow) + + sinfo := f.QuerySigningInfo(f.GDTendermint("show-validator")) + require.Equal(t, int64(0), sinfo.StartHeight) + require.False(t, sinfo.Tombstoned) } diff --git a/cmd/gaia/cli_test/test_helpers.go b/cmd/gaia/cli_test/test_helpers.go index 54f592f10..2d60ff292 100644 --- a/cmd/gaia/cli_test/test_helpers.go +++ b/cmd/gaia/cli_test/test_helpers.go @@ -196,6 +196,15 @@ func (f *Fixtures) GDStart(flags ...string) *tests.Process { return proc } +// GDTendermint returns the results of gaiad tendermint [query] +func (f *Fixtures) GDTendermint(query string) string { + cmd := fmt.Sprintf("gaiad tendermint %s --home=%s", query, f.GDHome) + success, stdout, stderr := executeWriteRetStdStreams(f.T, cmd) + require.Empty(f.T, stderr) + require.True(f.T, success) + return strings.TrimSpace(stdout) +} + //___________________________________________________________________________________ // gaiacli keys @@ -515,6 +524,18 @@ func (f *Fixtures) QueryGovDeposits(propsalID int, flags ...string) []gov.Deposi //___________________________________________________________________________________ // query slashing +// QuerySigningInfo returns the signing info for a validator +func (f *Fixtures) QuerySigningInfo(val string) slashing.ValidatorSigningInfo { + cmd := fmt.Sprintf("gaiacli query slashing signing-info %s %s", val, f.Flags()) + res, errStr := tests.ExecuteT(f.T, cmd, "") + require.Empty(f.T, errStr) + cdc := app.MakeCodec() + var sinfo slashing.ValidatorSigningInfo + err := cdc.UnmarshalJSON([]byte(res), &sinfo) + require.NoError(f.T, err) + return sinfo +} + // QuerySlashingParams is gaiacli query slashing params func (f *Fixtures) QuerySlashingParams() slashing.Params { cmd := fmt.Sprintf("gaiacli query slashing params %s", f.Flags()) From bb7690323016c26cca633ae14813fef45a3e83eb Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 28 Jan 2019 15:41:28 -0500 Subject: [PATCH 11/99] fix docs navbar RPC link (#3414) --- .github/CODEOWNERS | 4 +++- docs/.vuepress/config.js | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dcdf7080b..11f9e598b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,4 +4,6 @@ * @ebuchman @rigelrozanski @cwgoes # Precious documentation -/docs/ @zramsay @jolesbi +/docs/README.md @zramsay +/docs/DOCS_README.md @zramsay +/docs/.vuepress/ @zramsay diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index a007b9fe9..cb5f8cd43 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -21,7 +21,7 @@ module.exports = { }, nav: [ { text: "Back to Cosmos", link: "https://cosmos.network" }, - { text: "RPC", link: "../rpc/" } + { text: "RPC", link: "https://cosmos.network/rpc/" } ], sidebar: [ { From 17f67952dab6d6743291da317decabf5a05301ec Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 28 Jan 2019 13:08:42 -0800 Subject: [PATCH 12/99] loadGenesisDoc -> LoadGenesisDoc --- cmd/gaia/init/validate_genesis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/gaia/init/validate_genesis.go b/cmd/gaia/init/validate_genesis.go index ed0f6e0a0..5f5aae709 100644 --- a/cmd/gaia/init/validate_genesis.go +++ b/cmd/gaia/init/validate_genesis.go @@ -32,7 +32,7 @@ func ValidateGenesisCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { fmt.Fprintf(os.Stderr, "validating genesis file at %s\n", genesis) var genDoc types.GenesisDoc - if genDoc, err = loadGenesisDoc(cdc, genesis); err != nil { + if genDoc, err = LoadGenesisDoc(cdc, genesis); err != nil { return errors.Errorf("Error loading genesis doc from %s: %s", genesis, err.Error()) } From 57ae95716838232a690a638e3d2708f1b39d3ef5 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 28 Jan 2019 15:00:17 -0800 Subject: [PATCH 13/99] Snap Packaging #3313 --- Makefile | 17 ++++++++++++++-- snapcraft.yaml.in | 49 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 snapcraft.yaml.in diff --git a/Makefile b/Makefile index de0666dd6..a896678d7 100644 --- a/Makefile +++ b/Makefile @@ -122,7 +122,7 @@ draw_deps: tools @goviz -i github.com/cosmos/cosmos-sdk/cmd/gaia/cmd/gaiad -d 2 | dot -Tpng -o dependency-graph.png clean: - rm -f devtools-stamp vendor-deps + rm -f devtools-stamp vendor-deps snapcraft.yaml ######################################## ### Documentation @@ -235,6 +235,18 @@ localnet-start: localnet-stop localnet-stop: docker-compose down + +######################################## +### Packaging + +snapcraft.yaml: snapcraft.yaml.in + sed "s/@VERSION@/${VERSION}/g" < $< > $@ + +build-snap-edge: snapcraft.yaml + snapcraft clean + snapcraft + + # To avoid unintended conflicts with file names, always add to .PHONY # unless there is a reason not to. # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html @@ -243,4 +255,5 @@ check_tools check_dev_tools get_vendor_deps draw_deps test test_cli test_unit \ test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \ build-linux build-docker-gaiadnode localnet-start localnet-stop \ format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \ -test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools +test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools \ +build-snap-edge diff --git a/snapcraft.yaml.in b/snapcraft.yaml.in new file mode 100644 index 000000000..7a7878a3c --- /dev/null +++ b/snapcraft.yaml.in @@ -0,0 +1,49 @@ +name: gaia # you probably want to 'snapcraft register ' +# base: core18 # the base snap is the execution environment for this snap +version: '@VERSION@' # just for humans, typically '1.2+git' or '1.3.2' +summary: Gaia Daemon # 79 char long summary +description: | + This snap provides the Gaia daemon gaiad and the command line + tool gaiacli. +grade: devel # must be 'stable' to release into candidate/stable channels +confinement: strict # use 'strict' once you have the right plugs and slots + +apps: + gaiad: + command: bin/gaiad + plugs: [home,network,network-bind] + gaiacli: + command: bin/gaiacli + plugs: [home,network,network-bind] + +parts: + gaia: + plugin: dump + source: ./ + override-pull: | + rootdir=$(pwd) + gitroot=$(git rev-parse --show-toplevel) + cd ${gitroot} && git archive \ + -o ${rootdir}/gaia-@VERSION@.tar.gz \ + --format tar.gz -9 --prefix gaia-@VERSION@/ HEAD + cd ${rootdir} + tar xvf gaia-@VERSION@.tar.gz ; rm -f gaia-@VERSION@.tar.gz + mkdir -p go/src/github.com/cosmos bin + mv gaia-@VERSION@/ go/src/github.com/cosmos/cosmos-sdk/ + + # Use the following instructions to build a package from a release. + # wget https://github.com/cosmos/cosmos-sdk/archive/v@VERSION@.tar.gz + # tar xvf v@VERSION@.tar.gz + # rm v@VERSION@.tar.gz + + build-snaps: [go] + override-build: | + base=`pwd` + export GOPATH=`pwd`/go + cd $GOPATH/src/github.com/cosmos/cosmos-sdk + make tools + make vendor-deps + make install + mkdir $SNAPCRAFT_PART_INSTALL/bin + cp $GOPATH/bin/gaiad $SNAPCRAFT_PART_INSTALL/bin + cp $GOPATH/bin/gaiacli $SNAPCRAFT_PART_INSTALL/bin From 6de39e19f3c468276667f973e6f217ba8c55973b Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 28 Jan 2019 16:59:20 -0800 Subject: [PATCH 14/99] Merge PR #3419: Fix query distr slashes --- PENDING.md | 1 + x/distribution/client/cli/query.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/PENDING.md b/PENDING.md index a684ea882..ad915ada6 100644 --- a/PENDING.md +++ b/PENDING.md @@ -48,6 +48,7 @@ BUG FIXES * Gaia CLI (`gaiacli`) - [\#3417](https://github.com/cosmos/cosmos-sdk/pull/3417) Fix `q slashing signing-info` panic by ensuring safety of user input and properly returning not found error + - [\#3419](https://github.com/cosmos/cosmos-sdk/pull/3419) Fix `q distr slashes` panic * Gaia diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 09d4b458a..09b372278 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -143,7 +143,7 @@ func GetCmdQueryValidatorSlashes(queryRoute string, cdc *codec.Codec) *cobra.Com return err } - var slashes types.ValidatorSlashEvent + var slashes types.ValidatorSlashEvents cdc.MustUnmarshalJSON(res, &slashes) return cliCtx.PrintOutput(slashes) }, From f16cc31919302e4e4067b01bfe49910033cf7ead Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 28 Jan 2019 19:06:48 -0800 Subject: [PATCH 15/99] Merge PR #3403: Implement parameter-controlled bank/send switch-off --- Makefile | 2 +- cmd/gaia/app/app.go | 7 +++- cmd/gaia/app/app_test.go | 3 ++ cmd/gaia/app/export.go | 2 + cmd/gaia/app/genesis.go | 9 +++++ cmd/gaia/app/sim_test.go | 5 +++ cmd/gaia/cmd/gaiadebug/hack.go | 2 +- x/bank/app_test.go | 11 ++++++ x/bank/bench_test.go | 7 +++- x/bank/errors.go | 5 +++ x/bank/genesis.go | 32 ++++++++++++++++ x/bank/handler.go | 3 ++ x/bank/keeper.go | 56 ++++++++++++++++++++++------ x/bank/keeper_test.go | 30 ++++++++++----- x/bank/params.go | 22 +++++++++++ x/bank/simulation/msgs.go | 3 ++ x/distribution/keeper/test_common.go | 2 +- x/gov/endblocker_test.go | 4 ++ x/gov/test_common.go | 2 +- x/ibc/app_test.go | 4 +- x/ibc/ibc_test.go | 2 +- x/slashing/app_test.go | 2 +- x/slashing/test_common.go | 2 +- x/staking/app_test.go | 2 +- x/staking/keeper/test_common.go | 6 ++- 25 files changed, 191 insertions(+), 34 deletions(-) create mode 100644 x/bank/genesis.go create mode 100644 x/bank/params.go diff --git a/Makefile b/Makefile index a896678d7..96be180fd 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ GOTOOLS = \ github.com/rakyll/statik GOBIN ?= $(GOPATH)/bin -all: devtools install test_lint test +all: devtools vendor-deps install test_lint test # The below include contains the tools target. include scripts/Makefile diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index e99d0b808..2d66bd934 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -102,7 +102,11 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest b ) // add handlers - app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper) + app.bankKeeper = bank.NewBaseKeeper( + app.accountKeeper, + app.paramsKeeper.Subspace(bank.DefaultParamspace), + bank.DefaultCodespace, + ) app.feeCollectionKeeper = auth.NewFeeCollectionKeeper( app.cdc, app.keyFeeCollection, @@ -248,6 +252,7 @@ func (app *GaiaApp) initFromGenesisState(ctx sdk.Context, genesisState GenesisSt // initialize module-specific stores auth.InitGenesis(ctx, app.accountKeeper, app.feeCollectionKeeper, genesisState.AuthData) + bank.InitGenesis(ctx, app.bankKeeper, genesisState.BankData) slashing.InitGenesis(ctx, app.slashingKeeper, genesisState.SlashingData, genesisState.StakingData) gov.InitGenesis(ctx, app.govKeeper, genesisState.GovData) mint.InitGenesis(ctx, app.mintKeeper, genesisState.MintData) diff --git a/cmd/gaia/app/app_test.go b/cmd/gaia/app/app_test.go index 5f2b5b968..81f57abd6 100644 --- a/cmd/gaia/app/app_test.go +++ b/cmd/gaia/app/app_test.go @@ -4,6 +4,8 @@ import ( "os" "testing" + "github.com/cosmos/cosmos-sdk/x/bank" + "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" @@ -28,6 +30,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error { genesisState := NewGenesisState( genaccs, auth.DefaultGenesisState(), + bank.DefaultGenesisState(), staking.DefaultGenesisState(), mint.DefaultGenesisState(), distr.DefaultGenesisState(), diff --git a/cmd/gaia/app/export.go b/cmd/gaia/app/export.go index f535d1373..edf319cfa 100644 --- a/cmd/gaia/app/export.go +++ b/cmd/gaia/app/export.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/bank" distr "github.com/cosmos/cosmos-sdk/x/distribution" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/mint" @@ -39,6 +40,7 @@ func (app *GaiaApp) ExportAppStateAndValidators(forZeroHeight bool) ( genState := NewGenesisState( accounts, auth.ExportGenesis(ctx, app.accountKeeper, app.feeCollectionKeeper), + bank.ExportGenesis(ctx, app.bankKeeper), staking.ExportGenesis(ctx, app.stakingKeeper), mint.ExportGenesis(ctx, app.mintKeeper), distr.ExportGenesis(ctx, app.distrKeeper), diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index b0010323b..b8b9aa122 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -10,6 +10,8 @@ import ( "sort" "strings" + "github.com/cosmos/cosmos-sdk/x/bank" + tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/codec" @@ -34,6 +36,7 @@ var ( type GenesisState struct { Accounts []GenesisAccount `json:"accounts"` AuthData auth.GenesisState `json:"auth"` + BankData bank.GenesisState `json:"bank"` StakingData staking.GenesisState `json:"staking"` MintData mint.GenesisState `json:"mint"` DistrData distr.GenesisState `json:"distr"` @@ -43,6 +46,7 @@ type GenesisState struct { } func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, + bankData bank.GenesisState, stakingData staking.GenesisState, mintData mint.GenesisState, distrData distr.GenesisState, govData gov.GenesisState, slashingData slashing.GenesisState) GenesisState { @@ -50,6 +54,7 @@ func NewGenesisState(accounts []GenesisAccount, authData auth.GenesisState, return GenesisState{ Accounts: accounts, AuthData: authData, + BankData: bankData, StakingData: stakingData, MintData: mintData, DistrData: distrData, @@ -201,6 +206,7 @@ func NewDefaultGenesisState() GenesisState { return GenesisState{ Accounts: nil, AuthData: auth.DefaultGenesisState(), + BankData: bank.DefaultGenesisState(), StakingData: staking.DefaultGenesisState(), MintData: mint.DefaultGenesisState(), DistrData: distr.DefaultGenesisState(), @@ -227,6 +233,9 @@ func GaiaValidateGenesisState(genesisState GenesisState) error { if err := auth.ValidateGenesis(genesisState.AuthData); err != nil { return err } + if err := bank.ValidateGenesis(genesisState.BankData); err != nil { + return err + } if err := staking.ValidateGenesis(genesisState.StakingData); err != nil { return err } diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index b88e809eb..44e53425c 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -20,6 +20,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" authsim "github.com/cosmos/cosmos-sdk/x/auth/simulation" + "github.com/cosmos/cosmos-sdk/x/bank" banksim "github.com/cosmos/cosmos-sdk/x/bank/simulation" distr "github.com/cosmos/cosmos-sdk/x/distribution" distrsim "github.com/cosmos/cosmos-sdk/x/distribution/simulation" @@ -118,6 +119,9 @@ func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.T } fmt.Printf("Selected randomly generated auth parameters:\n\t%+v\n", authGenesis) + bankGenesis := bank.NewGenesisState(r.Int63n(2) == 0) + fmt.Printf("Selected randomly generated bank parameters:\n\t%+v\n", bankGenesis) + // Random genesis states vp := time.Duration(r.Intn(2*172800)) * time.Second govGenesis := gov.GenesisState{ @@ -203,6 +207,7 @@ func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.T genesis := GenesisState{ Accounts: genesisAccounts, AuthData: authGenesis, + BankData: bankGenesis, StakingData: stakingGenesis, MintData: mintGenesis, DistrData: distrGenesis, diff --git a/cmd/gaia/cmd/gaiadebug/hack.go b/cmd/gaia/cmd/gaiadebug/hack.go index 022192861..aa70b3fb0 100644 --- a/cmd/gaia/cmd/gaiadebug/hack.go +++ b/cmd/gaia/cmd/gaiadebug/hack.go @@ -178,7 +178,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB, baseAppOptions ...func(*bam.BaseAp ) // add handlers - app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper) + app.bankKeeper = bank.NewBaseKeeper(app.accountKeeper, app.paramsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace) app.stakingKeeper = staking.NewKeeper(app.cdc, app.keyStaking, app.tkeyStaking, app.bankKeeper, app.paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace) app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, app.stakingKeeper, app.paramsKeeper.Subspace(slashing.DefaultParamspace), slashing.DefaultCodespace) diff --git a/x/bank/app_test.go b/x/bank/app_test.go index 36e084b73..a6e9d6dfe 100644 --- a/x/bank/app_test.go +++ b/x/bank/app_test.go @@ -91,6 +91,17 @@ func getMockApp(t *testing.T) *mock.App { return mapp } +// overwrite the mock init chainer +func getInitChainer(mapp *mock.App, keeper BaseKeeper) sdk.InitChainer { + return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { + mapp.InitChainer(ctx, req) + bankGenesis := DefaultGenesisState() + InitGenesis(ctx, keeper, bankGenesis) + + return abci.ResponseInitChain{} + } +} + func TestMsgSendWithAccounts(t *testing.T) { mapp := getMockApp(t) acc := &auth.BaseAccount{ diff --git a/x/bank/bench_test.go b/x/bank/bench_test.go index a3f69dcdd..5eb702713 100644 --- a/x/bank/bench_test.go +++ b/x/bank/bench_test.go @@ -16,8 +16,13 @@ func getBenchmarkMockApp() (*mock.App, error) { mapp := mock.NewApp() RegisterCodec(mapp.Cdc) - bankKeeper := NewBaseKeeper(mapp.AccountKeeper) + bankKeeper := NewBaseKeeper( + mapp.AccountKeeper, + mapp.ParamsKeeper.Subspace(DefaultParamspace), + DefaultCodespace, + ) mapp.Router().AddRoute("bank", NewHandler(bankKeeper)) + mapp.SetInitChainer(getInitChainer(mapp, bankKeeper)) err := mapp.CompleteSetup() return mapp, err diff --git a/x/bank/errors.go b/x/bank/errors.go index 5df5cfdce..fd4211899 100644 --- a/x/bank/errors.go +++ b/x/bank/errors.go @@ -11,6 +11,7 @@ const ( CodeInvalidInput sdk.CodeType = 101 CodeInvalidOutput sdk.CodeType = 102 + CodeSendDisabled sdk.CodeType = 103 ) // NOTE: Don't stringer this, we'll put better messages in later. @@ -44,6 +45,10 @@ func ErrNoOutputs(codespace sdk.CodespaceType) sdk.Error { return newError(codespace, CodeInvalidOutput, "") } +func ErrSendDisabled(codespace sdk.CodespaceType) sdk.Error { + return newError(codespace, CodeSendDisabled, "") +} + //---------------------------------------- func msgOrDefaultMsg(msg string, code sdk.CodeType) string { diff --git a/x/bank/genesis.go b/x/bank/genesis.go new file mode 100644 index 000000000..b85ba2193 --- /dev/null +++ b/x/bank/genesis.go @@ -0,0 +1,32 @@ +package bank + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GenesisState is the bank state that must be provided at genesis. +type GenesisState struct { + SendEnabled bool `json:"send_enabled"` +} + +// NewGenesisState creates a new genesis state. +func NewGenesisState(sendEnabled bool) GenesisState { + return GenesisState{SendEnabled: sendEnabled} +} + +// Return a default genesis state +func DefaultGenesisState() GenesisState { return NewGenesisState(true) } + +// InitGenesis sets distribution information for genesis. +func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) { + keeper.SetSendEnabled(ctx, data.SendEnabled) +} + +// ExportGenesis returns a GenesisState for a given context and keeper. +func ExportGenesis(ctx sdk.Context, keeper Keeper) GenesisState { + return NewGenesisState(keeper.GetSendEnabled(ctx)) +} + +// ValidateGenesis performs basic validation of bank genesis data returning an +// error for any failed validation criteria. +func ValidateGenesis(data GenesisState) error { return nil } diff --git a/x/bank/handler.go b/x/bank/handler.go index 9b4095700..2d1abf822 100644 --- a/x/bank/handler.go +++ b/x/bank/handler.go @@ -20,6 +20,9 @@ func NewHandler(k Keeper) sdk.Handler { // Handle MsgSend. func handleMsgSend(ctx sdk.Context, k Keeper, msg MsgSend) sdk.Result { // NOTE: totalIn == totalOut should already have been checked + if !k.GetSendEnabled(ctx) { + return ErrSendDisabled(k.Codespace()).Result() + } tags, err := k.InputOutputCoins(ctx, msg.Inputs, msg.Outputs) if err != nil { return err.Result() diff --git a/x/bank/keeper.go b/x/bank/keeper.go index 499b43e59..3055d8df4 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -6,6 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/params" ) //----------------------------------------------------------------------------- @@ -32,14 +33,20 @@ type Keeper interface { type BaseKeeper struct { BaseSendKeeper - ak auth.AccountKeeper + ak auth.AccountKeeper + paramSpace params.Subspace } // NewBaseKeeper returns a new BaseKeeper -func NewBaseKeeper(ak auth.AccountKeeper) BaseKeeper { +func NewBaseKeeper(ak auth.AccountKeeper, + paramSpace params.Subspace, + codespace sdk.CodespaceType) BaseKeeper { + + ps := paramSpace.WithTypeTable(ParamTypeTable()) return BaseKeeper{ - BaseSendKeeper: NewBaseSendKeeper(ak), + BaseSendKeeper: NewBaseSendKeeper(ak, ps, codespace), ak: ak, + paramSpace: ps, } } @@ -95,6 +102,9 @@ type SendKeeper interface { ViewKeeper SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) (sdk.Tags, sdk.Error) + + GetSendEnabled(ctx sdk.Context) bool + SetSendEnabled(ctx sdk.Context, enabled bool) } var _ SendKeeper = (*BaseSendKeeper)(nil) @@ -104,14 +114,18 @@ var _ SendKeeper = (*BaseSendKeeper)(nil) type BaseSendKeeper struct { BaseViewKeeper - ak auth.AccountKeeper + ak auth.AccountKeeper + paramSpace params.Subspace } // NewBaseSendKeeper returns a new BaseSendKeeper. -func NewBaseSendKeeper(ak auth.AccountKeeper) BaseSendKeeper { +func NewBaseSendKeeper(ak auth.AccountKeeper, + paramSpace params.Subspace, codespace sdk.CodespaceType) BaseSendKeeper { + return BaseSendKeeper{ - BaseViewKeeper: NewBaseViewKeeper(ak), + BaseViewKeeper: NewBaseViewKeeper(ak, codespace), ak: ak, + paramSpace: paramSpace, } } @@ -119,10 +133,22 @@ func NewBaseSendKeeper(ak auth.AccountKeeper) BaseSendKeeper { func (keeper BaseSendKeeper) SendCoins( ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins, ) (sdk.Tags, sdk.Error) { - return sendCoins(ctx, keeper.ak, fromAddr, toAddr, amt) } +// GetSendEnabled returns the current SendEnabled +// nolint: errcheck +func (keeper BaseSendKeeper) GetSendEnabled(ctx sdk.Context) bool { + var enabled bool + keeper.paramSpace.Get(ctx, ParamStoreKeySendEnabled, &enabled) + return enabled +} + +// nolint: errcheck +func (keeper BaseSendKeeper) SetSendEnabled(ctx sdk.Context, enabled bool) { + keeper.paramSpace.Set(ctx, ParamStoreKeySendEnabled, &enabled) +} + //----------------------------------------------------------------------------- // View Keeper @@ -133,18 +159,19 @@ var _ ViewKeeper = (*BaseViewKeeper)(nil) type ViewKeeper interface { GetCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coins) bool + + Codespace() sdk.CodespaceType } // BaseViewKeeper implements a read only keeper implementation of ViewKeeper. type BaseViewKeeper struct { - ak auth.AccountKeeper + ak auth.AccountKeeper + codespace sdk.CodespaceType } // NewBaseViewKeeper returns a new BaseViewKeeper. -func NewBaseViewKeeper(ak auth.AccountKeeper) BaseViewKeeper { - return BaseViewKeeper{ - ak: ak, - } +func NewBaseViewKeeper(ak auth.AccountKeeper, codespace sdk.CodespaceType) BaseViewKeeper { + return BaseViewKeeper{ak: ak, codespace: codespace} } // GetCoins returns the coins at the addr. @@ -157,6 +184,11 @@ func (keeper BaseViewKeeper) HasCoins(ctx sdk.Context, addr sdk.AccAddress, amt return hasCoins(ctx, keeper.ak, addr, amt) } +// Codespace returns the keeper's codespace. +func (keeper BaseViewKeeper) Codespace() sdk.CodespaceType { + return keeper.codespace +} + //----------------------------------------------------------------------------- // Auxiliary diff --git a/x/bank/keeper_test.go b/x/bank/keeper_test.go index 8a3d4dc83..23e6321ab 100644 --- a/x/bank/keeper_test.go +++ b/x/bank/keeper_test.go @@ -21,6 +21,7 @@ type testInput struct { cdc *codec.Codec ctx sdk.Context ak auth.AccountKeeper + pk params.Keeper } func setupTestInput() testInput { @@ -49,13 +50,14 @@ func setupTestInput() testInput { ak.SetParams(ctx, auth.DefaultParams()) - return testInput{cdc: cdc, ctx: ctx, ak: ak} + return testInput{cdc: cdc, ctx: ctx, ak: ak, pk: pk} } func TestKeeper(t *testing.T) { input := setupTestInput() ctx := input.ctx - bankKeeper := NewBaseKeeper(input.ak) + bankKeeper := NewBaseKeeper(input.ak, input.pk.Subspace(DefaultParamspace), DefaultCodespace) + bankKeeper.SetSendEnabled(ctx, true) addr := sdk.AccAddress([]byte("addr1")) addr2 := sdk.AccAddress([]byte("addr2")) @@ -134,8 +136,10 @@ func TestKeeper(t *testing.T) { func TestSendKeeper(t *testing.T) { input := setupTestInput() ctx := input.ctx - bankKeeper := NewBaseKeeper(input.ak) - sendKeeper := NewBaseSendKeeper(input.ak) + paramSpace := input.pk.Subspace(DefaultParamspace) + bankKeeper := NewBaseKeeper(input.ak, paramSpace, DefaultCodespace) + sendKeeper := NewBaseSendKeeper(input.ak, paramSpace, DefaultCodespace) + bankKeeper.SetSendEnabled(ctx, true) addr := sdk.AccAddress([]byte("addr1")) addr2 := sdk.AccAddress([]byte("addr2")) @@ -181,8 +185,10 @@ func TestSendKeeper(t *testing.T) { func TestViewKeeper(t *testing.T) { input := setupTestInput() ctx := input.ctx - bankKeeper := NewBaseKeeper(input.ak) - viewKeeper := NewBaseViewKeeper(input.ak) + paramSpace := input.pk.Subspace(DefaultParamspace) + bankKeeper := NewBaseKeeper(input.ak, paramSpace, DefaultCodespace) + bankKeeper.SetSendEnabled(ctx, true) + viewKeeper := NewBaseViewKeeper(input.ak, DefaultCodespace) addr := sdk.AccAddress([]byte("addr1")) acc := input.ak.NewAccountWithAddress(ctx, addr) @@ -209,7 +215,8 @@ func TestVestingAccountSend(t *testing.T) { origCoins := sdk.Coins{sdk.NewInt64Coin("steak", 100)} sendCoins := sdk.Coins{sdk.NewInt64Coin("steak", 50)} - bankKeeper := NewBaseKeeper(input.ak) + bankKeeper := NewBaseKeeper(input.ak, input.pk.Subspace(DefaultParamspace), DefaultCodespace) + bankKeeper.SetSendEnabled(ctx, true) addr1 := sdk.AccAddress([]byte("addr1")) addr2 := sdk.AccAddress([]byte("addr2")) @@ -242,7 +249,8 @@ func TestVestingAccountReceive(t *testing.T) { origCoins := sdk.Coins{sdk.NewInt64Coin("steak", 100)} sendCoins := sdk.Coins{sdk.NewInt64Coin("steak", 50)} - bankKeeper := NewBaseKeeper(input.ak) + bankKeeper := NewBaseKeeper(input.ak, input.pk.Subspace(DefaultParamspace), DefaultCodespace) + bankKeeper.SetSendEnabled(ctx, true) addr1 := sdk.AccAddress([]byte("addr1")) addr2 := sdk.AccAddress([]byte("addr2")) @@ -275,7 +283,8 @@ func TestDelegateCoins(t *testing.T) { origCoins := sdk.Coins{sdk.NewInt64Coin("steak", 100)} delCoins := sdk.Coins{sdk.NewInt64Coin("steak", 50)} - bankKeeper := NewBaseKeeper(input.ak) + bankKeeper := NewBaseKeeper(input.ak, input.pk.Subspace(DefaultParamspace), DefaultCodespace) + bankKeeper.SetSendEnabled(ctx, true) addr1 := sdk.AccAddress([]byte("addr1")) addr2 := sdk.AccAddress([]byte("addr2")) @@ -311,7 +320,8 @@ func TestUndelegateCoins(t *testing.T) { origCoins := sdk.Coins{sdk.NewInt64Coin("steak", 100)} delCoins := sdk.Coins{sdk.NewInt64Coin("steak", 50)} - bankKeeper := NewBaseKeeper(input.ak) + bankKeeper := NewBaseKeeper(input.ak, input.pk.Subspace(DefaultParamspace), DefaultCodespace) + bankKeeper.SetSendEnabled(ctx, true) addr1 := sdk.AccAddress([]byte("addr1")) addr2 := sdk.AccAddress([]byte("addr2")) diff --git a/x/bank/params.go b/x/bank/params.go new file mode 100644 index 000000000..9aea6adae --- /dev/null +++ b/x/bank/params.go @@ -0,0 +1,22 @@ +package bank + +import ( + "github.com/cosmos/cosmos-sdk/x/params" +) + +const ( + // default paramspace for params keeper + DefaultParamspace = "bank" + // default send enabled + DefaultSendEnabled = true +) + +// ParamStoreKeySendEnabled is store's key for SendEnabled +var ParamStoreKeySendEnabled = []byte("sendenabled") + +// type declaration for parameters +func ParamTypeTable() params.TypeTable { + return params.NewTypeTable( + ParamStoreKeySendEnabled, false, + ) +} diff --git a/x/bank/simulation/msgs.go b/x/bank/simulation/msgs.go index e7d16496b..c4d4919eb 100644 --- a/x/bank/simulation/msgs.go +++ b/x/bank/simulation/msgs.go @@ -112,6 +112,9 @@ func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper auth.AccountKeeper, msg b if handler != nil { res := handler(ctx, msg) if !res.IsOK() { + if res.Code == bank.CodeSendDisabled { + return nil + } // TODO: Do this in a more 'canonical' way return fmt.Errorf("handling msg failed %v", res) } diff --git a/x/distribution/keeper/test_common.go b/x/distribution/keeper/test_common.go index 420f40ac4..8e8b157c4 100644 --- a/x/distribution/keeper/test_common.go +++ b/x/distribution/keeper/test_common.go @@ -110,7 +110,7 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initCoins int64, ctx := sdk.NewContext(ms, abci.Header{ChainID: "foochainid"}, isCheckTx, log.NewNopLogger()) accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount) - ck := bank.NewBaseKeeper(accountKeeper) + ck := bank.NewBaseKeeper(accountKeeper, pk.Subspace(bank.DefaultParamspace), bank.DefaultCodespace) sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, ck, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace) sk.SetPool(ctx, staking.InitialPool()) sk.SetParams(ctx, staking.DefaultParams()) diff --git a/x/gov/endblocker_test.go b/x/gov/endblocker_test.go index 1eb60c4f2..952f87f77 100644 --- a/x/gov/endblocker_test.go +++ b/x/gov/endblocker_test.go @@ -16,6 +16,7 @@ func TestTickExpiredDepositPeriod(t *testing.T) { mapp, keeper, _, addrs, _, _ := getMockApp(t, 10, GenesisState{}, nil) mapp.BeginBlock(abci.RequestBeginBlock{}) ctx := mapp.BaseApp.NewContext(false, abci.Header{}) + keeper.ck.SetSendEnabled(ctx, true) govHandler := NewHandler(keeper) inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) @@ -58,6 +59,7 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { mapp, keeper, _, addrs, _, _ := getMockApp(t, 10, GenesisState{}, nil) mapp.BeginBlock(abci.RequestBeginBlock{}) ctx := mapp.BaseApp.NewContext(false, abci.Header{}) + keeper.ck.SetSendEnabled(ctx, true) govHandler := NewHandler(keeper) inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) @@ -114,6 +116,7 @@ func TestTickPassedDepositPeriod(t *testing.T) { mapp, keeper, _, addrs, _, _ := getMockApp(t, 10, GenesisState{}, nil) mapp.BeginBlock(abci.RequestBeginBlock{}) ctx := mapp.BaseApp.NewContext(false, abci.Header{}) + keeper.ck.SetSendEnabled(ctx, true) govHandler := NewHandler(keeper) inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) @@ -156,6 +159,7 @@ func TestTickPassedVotingPeriod(t *testing.T) { SortAddresses(addrs) mapp.BeginBlock(abci.RequestBeginBlock{}) ctx := mapp.BaseApp.NewContext(false, abci.Header{}) + keeper.ck.SetSendEnabled(ctx, true) govHandler := NewHandler(keeper) inactiveQueue := keeper.InactiveProposalQueueIterator(ctx, ctx.BlockHeader().Time) diff --git a/x/gov/test_common.go b/x/gov/test_common.go index f030d7b84..34a507c26 100644 --- a/x/gov/test_common.go +++ b/x/gov/test_common.go @@ -31,7 +31,7 @@ func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []a keyGov := sdk.NewKVStoreKey(StoreKey) pk := mapp.ParamsKeeper - ck := bank.NewBaseKeeper(mapp.AccountKeeper) + ck := bank.NewBaseKeeper(mapp.AccountKeeper, mapp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace) sk = staking.NewKeeper(mapp.Cdc, keyStaking, tkeyStaking, ck, pk.Subspace(staking.DefaultParamspace), staking.DefaultCodespace) keeper = NewKeeper(mapp.Cdc, keyGov, pk, pk.Subspace("testgov"), ck, sk, DefaultCodespace) diff --git a/x/ibc/app_test.go b/x/ibc/app_test.go index f59b37921..8129783d4 100644 --- a/x/ibc/app_test.go +++ b/x/ibc/app_test.go @@ -21,7 +21,9 @@ func getMockApp(t *testing.T) *mock.App { RegisterCodec(mapp.Cdc) keyIBC := sdk.NewKVStoreKey("ibc") ibcMapper := NewMapper(mapp.Cdc, keyIBC, DefaultCodespace) - bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper) + bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper, + mapp.ParamsKeeper.Subspace(bank.DefaultParamspace), + bank.DefaultCodespace) mapp.Router().AddRoute("ibc", NewHandler(ibcMapper, bankKeeper)) require.NoError(t, mapp.CompleteSetup(keyIBC)) diff --git a/x/ibc/ibc_test.go b/x/ibc/ibc_test.go index 9b9bbf93a..f2719f7b3 100644 --- a/x/ibc/ibc_test.go +++ b/x/ibc/ibc_test.go @@ -50,7 +50,7 @@ func setupTestInput() testInput { ak := auth.NewAccountKeeper( cdc, authCapKey, pk.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount, ) - bk := bank.NewBaseKeeper(ak) + bk := bank.NewBaseKeeper(ak, pk.Subspace(bank.DefaultParamspace), bank.DefaultCodespace) ctx := sdk.NewContext(ms, abci.Header{ChainID: "test-chain-id"}, false, log.NewNopLogger()) ak.SetParams(ctx, auth.DefaultParams()) diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index 3662d7faa..e9c17b89a 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -30,7 +30,7 @@ func getMockApp(t *testing.T) (*mock.App, staking.Keeper, Keeper) { tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) keySlashing := sdk.NewKVStoreKey(StoreKey) - bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper) + bankKeeper := bank.NewBaseKeeper(mapp.AccountKeeper, mapp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace) stakingKeeper := staking.NewKeeper(mapp.Cdc, keyStaking, tkeyStaking, bankKeeper, mapp.ParamsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace) keeper := NewKeeper(mapp.Cdc, keySlashing, stakingKeeper, mapp.ParamsKeeper.Subspace(DefaultParamspace), DefaultCodespace) mapp.Router().AddRoute(staking.RouterKey, staking.NewHandler(stakingKeeper)) diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index c2e7c2e87..428961854 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -72,7 +72,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams) accountKeeper := auth.NewAccountKeeper(cdc, keyAcc, paramsKeeper.Subspace(auth.DefaultParamspace), auth.ProtoBaseAccount) - ck := bank.NewBaseKeeper(accountKeeper) + ck := bank.NewBaseKeeper(accountKeeper, paramsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace) sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, ck, paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace) genesis := staking.DefaultGenesisState() diff --git a/x/staking/app_test.go b/x/staking/app_test.go index f95bce132..852a9593a 100644 --- a/x/staking/app_test.go +++ b/x/staking/app_test.go @@ -22,7 +22,7 @@ func getMockApp(t *testing.T) (*mock.App, Keeper) { keyStaking := sdk.NewKVStoreKey(StoreKey) tkeyStaking := sdk.NewTransientStoreKey(TStoreKey) - bankKeeper := bank.NewBaseKeeper(mApp.AccountKeeper) + bankKeeper := bank.NewBaseKeeper(mApp.AccountKeeper, mApp.ParamsKeeper.Subspace(bank.DefaultParamspace), bank.DefaultCodespace) keeper := NewKeeper(mApp.Cdc, keyStaking, tkeyStaking, bankKeeper, mApp.ParamsKeeper.Subspace(DefaultParamspace), DefaultCodespace) mApp.Router().AddRoute(RouterKey, NewHandler(keeper)) diff --git a/x/staking/keeper/test_common.go b/x/staking/keeper/test_common.go index b8c106bf8..aa95c885c 100644 --- a/x/staking/keeper/test_common.go +++ b/x/staking/keeper/test_common.go @@ -112,7 +112,11 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context auth.ProtoBaseAccount, // prototype ) - ck := bank.NewBaseKeeper(accountKeeper) + ck := bank.NewBaseKeeper( + accountKeeper, + pk.Subspace(bank.DefaultParamspace), + bank.DefaultCodespace, + ) keeper := NewKeeper(cdc, keyStaking, tkeyStaking, ck, pk.Subspace(DefaultParamspace), types.DefaultCodespace) keeper.SetPool(ctx, types.InitialPool()) From 3e6ce3de8d977315bc889a65c3dc5d239ce623b4 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Tue, 29 Jan 2019 10:10:35 -0800 Subject: [PATCH 16/99] Merge PR #3103: Tombstone specification --- docs/spec/slashing/tombstone.md | 121 ++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 docs/spec/slashing/tombstone.md diff --git a/docs/spec/slashing/tombstone.md b/docs/spec/slashing/tombstone.md new file mode 100644 index 000000000..d6ba47d5f --- /dev/null +++ b/docs/spec/slashing/tombstone.md @@ -0,0 +1,121 @@ +# Staking Tombstone + +## Abstract + +In the current implementation of the `slashing` module, when the consensus engine +informs the state machine of a validator's consensus fault, the validator is +partially slashed, and put into a "jail period", a period of time in which they +are not allowed to rejoin the validator set. However, because of the nature of +consensus faults and ABCI, there can be a delay between an infraction occurring, +and evidence of the infraction reaching the state machine (this is one of the +primary reasons for the existence of the unbonding period). + +> Note: The tombstone concept, only applies to faults that have a delay between +the infraction occurring and evidence reaching the state machine. For example, +evidence of a validator double signing may take a while to reach the state machine +due to unpredictable evidence gossip layer delays and the ability of validators to +selectively reveal double-signatures (e.g. to infrequently-online light clients). +Liveness slashing, on the other hand, is detected immediately as soon as the +infraction occurs, and therefore no slashing period is needed. A validator is +immediately put into jail period, and they cannot commit another liveness fault +until they unjail. In the future, there may be other types of byzantine faults +that have delays (for example, submitting evidence of an invalid proposal as a transaction). +When implemented, it will have to be decided whether these future types of +byzantine faults will result in a tombstoning (and if not, the slash amounts +will not be capped by a slashing period). + +In the current system design, once a validator is put in the jail for a consensus +fault, after the `JailPeriod` they are allowed to send a transaction to `unjail` +themselves, and thus rejoin the validator set. + +One of the "design desires" of the `slashing` module is that if multiple +infractions occur before evidence is executed (and a validator is put in jail), +they should only be punished for single worst infraction, but not cumulatively. +For example, if the sequence of events is: + +1. Validator A commits Infraction 1 (worth 30% slash) +2. Validator A commits Infraction 2 (worth 40% slash) +3. Validator A commits Infraction 3 (worth 35% slash) +4. Evidence for Infraction 1 reaches state machine (and validator is put in jail) +5. Evidence for Infraction 2 reaches state machine +6. Evidence for Infraction 3 reaches state machine + +Only Infraction 2 should have its slash take effect, as it is the highest. This +is done, so that in the case of the compromise of a validator's consensus key, +they will only be punished once, even if the hacker double-signs many blocks. +Because, the unjailing has to be done with the validator's operator key, they +have a chance to re-secure their consensus key, and then signal that they are +ready using their operator key. We call this period during which we track only +the max infraction, the "slashing period". + +Once, a validator rejoins by unjailing themselves, we begin a new slashing period; +if they commit a new infraction after unjailing, it gets slashed cumulatively on +top of the worst infraction from the previous slashing period. + +However, while infractions are grouped based off of the slashing periods, because +evidence can be submitted up to an `unbondingPeriod` after the infraction, we +still have to allow for evidence to be submitted for previous slashing periods. +For example, if the sequence of events is: + +1. Validator A commits Infraction 1 (worth 30% slash) +2. Validator A commits Infraction 2 (worth 40% slash) +3. Evidence for Infraction 1 reaches state machine (and Validator A is put in jail) +4. Validator A unjails + +We are now in a new slashing period, however we still have to keep the door open +for the previous infraction, as the evidence for Infraction 2 may still come in. +As the number of slashing periods increase, it creates more complexity as we have +to keep track of the highest infraction amount for every single slashing period. + +> Note: Currently, according to the `slashing` module spec, a new slashing period +is created every time a validator is unbonded then rebonded. This should probably +be changed to jailed/unjailed. See issue [#3205](https://github.com/cosmos/cosmos-sdk/issues/3205) +for further details. For the remainder of this, I will assume that we only start +a new slashing period when a validator gets unjailed. + +The maximum number of slashing periods is the `len(UnbondingPeriod) / len(JailPeriod)`. +The current defaults in Gaia for the `UnbondingPeriod` and `JailPeriod` are 3 weeks +and 2 days, respectively. This means there could potentially be up to 11 slashing +periods concurrently being tracked per validator. If we set the `JailPeriod >= UnbondingPeriod`, +we only have to track 1 slashing period (i.e not have to track slashing periods). + +Currently, in the jail period implementation, once a validator unjails, all of +their delegators who are delegated to them (haven't unbonded / redelegated away), +stay with them. Given that consensus safety faults are so egregious +(way more so than liveness faults), it is probably prudent to have delegators not +"auto-rebond" to the validator. Thus, we propose setting the "jail time" for a +validator who commits a consensus safety fault, to `infinite` (i.e. a tombstone state). +This essentially kicks the validator out of the validator set and does not allow +them to re-enter the validator set. All of their delegators (including the operator themselves) +have to either unbond or redelegate away. The validator operator can create a new +validator if they would like, with a new operator key and consensus key, but they +have to "re-earn" their delegations back. To put the validator in the tombstone +state, we set `DoubleSignJailEndTime` to `time.Unix(253402300800)`, the maximum +time supported by Amino. + +Implementing the tombstone system and getting rid of the slashing period tracking +will make the `slashing` module way simpler, especially because we can remove all +of the hooks defined in the `slashing` module consumed by the `staking` module +(the `slashing` module still consumes hooks defined in `staking`). + +### Single slashing amount + +Another optimization that can be made is that if we assume that all ABCI faults +for Tendermint consensus are slashed at the same level, we don't have to keep +track of "max slash". Once an ABCI fault happens, we don't have to worry about +comparing potential future ones to find the max. + +Currently the only Tendermint ABCI fault is: + +- Unjustified precommits (double signs) + +It is currently planned to include the following fault in the near future: + +- Signing a precommit when you're in unbonding phase (needed to make light client bisection safe) + +Given that these faults are both attributable byzantine faults, we will likely +want to slash them equally, and thus we can enact the above change. + +> Note: This change may make sense for current Tendermint consensus, but maybe +not for a different consensus algorithm or future versions of Tendermint that +may want to punish at different levels (for example, partial slashing). From 1a656e70235b72840a158fc235ac5db72fe8e376 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 29 Jan 2019 13:11:33 -0500 Subject: [PATCH 17/99] Merge PR #3425: Add Vesting Account Genesis Validation --- PENDING.md | 2 ++ cmd/gaia/app/genesis.go | 40 ++++++++++++++++++------ cmd/gaia/app/genesis_test.go | 32 +++++++++++++------ docs/gaia/genesis.md | 60 ++++++++++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+), 19 deletions(-) create mode 100644 docs/gaia/genesis.md diff --git a/PENDING.md b/PENDING.md index ad915ada6..7f34780cb 100644 --- a/PENDING.md +++ b/PENDING.md @@ -36,6 +36,8 @@ IMPROVEMENTS * Gaia CLI (`gaiacli`) * Gaia + * [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account + genesis validation checks to `GaiaValidateGenesisState`. * SDK diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index b8b9aa122..3eae8af87 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -9,6 +9,7 @@ import ( "path/filepath" "sort" "strings" + "time" "github.com/cosmos/cosmos-sdk/x/bank" @@ -85,8 +86,8 @@ type GenesisAccount struct { OriginalVesting sdk.Coins `json:"original_vesting"` // total vesting coins upon initialization DelegatedFree sdk.Coins `json:"delegated_free"` // delegated vested coins at time of delegation DelegatedVesting sdk.Coins `json:"delegated_vesting"` // delegated vesting coins at time of delegation - StartTime int64 `json:"start_time"` // vesting start time - EndTime int64 `json:"end_time"` // vesting end time + StartTime int64 `json:"start_time"` // vesting start time (UNIX Epoch time) + EndTime int64 `json:"end_time"` // vesting end time (UNIX Epoch time) } func NewGenesisAccount(acc *auth.BaseAccount) GenesisAccount { @@ -252,17 +253,38 @@ func GaiaValidateGenesisState(genesisState GenesisState) error { return slashing.ValidateGenesis(genesisState.SlashingData) } -// Ensures that there are no duplicate accounts in the genesis state, +// validateGenesisStateAccounts performs validation of genesis accounts. It +// ensures that there are no duplicate accounts in the genesis state and any +// provided vesting accounts are valid. func validateGenesisStateAccounts(accs []GenesisAccount) error { addrMap := make(map[string]bool, len(accs)) - for i := 0; i < len(accs); i++ { - acc := accs[i] - strAddr := string(acc.Address) - if _, ok := addrMap[strAddr]; ok { - return fmt.Errorf("Duplicate account in genesis state: Address %v", acc.Address) + for _, acc := range accs { + addrStr := acc.Address.String() + + // disallow any duplicate accounts + if _, ok := addrMap[addrStr]; ok { + return fmt.Errorf("duplicate account found in genesis state; address: %s", addrStr) } - addrMap[strAddr] = true + + // validate any vesting fields + if !acc.OriginalVesting.IsZero() { + if acc.EndTime == 0 { + return fmt.Errorf("missing end time for vesting account; address: %s", addrStr) + } + + if acc.StartTime >= acc.EndTime { + return fmt.Errorf( + "vesting start time must before end time; address: %s, start: %s, end: %s", + addrStr, + time.Unix(acc.StartTime, 0).UTC().Format(time.RFC3339), + time.Unix(acc.EndTime, 0).UTC().Format(time.RFC3339), + ) + } + } + + addrMap[addrStr] = true } + return nil } diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go index be0c9dba4..82829b699 100644 --- a/cmd/gaia/app/genesis_test.go +++ b/cmd/gaia/app/genesis_test.go @@ -111,29 +111,41 @@ func makeMsg(name string, pk crypto.PubKey) auth.StdTx { } func TestGaiaGenesisValidation(t *testing.T) { - genTxs := make([]auth.StdTx, 2) - // Test duplicate accounts fails - genTxs[0] = makeMsg("test-0", pk1) - genTxs[1] = makeMsg("test-1", pk1) - genesisState := makeGenesisState(t, genTxs) + genTxs := []auth.StdTx{makeMsg("test-0", pk1), makeMsg("test-1", pk2)} + dupGenTxs := []auth.StdTx{makeMsg("test-0", pk1), makeMsg("test-1", pk1)} + + // require duplicate accounts fails validation + genesisState := makeGenesisState(t, dupGenTxs) err := GaiaValidateGenesisState(genesisState) - require.NotNil(t, err) - // Test bonded + jailed validator fails + require.Error(t, err) + + // require invalid vesting account fails validation (invalid end time) + genesisState = makeGenesisState(t, genTxs) + genesisState.Accounts[0].OriginalVesting = genesisState.Accounts[0].Coins + err = GaiaValidateGenesisState(genesisState) + require.Error(t, err) + genesisState.Accounts[0].StartTime = 1548888000 + genesisState.Accounts[0].EndTime = 1548775410 + err = GaiaValidateGenesisState(genesisState) + require.Error(t, err) + + // require bonded + jailed validator fails validation genesisState = makeGenesisState(t, genTxs) val1 := stakingTypes.NewValidator(addr1, pk1, stakingTypes.Description{Moniker: "test #2"}) val1.Jailed = true val1.Status = sdk.Bonded genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val1) err = GaiaValidateGenesisState(genesisState) - require.NotNil(t, err) - // Test duplicate validator fails + require.Error(t, err) + + // require duplicate validator fails validation val1.Jailed = false genesisState = makeGenesisState(t, genTxs) val2 := stakingTypes.NewValidator(addr1, pk1, stakingTypes.Description{Moniker: "test #3"}) genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val1) genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val2) err = GaiaValidateGenesisState(genesisState) - require.NotNil(t, err) + require.Error(t, err) } func TestNewDefaultGenesisAccount(t *testing.T) { diff --git a/docs/gaia/genesis.md b/docs/gaia/genesis.md new file mode 100644 index 000000000..727944fbc --- /dev/null +++ b/docs/gaia/genesis.md @@ -0,0 +1,60 @@ +# Gaia Genesis State + +Gaia genesis state, `GenesisState`, is composed of accounts, various module +states and metadata such as genesis transactions. Each module may specify its +own `GenesisState`. In addition, each module may specify its own genesis state +validation, import and export functionality. + +The Gaia genesis state is defined as follows: + +```go +type GenesisState struct { + Accounts []GenesisAccount `json:"accounts"` + AuthData auth.GenesisState `json:"auth"` + BankData bank.GenesisState `json:"bank"` + StakingData staking.GenesisState `json:"staking"` + MintData mint.GenesisState `json:"mint"` + DistrData distr.GenesisState `json:"distr"` + GovData gov.GenesisState `json:"gov"` + SlashingData slashing.GenesisState `json:"slashing"` + GenTxs []json.RawMessage `json:"gentxs"` +} +``` + +In the ABCI `initChainer` definition of Gaia the `initFromGenesisState` is called +which internally calls each module's `InitGenesis` providing its own respective +`GenesisState` as a parameter. + +## Accounts + +Genesis accounts defined in the `GenesisState` are defined as follows: + +```go +type GenesisAccount struct { + Address sdk.AccAddress `json:"address"` + Coins sdk.Coins `json:"coins"` + Sequence uint64 `json:"sequence_number"` + AccountNumber uint64 `json:"account_number"` + + // vesting account fields + OriginalVesting sdk.Coins `json:"original_vesting"` // total vesting coins upon initialization + DelegatedFree sdk.Coins `json:"delegated_free"` // delegated vested coins at time of delegation + DelegatedVesting sdk.Coins `json:"delegated_vesting"` // delegated vesting coins at time of delegation + StartTime int64 `json:"start_time"` // vesting start time (UNIX Epoch time) + EndTime int64 `json:"end_time"` // vesting end time (UNIX Epoch time) +} +``` + +Each account must have a valid and unique account number in addition to a +sequence number (nonce) and address. + +Accounts may also be vesting, in which case they must provide the necessary vesting +information. Vesting accounts must provide at a minimum `OriginalVesting` and +`EndTime`. If `StartTime` is also provided, the account will be treated as a +"continuous" vesting account in which it vests coins at a predefined schedule. +Providing a `StartTime` must be less than `EndTime` but may be in the future. +In other words, it does not have to be equal to the genesis time. In a new chain +starting from a fresh state (not exported), `OriginalVesting` must be less than +or equal to `Coins.` + + From 90797f5e09f980276e0fa609c3f5d1e9fbd6e335 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 29 Jan 2019 14:22:47 -0500 Subject: [PATCH 18/99] Gaia Lite Generate Only Support (w/o Keybase) (#3396) --- PENDING.md | 7 + client/context/context.go | 96 +- client/context/query.go | 14 +- client/lcd/lcd_test.go | 73 +- client/lcd/swagger-ui/swagger.yaml | 1385 +++++++++++++--------------- client/lcd/test_helpers.go | 56 +- client/utils/rest.go | 133 ++- client/utils/utils.go | 32 +- docs/clients/service-providers.md | 17 +- docs/gaia/gaiacli.md | 15 +- x/auth/client/rest/sign.go | 11 +- x/bank/client/cli/sendtx.go | 8 +- x/bank/client/rest/sendtx.go | 25 +- x/distribution/client/cli/tx.go | 21 +- x/gov/client/cli/tx.go | 22 +- x/gov/client/rest/rest.go | 15 + x/ibc/client/cli/ibctx.go | 8 +- x/ibc/client/cli/relay.go | 17 +- x/ibc/client/rest/transfer.go | 26 +- x/slashing/client/cli/tx.go | 7 +- x/slashing/client/rest/tx.go | 30 +- x/staking/client/cli/tx.go | 41 +- x/staking/client/rest/tx.go | 90 +- 23 files changed, 1138 insertions(+), 1011 deletions(-) diff --git a/PENDING.md b/PENDING.md index 7f34780cb..1ce08770d 100644 --- a/PENDING.md +++ b/PENDING.md @@ -3,6 +3,8 @@ BREAKING CHANGES * Gaia REST API (`gaiacli advanced rest-server`) + * [\#3284](https://github.com/cosmos/cosmos-sdk/issues/3284) Rename the `name` + field to `from` in the `base_req` body. * Gaia CLI (`gaiacli`) - [#3399](https://github.com/cosmos/cosmos-sdk/pull/3399) Add `gaiad validate-genesis` command to facilitate checking of genesis files @@ -32,6 +34,11 @@ FEATURES IMPROVEMENTS * Gaia REST API + * [\#3284](https://github.com/cosmos/cosmos-sdk/issues/3284) Update Gaia Lite + REST service to support the following: + * Automatic account number and sequence population when fields are omitted + * Generate only functionality no longer requires access to a local Keybase + * `from` field in the `base_req` body can be a Keybase name or account address * Gaia CLI (`gaiacli`) diff --git a/client/context/context.go b/client/context/context.go index 6950122c4..a253a88fa 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -8,7 +8,9 @@ import ( "path/filepath" "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/codec" + cryptokeys "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/spf13/viper" @@ -19,14 +21,10 @@ import ( tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" - "github.com/cosmos/cosmos-sdk/client/keys" - cskeys "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) -var ( - verifier tmlite.Verifier -) +var verifier tmlite.Verifier // CLIContext implements a typical CLI context created in SDK modules for // transaction handling and queries. @@ -47,8 +45,8 @@ type CLIContext struct { Verifier tmlite.Verifier Simulate bool GenerateOnly bool - fromAddress types.AccAddress - fromName string + FromAddress sdk.AccAddress + FromName string Indent bool } @@ -63,7 +61,11 @@ func NewCLIContext() CLIContext { } from := viper.GetString(client.FlagFrom) - fromAddress, fromName := fromFields(from) + fromAddress, fromName, err := GetFromFields(from) + if err != nil { + fmt.Printf("failed to get from fields: %v", err) + os.Exit(1) + } // We need to use a single verifier for all contexts if verifier == nil { @@ -85,8 +87,8 @@ func NewCLIContext() CLIContext { Verifier: verifier, Simulate: viper.GetBool(client.FlagDryRun), GenerateOnly: viper.GetBool(client.FlagGenerateOnly), - fromAddress: fromAddress, - fromName: fromName, + FromAddress: fromAddress, + FromName: fromName, Indent: viper.GetBool(client.FlagIndentResponse), } } @@ -137,37 +139,6 @@ func createVerifier() tmlite.Verifier { return verifier } -func fromFields(from string) (fromAddr types.AccAddress, fromName string) { - if from == "" { - return nil, "" - } - - keybase, err := keys.GetKeyBase() - if err != nil { - fmt.Println("no keybase found") - os.Exit(1) - } - - var info cskeys.Info - if addr, err := types.AccAddressFromBech32(from); err == nil { - info, err = keybase.GetByAddress(addr) - if err != nil { - fmt.Printf("could not find key %s\n", from) - os.Exit(1) - } - } else { - info, err = keybase.Get(from) - if err != nil { - fmt.Printf("could not find key %s\n", from) - os.Exit(1) - } - } - - fromAddr = info.GetAddress() - fromName = info.GetName() - return -} - // WithCodec returns a copy of the context with an updated codec. func (ctx CLIContext) WithCodec(cdc *codec.Codec) CLIContext { ctx.Codec = cdc @@ -255,6 +226,19 @@ func (ctx CLIContext) WithSimulation(simulate bool) CLIContext { return ctx } +// WithFromName returns a copy of the context with an updated from account name. +func (ctx CLIContext) WithFromName(name string) CLIContext { + ctx.FromName = name + return ctx +} + +// WithFromAddress returns a copy of the context with an updated from account +// address. +func (ctx CLIContext) WithFromAddress(addr sdk.AccAddress) CLIContext { + ctx.FromAddress = addr + return ctx +} + // PrintOutput prints output while respecting output and indent flags // NOTE: pass in marshalled structs that have been unmarshaled // because this function will panic on marshaling errors @@ -279,3 +263,31 @@ func (ctx CLIContext) PrintOutput(toPrint fmt.Stringer) (err error) { fmt.Println(string(out)) return } + +// GetFromFields returns a from account address and Keybase name given either +// an address or key name. +func GetFromFields(from string) (sdk.AccAddress, string, error) { + if from == "" { + return nil, "", nil + } + + keybase, err := keys.GetKeyBase() + if err != nil { + return nil, "", err + } + + var info cryptokeys.Info + if addr, err := sdk.AccAddressFromBech32(from); err == nil { + info, err = keybase.GetByAddress(addr) + if err != nil { + return nil, "", err + } + } else { + info, err = keybase.Get(from) + if err != nil { + return nil, "", err + } + } + + return info.GetAddress(), info.GetName(), nil +} diff --git a/client/context/query.go b/client/context/query.go index aba8df190..cf70b3cea 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -82,13 +82,13 @@ func (ctx CLIContext) GetAccount(address []byte) (auth.Account, error) { } // GetFromAddress returns the from address from the context's name. -func (ctx CLIContext) GetFromAddress() (sdk.AccAddress, error) { - return ctx.fromAddress, nil +func (ctx CLIContext) GetFromAddress() sdk.AccAddress { + return ctx.FromAddress } // GetFromName returns the key name for the current context. -func (ctx CLIContext) GetFromName() (string, error) { - return ctx.fromName, nil +func (ctx CLIContext) GetFromName() string { + return ctx.FromName } // GetAccountNumber returns the next account number for the given account @@ -116,11 +116,7 @@ func (ctx CLIContext) GetAccountSequence(address []byte) (uint64, error) { // EnsureAccountExists ensures that an account exists for a given context. An // error is returned if it does not. func (ctx CLIContext) EnsureAccountExists() error { - addr, err := ctx.GetFromAddress() - if err != nil { - return err - } - + addr := ctx.GetFromAddress() accountBytes, err := ctx.QueryStore(auth.AddressStoreKey(addr), ctx.AccountStore) if err != nil { return err diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 0d680ada4..5e13cf474 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -2,7 +2,6 @@ package lcd import ( "encoding/hex" - "encoding/json" "fmt" "net/http" "os" @@ -26,6 +25,7 @@ import ( "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" @@ -211,16 +211,17 @@ func TestCoinSend(t *testing.T) { // run simulation and test success with estimated gas res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, "10000", 1.0, true, false, fees) require.Equal(t, http.StatusOK, res.StatusCode, body) - var responseBody struct { - GasEstimate int64 `json:"gas_estimate"` - } - require.Nil(t, json.Unmarshal([]byte(body), &responseBody)) + + var gasEstResp utils.GasEstimateResponse + require.Nil(t, cdc.UnmarshalJSON([]byte(body), &gasEstResp)) + require.NotZero(t, gasEstResp.GasEstimate) acc = getAccount(t, port, addr) require.Equal(t, expectedBalance.Amount, acc.GetCoins().AmountOf(stakingTypes.DefaultBondDenom)) - res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, - fmt.Sprintf("%d", responseBody.GasEstimate), 1.0, false, false, fees) + // run successful tx + gas := fmt.Sprintf("%d", gasEstResp.GasEstimate) + res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, gas, 1.0, false, false, fees) require.Equal(t, http.StatusOK, res.StatusCode, body) err = cdc.UnmarshalJSON([]byte(body), &resultTx) @@ -235,15 +236,67 @@ func TestCoinSend(t *testing.T) { require.Equal(t, expectedBalance.Amount.SubRaw(1), acc.GetCoins().AmountOf(stakingTypes.DefaultBondDenom)) } +func TestCoinSendAccAuto(t *testing.T) { + addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + defer cleanup() + + acc := getAccount(t, port, addr) + initialBalance := acc.GetCoins() + + // send a transfer tx without specifying account number and sequence + res, body, _ := doTransferWithGasAccAuto(t, port, seed, name1, memo, pw, "200000", 1.0, false, false, fees) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + // query sender + acc = getAccount(t, port, addr) + coins := acc.GetCoins() + expectedBalance := initialBalance[0].Minus(fees[0]) + + require.Equal(t, stakingTypes.DefaultBondDenom, coins[0].Denom) + require.Equal(t, expectedBalance.Amount.SubRaw(1), coins[0].Amount) +} + +func TestCoinSendGenerateOnly(t *testing.T) { + addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + defer cleanup() + + // generate only + res, body, _ := doTransferWithGas(t, port, seed, "", memo, "", addr, "200000", 1, false, true, fees) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var stdTx auth.StdTx + require.Nil(t, cdc.UnmarshalJSON([]byte(body), &stdTx)) + require.Equal(t, len(stdTx.Msgs), 1) + require.Equal(t, stdTx.GetMsgs()[0].Route(), "bank") + require.Equal(t, stdTx.GetMsgs()[0].GetSigners(), []sdk.AccAddress{addr}) + require.Equal(t, 0, len(stdTx.Signatures)) + require.Equal(t, memo, stdTx.Memo) + require.NotZero(t, stdTx.Fee.Gas) + require.IsType(t, stdTx.GetMsgs()[0], bank.MsgSend{}) + require.Equal(t, addr, stdTx.GetMsgs()[0].(bank.MsgSend).Inputs[0].Address) +} + func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() acc := getAccount(t, port, addr) - // generate TX - res, body, _ := doTransferWithGas(t, port, seed, name1, memo, "", addr, client.GasFlagAuto, 1, false, true, fees) + // simulate tx + res, body, _ := doTransferWithGas(t, port, seed, name1, memo, "", addr, client.GasFlagAuto, 1, true, false, fees) require.Equal(t, http.StatusOK, res.StatusCode, body) + + var gasEstResp utils.GasEstimateResponse + require.Nil(t, cdc.UnmarshalJSON([]byte(body), &gasEstResp)) + require.NotZero(t, gasEstResp.GasEstimate) + + // generate tx + gas := fmt.Sprintf("%d", gasEstResp.GasEstimate) + res, body, _ = doTransferWithGas(t, port, seed, name1, memo, "", addr, gas, 1, false, true, fees) + require.Equal(t, http.StatusOK, res.StatusCode, body) + var msg auth.StdTx require.Nil(t, cdc.UnmarshalJSON([]byte(body), &msg)) require.Equal(t, len(msg.Msgs), 1) @@ -251,6 +304,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { require.Equal(t, msg.Msgs[0].GetSigners(), []sdk.AccAddress{addr}) require.Equal(t, 0, len(msg.Signatures)) require.Equal(t, memo, msg.Memo) + require.NotZero(t, msg.Fee.Gas) gasEstimate := int64(msg.Fee.Gas) accnum := acc.GetAccountNumber() @@ -268,6 +322,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { } json, err := cdc.MarshalJSON(payload) require.Nil(t, err) + res, body = Request(t, port, "POST", "/tx/sign", json) require.Equal(t, http.StatusOK, res.StatusCode, body) require.Nil(t, cdc.UnmarshalJSON([]byte(body), &signedMsg)) diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index eed9d7e27..363345917 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -1,28 +1,28 @@ --- -swagger: '2.0' +swagger: "2.0" info: version: "3.0" title: Gaia-Lite for Cosmos description: A REST interface for state queries, transaction generation, signing, and broadcast. tags: -- name: ICS0 - description: Tendermint APIs, such as query blocks, transactions and validatorset -- name: ICS1 - description: Key management APIs -- name: ICS20 - description: Create, sign and broadcast transactions -- name: ICS21 - description: Stake module APIs -- name: ICS22 - description: Governance module APIs -- name: ICS23 - description: Slashing module APIs -- name: ICS24 - description: WIP - Fee distribution module APIs -- name: version - description: Query app version + - name: ICS0 + description: Tendermint APIs, such as query blocks, transactions and validatorset + - name: ICS1 + description: Key management APIs + - name: ICS20 + description: Create, sign and broadcast transactions + - name: ICS21 + description: Stake module APIs + - name: ICS22 + description: Governance module APIs + - name: ICS23 + description: Slashing module APIs + - name: ICS24 + description: WIP - Fee distribution module APIs + - name: version + description: Query app version schemes: -- https + - https host: fabo.interblock.io:1317 securityDefinitions: kms: @@ -32,7 +32,7 @@ paths: get: summary: Version of Gaia-lite tags: - - version + - version description: Get the version of gaia-lite running locally to compare against expected responses: 200: @@ -41,7 +41,7 @@ paths: get: summary: Version of the connected node tags: - - version + - version description: Get the version of the SDK running on the connected node to compare against expected responses: 200: @@ -53,9 +53,9 @@ paths: description: Information about the connected node summary: The properties of the connected node tags: - - ICS0 + - ICS0 produces: - - application/json + - application/json responses: 200: description: Node status @@ -90,7 +90,7 @@ paths: get: summary: Syncing state of node tags: - - ICS0 + - ICS0 description: Get if the node is currently syning with other nodes responses: 200: @@ -101,9 +101,9 @@ paths: get: summary: Get the latest block tags: - - ICS0 + - ICS0 produces: - - application/json + - application/json responses: 200: description: The latest block @@ -115,15 +115,15 @@ paths: get: summary: Get a block at a certain height tags: - - ICS0 + - ICS0 produces: - - application/json + - application/json parameters: - - in: path - name: height - description: Block height - required: true - type: number + - in: path + name: height + description: Block height + required: true + type: number responses: 200: description: The block at a specific height @@ -139,9 +139,9 @@ paths: get: summary: Get the latest validator set tags: - - ICS0 + - ICS0 produces: - - application/json + - application/json responses: 200: description: The validator set at the latest block height @@ -160,15 +160,15 @@ paths: get: summary: Get a validator set a certain height tags: - - ICS0 + - ICS0 produces: - - application/json + - application/json parameters: - - in: path - name: height - description: Block height - required: true - type: number + - in: path + name: height + description: Block height + required: true + type: number responses: 200: description: The validator set at a specific block height @@ -191,15 +191,15 @@ paths: get: summary: Get a Tx by hash tags: - - ICS0 + - ICS0 produces: - - application/json + - application/json parameters: - - in: path - name: hash - description: Tx hash - required: true - type: string + - in: path + name: hash + description: Tx hash + required: true + type: string responses: 200: description: Tx with the provided hash @@ -210,25 +210,25 @@ paths: /txs: get: tags: - - ICS0 + - ICS0 summary: Search transactions description: Search transactions by tag(s). produces: - - application/json + - application/json parameters: - - in: query - name: tag - type: string - description: "transaction tags such as 'action=submit-proposal' and 'proposer=cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc' which results in the following endpoint: 'GET /txs?action=submit-proposal&proposer=cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc'" - required: true - - in: query - name: page - description: Pagination page - type: integer - - in: query - name: size - description: Pagination size - type: integer + - in: query + name: tag + type: string + description: "transaction tags such as 'action=submit-proposal' and 'proposer=cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc' which results in the following endpoint: 'GET /txs?action=submit-proposal&proposer=cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc'" + required: true + - in: query + name: page + description: Pagination page + type: integer + - in: query + name: size + description: Pagination size + type: integer responses: 200: description: All txs matching the provided tags @@ -242,26 +242,26 @@ paths: description: Internal Server Error post: tags: - - ICS0 + - ICS0 summary: broadcast Tx description: broadcast tx with tendermint rpc consumes: - - application/json + - application/json produces: - - application/json + - application/json parameters: - - in: body - name: txBroadcast - description: Build a StdTx transaction and serilize it to a byte array with amino, then the `"tx"` field in the post body will be the base64 encoding of the byte array. The supported return types includes `"block"`(return after tx commit), `"sync"`(return afer CheckTx) and `"async"`(return right away). - required: true - schema: - type: object - properties: - tx: - type: string - return: - type: string - example: block + - in: body + name: txBroadcast + description: Build a StdTx transaction and serilize it to a byte array with amino, then the `"tx"` field in the post body will be the base64 encoding of the byte array. The supported return types includes `"block"`(return after tx commit), `"sync"`(return afer CheckTx) and `"async"`(return right away). + required: true + schema: + type: object + properties: + tx: + type: string + return: + type: string + example: block responses: 200: description: Broadcast tx result @@ -272,28 +272,28 @@ paths: /tx/sign: post: tags: - - ICS20 + - ICS20 summary: Sign a Tx description: Sign a Tx providing locally stored account and according password consumes: - - application/json + - application/json produces: - - application/json + - application/json parameters: - - in: body - name: sendToken - description: sign tx - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - tx: - $ref: "#/definitions/StdTx" - append_sig: - type: boolean - example: true + - in: body + name: sendToken + description: sign tx + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + tx: + $ref: "#/definitions/StdTx" + append_sig: + type: boolean + example: true responses: 200: description: The signed Tx @@ -308,23 +308,23 @@ paths: /tx/broadcast: post: tags: - - ICS20 + - ICS20 summary: Send a signed Tx description: Send a signed Tx to a Gaiad full node consumes: - - application/json + - application/json produces: - - application/json + - application/json parameters: - - in: body - name: txBroadcast - description: broadcast tx - required: true - schema: - type: object - properties: - tx: - $ref: "#/definitions/StdTx" + - in: body + name: txBroadcast + description: broadcast tx + required: true + schema: + type: object + properties: + tx: + $ref: "#/definitions/StdTx" responses: 202: description: Tx was send and will probably be added to the next block @@ -338,15 +338,15 @@ paths: get: summary: Get the account balances tags: - - ICS20 + - ICS20 produces: - - application/json + - application/json parameters: - - in: path - name: address - description: Account address in bech32 format - required: true - type: string + - in: path + name: address + description: Account address in bech32 format + required: true + type: string responses: 200: description: Account balances @@ -363,30 +363,30 @@ paths: summary: Send coins (build -> sign -> send) description: Send coins (build -> sign -> send) tags: - - ICS20 + - ICS20 consumes: - - application/json + - application/json produces: - - application/json + - application/json parameters: - - in: path - name: address - description: Account address in bech32 format - required: true - type: string - - in: body - name: account - description: The password of the account to remove from the KMS - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - amount: - type: array - items: - $ref: "#/definitions/Coin" + - in: path + name: address + description: Account address in bech32 format + required: true + type: string + - in: body + name: account + description: The password of the account to remove from the KMS + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + amount: + type: array + items: + $ref: "#/definitions/Coin" responses: 202: description: Tx was send and will probably be added to the next block @@ -402,43 +402,43 @@ paths: get: summary: List of accounts stored locally tags: - - ICS1 + - ICS1 produces: - - application/json + - application/json responses: 200: description: Array of accounts schema: type: array items: - $ref: '#/definitions/KeyOutput' + $ref: "#/definitions/KeyOutput" 500: description: Server internal error post: summary: Create a new account locally tags: - - ICS1 + - ICS1 consumes: - - application/json + - application/json produces: - - application/json + - application/json parameters: - - in: body - name: account - description: The account to create - schema: - type: object - required: - - name - - password - - seed - properties: - name: - type: string - password: - type: string - seed: - type: string + - in: body + name: account + description: The account to create + schema: + type: object + required: + - name + - password + - seed + properties: + name: + type: string + password: + type: string + seed: + type: string responses: 200: description: Returns account information of the created key @@ -454,7 +454,7 @@ paths: get: summary: Create a new seed to create a new account with tags: - - ICS1 + - ICS1 responses: 200: description: 24 word Seed @@ -465,30 +465,30 @@ paths: post: summary: Recover a account from a seed tags: - - ICS1 + - ICS1 consumes: - - application/json + - application/json produces: - - application/json + - application/json parameters: - - in: path - name: name - description: Account name - required: true - type: string - - in: body - name: pwdAndSeed - description: Provide password and seed to recover a key - schema: - type: object - required: - - password - - seed - properties: - password: - type: string - seed: - type: string + - in: path + name: name + description: Account name + required: true + type: string + - in: body + name: pwdAndSeed + description: Provide password and seed to recover a key + schema: + type: object + required: + - password + - seed + properties: + password: + type: string + seed: + type: string responses: 200: description: Returns account information of the recovered key @@ -502,17 +502,17 @@ paths: description: Server internal error /keys/{name}: parameters: - - in: path - name: name - description: Account name - required: true - type: string + - in: path + name: name + description: Account name + required: true + type: string get: summary: Get a certain locally stored account tags: - - ICS1 + - ICS1 produces: - - application/json + - application/json responses: 200: description: Locally stored account @@ -523,23 +523,23 @@ paths: put: summary: Update the password for this account in the KMS tags: - - ICS1 + - ICS1 consumes: - - application/json + - application/json parameters: - - in: body - name: account - description: The new and old password - schema: - type: object - required: - - new_password - - old_password - properties: - new_password: - type: string - old_password: - type: string + - in: body + name: account + description: The new and old password + schema: + type: object + required: + - new_password + - old_password + properties: + new_password: + type: string + old_password: + type: string responses: 200: description: Updated password @@ -550,20 +550,20 @@ paths: delete: summary: Remove an account tags: - - ICS1 + - ICS1 consumes: - - application/json + - application/json parameters: - - in: body - name: account - description: The password of the account to remove from the KMS - schema: - type: object - required: - - password - properties: - password: - type: string + - in: body + name: account + description: The password of the account to remove from the KMS + schema: + type: object + required: + - password + properties: + password: + type: string responses: 200: description: Removed account @@ -575,15 +575,15 @@ paths: get: summary: Get the account information on blockchain tags: - - ICS1 + - ICS1 produces: - - application/json + - application/json parameters: - - in: path - name: address - description: Account address - required: true - type: string + - in: path + name: address + description: Account address + required: true + type: string responses: 200: description: Account information on the blockchain @@ -613,17 +613,17 @@ paths: description: Server internel error /staking/delegators/{delegatorAddr}/delegations: parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string get: summary: Get all delegations from a delegator tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -638,26 +638,26 @@ paths: post: summary: Submit delegation parameters: - - in: body - name: delegation - description: The password of the account to remove from the KMS - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - delegator_addr: - $ref: "#/definitions/Address" - validator_addr: - $ref: "#/definitions/ValidatorAddress" - delegation: - $ref: "#/definitions/Coin" + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + delegator_addr: + $ref: "#/definitions/Address" + validator_addr: + $ref: "#/definitions/ValidatorAddress" + delegation: + $ref: "#/definitions/Coin" tags: - - ICS21 + - ICS21 consumes: - - application/json + - application/json produces: - - application/json + - application/json responses: 200: description: OK @@ -671,22 +671,22 @@ paths: description: Internal Server Error /staking/delegators/{delegatorAddr}/delegations/{validatorAddr}: parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string get: summary: Query the current delegation between a delegator and a validator tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -698,17 +698,17 @@ paths: description: Internal Server Error /staking/delegators/{delegatorAddr}/unbonding_delegations: parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string get: summary: Get all unbonding delegations from a delegator tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -723,37 +723,27 @@ paths: post: summary: Submit an unbonding delegation parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - - in: body - name: delegation - description: The password of the account to remove from the KMS - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - delegator_addr: - $ref: "#/definitions/Address" - validator_addr: - $ref: "#/definitions/ValidatorAddress" - shares: - type: string - example: "100" + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + delegator_addr: + $ref: "#/definitions/Address" + validator_addr: + $ref: "#/definitions/ValidatorAddress" + shares: + type: string + example: "100" tags: - - ICS21 + - ICS21 consumes: - - application/json + - application/json produces: - - application/json + - application/json responses: 200: description: OK @@ -767,22 +757,22 @@ paths: description: Internal Server Error /staking/delegators/{delegatorAddr}/unbonding_delegations/{validatorAddr}: parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string get: summary: Query all unbonding delegations between a delegator and a validator tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -796,27 +786,27 @@ paths: description: Internal Server Error /staking/redelegations: parameters: - - in: query - name: delegator - description: Bech32 AccAddress of Delegator - required: false - type: string - - in: query - name: validator_from - description: Bech32 ValAddress of SrcValidator - required: false - type: string - - in: query - name: validator_to - description: Bech32 ValAddress of DstValidator - required: false - type: string + - in: query + name: delegator + description: Bech32 AccAddress of Delegator + required: false + type: string + - in: query + name: validator_from + description: Bech32 ValAddress of SrcValidator + required: false + type: string + - in: query + name: validator_to + description: Bech32 ValAddress of DstValidator + required: false + type: string get: summary: Get all redelegations (filter by query params) tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -828,41 +818,31 @@ paths: description: Internal Server Error /staking/delegators/{delegatorAddr}/redelegations: parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string post: summary: Submit a redelegation parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - - in: body - name: delegation - description: The password of the account to remove from the KMS - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - delegator_addr: - $ref: "#/definitions/Address" - validator_src_addr: - $ref: "#/definitions/ValidatorAddress" - validator_dst_addr: - $ref: "#/definitions/ValidatorAddress" - shares: - type: string - example: "100" + - in: body + name: delegation + description: The password of the account to remove from the KMS + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + delegator_addr: + $ref: "#/definitions/Address" + validator_src_addr: + $ref: "#/definitions/ValidatorAddress" + validator_dst_addr: + $ref: "#/definitions/ValidatorAddress" + shares: + type: string + example: "100" tags: - ICS21 consumes: @@ -882,17 +862,17 @@ paths: description: Internal Server Error /staking/delegators/{delegatorAddr}/validators: parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string get: summary: Query all validators that a delegator is bonded to tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -906,22 +886,22 @@ paths: description: Internal Server Error /staking/delegators/{delegatorAddr}/validators/{validatorAddr}: parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - - in: path - name: validatorAddr - description: Bech32 ValAddress of Delegator - required: true - type: string + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + - in: path + name: validatorAddr + description: Bech32 ValAddress of Delegator + required: true + type: string get: summary: Query a validator that a delegator is bonded to tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -933,17 +913,17 @@ paths: description: Internal Server Error /staking/delegators/{delegatorAddr}/txs: parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string get: summary: Get all staking txs (i.e msgs) from a delegator tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -961,9 +941,9 @@ paths: get: summary: Get all validator candidates tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -975,17 +955,17 @@ paths: description: Internal Server Error /staking/validators/{validatorAddr}: parameters: - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string get: summary: Query the information from a single validator tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -997,17 +977,17 @@ paths: description: Internal Server Error /staking/validators/{validatorAddr}/delegations: parameters: - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string get: summary: Get all delegations from a validator tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -1021,17 +1001,17 @@ paths: description: Internal Server Error /staking/validators/{validatorAddr}/unbonding_delegations: parameters: - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string get: summary: Get all unbonding delegations from a validator tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -1047,9 +1027,9 @@ paths: get: summary: Get the current state of the staking pool tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -1074,9 +1054,9 @@ paths: get: summary: Get the current staking parameter values tags: - - ICS21 + - ICS21 produces: - - application/json + - application/json responses: 200: description: OK @@ -1104,15 +1084,15 @@ paths: summary: Get sign info of given validator description: Get sign info of given validator produces: - - application/json + - application/json tags: - - ICS23 + - ICS23 parameters: - - type: string - description: Bech32 validator public key - name: validatorPubKey - required: true - in: path + - type: string + description: Bech32 validator public key + name: validatorPubKey + required: true + in: path responses: 200: description: OK @@ -1138,26 +1118,26 @@ paths: summary: Unjail a jailed validator description: Send transaction to unjail a jailed validator consumes: - - application/json + - application/json produces: - - application/json + - application/json tags: - - ICS23 + - ICS23 parameters: - - type: string - description: Bech32 validator address - name: validatorAddr - required: true - in: path - - description: '' - name: UnjailBody - in: body - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" + - type: string + description: Bech32 validator address + name: validatorAddr + required: true + in: path + - description: "" + name: UnjailBody + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" responses: 200: description: OK @@ -1173,9 +1153,9 @@ paths: get: summary: Get the current slashing parameters tags: - - ICS23 + - ICS23 produces: - - application/json + - application/json responses: 200: description: OK @@ -1203,34 +1183,34 @@ paths: summary: Submit a proposal description: Send transaction to submit a proposal consumes: - - application/json + - application/json produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 parameters: - - description: valid value of `"proposal_type"` can be `"text"`, `"parameter_change"`, `"software_upgrade"` - name: post_proposal_body - in: body - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - title: - type: string - description: - type: string - proposal_type: - type: string - example: "text" - proposer: - $ref: "#/definitions/Address" - initial_deposit: - type: array - items: - $ref: "#/definitions/Coin" + - description: valid value of `"proposal_type"` can be `"text"`, `"parameter_change"`, `"software_upgrade"` + name: post_proposal_body + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + title: + type: string + description: + type: string + proposal_type: + type: string + example: "text" + proposer: + $ref: "#/definitions/Address" + initial_deposit: + type: array + items: + $ref: "#/definitions/Coin" responses: 200: description: OK @@ -1246,25 +1226,25 @@ paths: summary: Query proposals description: Query proposals information with parameters produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 parameters: - - in: query - name: voter - description: voter address - required: false - type: string - - in: query - name: depositor - description: depositor address - required: false - type: string - - in: query - name: status - description: proposal status, valid values can be `"deposit_period"`, `"voting_period"`, `"passed"`, `"rejected"` - required: false - type: string + - in: query + name: voter + description: voter address + required: false + type: string + - in: query + name: depositor + description: depositor address + required: false + type: string + - in: query + name: status + description: proposal status, valid values can be `"deposit_period"`, `"voting_period"`, `"passed"`, `"rejected"` + required: false + type: string responses: 200: description: OK @@ -1281,14 +1261,14 @@ paths: summary: Query a proposal description: Query a proposal by id produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 parameters: - - type: string - name: proposalId - required: true - in: path + - type: string + name: proposalId + required: true + in: path responses: 200: description: OK @@ -1303,14 +1283,14 @@ paths: summary: Query proposer description: Query for the proposer for a proposal produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 parameters: - - type: string - name: proposalId - required: true - in: path + - type: string + name: proposalId + required: true + in: path responses: 200: description: OK @@ -1325,14 +1305,14 @@ paths: summary: Query deposits description: Query deposits by proposalId produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 parameters: - - type: string - name: proposalId - required: true - in: path + - type: string + name: proposalId + required: true + in: path responses: 200: description: OK @@ -1348,32 +1328,32 @@ paths: summary: Deposit tokens to a proposal description: Send transaction to deposit tokens to a proposal consumes: - - application/json + - application/json produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - - description: '' - name: post_deposit_body - in: body - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - depositor: - $ref: "#/definitions/Address" - amount: - type: array - items: - $ref: "#/definitions/Coin" + - type: string + description: proposal id + name: proposalId + required: true + in: path + - description: "" + name: post_deposit_body + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + depositor: + $ref: "#/definitions/Address" + amount: + type: array + items: + $ref: "#/definitions/Coin" responses: 200: description: OK @@ -1390,20 +1370,20 @@ paths: summary: Query deposit description: Query deposit by proposalId and depositor address produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - - type: string - description: Bech32 depositor address - name: depositor - required: true - in: path + - type: string + description: proposal id + name: proposalId + required: true + in: path + - type: string + description: Bech32 depositor address + name: depositor + required: true + in: path responses: 200: description: OK @@ -1420,15 +1400,15 @@ paths: summary: Query voters description: Query voters information by proposalId produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path + - type: string + description: proposal id + name: proposalId + required: true + in: path responses: 200: description: OK @@ -1444,31 +1424,31 @@ paths: summary: Vote a proposal description: Send transaction to vote a proposal consumes: - - application/json + - application/json produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - - description: valid value of `"option"` field can be `"yes"`, `"no"`, `"no_with_veto"` and `"abstain"` - name: post_vote_body - in: body - required: true - schema: - type: object - properties: - base_req: - $ref: "#/definitions/BaseReq" - voter: - $ref: "#/definitions/Address" - option: - type: string - example: "yes" + - type: string + description: proposal id + name: proposalId + required: true + in: path + - description: valid value of `"option"` field can be `"yes"`, `"no"`, `"no_with_veto"` and `"abstain"` + name: post_vote_body + in: body + required: true + schema: + type: object + properties: + base_req: + $ref: "#/definitions/BaseReq" + voter: + $ref: "#/definitions/Address" + option: + type: string + example: "yes" responses: 200: description: OK @@ -1485,20 +1465,20 @@ paths: summary: Query vote description: Query vote information by proposal Id and voter address produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path - - type: string - description: Bech32 voter address - name: voter - required: true - in: path + - type: string + description: proposal id + name: proposalId + required: true + in: path + - type: string + description: Bech32 voter address + name: voter + required: true + in: path responses: 200: description: OK @@ -1515,15 +1495,15 @@ paths: summary: Get a proposal's tally result at the current time description: Gets a proposal's tally result at the current time. If the proposal is pending deposits (i.e status 'DepositPeriod') it returns an empty tally result. produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 parameters: - - type: string - description: proposal id - name: proposalId - required: true - in: path + - type: string + description: proposal id + name: proposalId + required: true + in: path responses: 200: description: OK @@ -1538,9 +1518,9 @@ paths: summary: Query governance deposit parameters description: Query governance deposit parameters. The max_deposit_period units are in nanoseconds. produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 responses: 200: description: OK @@ -1565,9 +1545,9 @@ paths: summary: Query governance tally parameters description: Query governance tally parameters produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 responses: 200: description: OK @@ -1593,9 +1573,9 @@ paths: summary: Query governance voting parameters description: Query governance voting parameters. The voting_period units are in nanoseconds. produces: - - application/json + - application/json tags: - - ICS22 + - ICS22 responses: 200: description: OK @@ -1612,18 +1592,18 @@ paths: description: Internal Server Error /distribution/delegators/{delegatorAddr}/rewards: parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string get: summary: Get the total rewards balance from all delegations description: Get the sum of all the rewards earned by delegations by a single delegator produces: - - application/json + - application/json tags: - - ICS24 + - ICS24 responses: 200: description: OK @@ -1639,28 +1619,18 @@ paths: summary: Withdraw all the delegator's delegation rewards description: Withdraw all the delegator's delegation rewards tags: - - ICS24 + - ICS24 consumes: - - application/json + - application/json produces: - - application/json + - application/json parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - - in: body - name: Withdraw request body - schema: - properties: - base_req: - $ref: "#/definitions/BaseReq" + - in: body + name: Withdraw request body + schema: + properties: + base_req: + $ref: "#/definitions/BaseReq" responses: 200: description: OK @@ -1674,23 +1644,23 @@ paths: description: Internal Server Error /distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}: parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string - - in: path - name: validatorAddr - description: Bech32 OperatorAddress of validator - required: true - type: string + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string + - in: path + name: validatorAddr + description: Bech32 OperatorAddress of validator + required: true + type: string get: summary: Query a delegation reward description: Query a single delegation reward by a delegator tags: - - ICS24 + - ICS24 produces: - - application/json + - application/json responses: 200: description: OK @@ -1706,28 +1676,18 @@ paths: summary: Withdraw a delegation reward description: Withdraw a delegator's delegation reward from a single validator tags: - - ICS24 + - ICS24 consumes: - - application/json + - application/json produces: - - application/json + - application/json parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - - in: body - name: Withdraw request body - schema: - properties: - base_req: - $ref: "#/definitions/BaseReq" + - in: body + name: Withdraw request body + schema: + properties: + base_req: + $ref: "#/definitions/BaseReq" responses: 200: description: OK @@ -1741,18 +1701,18 @@ paths: description: Internal Server Error /distribution/delegators/{delegatorAddr}/withdraw_address: parameters: - - in: path - name: delegatorAddr - description: Bech32 AccAddress of Delegator - required: true - type: string + - in: path + name: delegatorAddr + description: Bech32 AccAddress of Delegator + required: true + type: string get: summary: Get the rewards withdrawal address description: Get the delegations' rewards withdrawal address. This is the address in which the user will receive the reward funds tags: - - ICS24 + - ICS24 produces: - - application/json + - application/json responses: 200: description: OK @@ -1766,30 +1726,20 @@ paths: summary: Replace the rewards withdrawal address description: Replace the delegations' rewards withdrawal address for a new one. tags: - - ICS24 + - ICS24 consumes: - - application/json + - application/json produces: - - application/json + - application/json parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - - in: body - name: Withdraw request body - schema: - properties: - base_req: - $ref: "#/definitions/BaseReq" - withdraw_address: - $ref: "#/definitions/Address" + - in: body + name: Withdraw request body + schema: + properties: + base_req: + $ref: "#/definitions/BaseReq" + withdraw_address: + $ref: "#/definitions/Address" responses: 200: description: OK @@ -1812,9 +1762,9 @@ paths: summary: Validator distribution information description: Query the distribution information of a single validator tags: - - ICS24 + - ICS24 produces: - - application/json + - application/json responses: 200: description: OK @@ -1835,9 +1785,9 @@ paths: summary: Commission and self-delegation rewards of a single a validator description: Query the commission and self-delegation rewards of a validator. tags: - - ICS24 + - ICS24 produces: - - application/json + - application/json responses: 200: description: OK @@ -1853,28 +1803,18 @@ paths: summary: Withdraw the validator's rewards description: Withdraw the validator's self-delegation and commissions rewards tags: - - ICS24 + - ICS24 consumes: - - application/json + - application/json produces: - - application/json + - application/json parameters: - - in: query - name: simulate - description: if true, ignore the gas field and perform a simulation of a transaction, but don't broadcast it - required: false - type: boolean - - in: query - name: generate_only - description: if true, build an unsigned transaction and write it back - required: false - type: boolean - - in: body - name: Withdraw request body - schema: - properties: - base_req: - $ref: "#/definitions/BaseReq" + - in: body + name: Withdraw request body + schema: + properties: + base_req: + $ref: "#/definitions/BaseReq" responses: 200: description: OK @@ -1890,9 +1830,9 @@ paths: get: summary: Fee distribution parameters tags: - - ICS24 + - ICS24 produces: - - application/json + - application/json responses: 200: description: OK @@ -1910,9 +1850,9 @@ paths: get: summary: Fee distribution pool tags: - - ICS24 + - ICS24 produces: - - application/json + - application/json responses: 200: description: OK @@ -1948,8 +1888,8 @@ definitions: gas_wanted: 10000 info: info tags: - - '' - - '' + - "" + - "" DeliverTxResult: type: object properties: @@ -1977,8 +1917,8 @@ definitions: gas_wanted: 10000 info: info tags: - - '' - - '' + - "" + - "" BroadcastTxCommitResult: type: object properties: @@ -2123,7 +2063,7 @@ definitions: example: 1 time: type: string - example: '2017-12-30T05:53:09.287+01:00' + example: "2017-12-30T05:53:09.287+01:00" num_txs: type: number example: 0 @@ -2186,7 +2126,7 @@ definitions: example: "0" timestamp: type: string - example: '2017-12-30T05:53:09.287+01:00' + example: "2017-12-30T05:53:09.287+01:00" type: type: number example: 2 @@ -2194,7 +2134,7 @@ definitions: $ref: "#/definitions/BlockID" signature: type: string - example: '7uTC74QlknqYWEwg7Vn6M8Om7FuZ0EO4bjvuj6rwH1mTUJrRuMMZvAAqT9VjNgP0RA/TDp6u/92AqrZfXJSpBQ==' + example: "7uTC74QlknqYWEwg7Vn6M8Om7FuZ0EO4bjvuj6rwH1mTUJrRuMMZvAAqT9VjNgP0RA/TDp6u/92AqrZfXJSpBQ==" BlockQuery: type: object properties: @@ -2210,9 +2150,10 @@ definitions: BaseReq: type: object properties: - name: + from: type: string - example: "my_name" + example: "cosmos1g9ahr6xhht5rmqven628nklxluzyv8z9jqjcmc" + description: Sender address or Keybase name to generate a transaction password: type: string example: "12345678" @@ -2238,21 +2179,19 @@ definitions: type: array items: $ref: "#/definitions/Coin" - gas_prices: - type: array - items: - $ref: "#/definitions/DecCoin" generate_only: type: boolean example: false + description: Create a JSON transaction that can be signed client side instead of actually signing and broadcasting simulate: type: boolean - example: true + example: false + description: Estimate gas for a transaction (cannot be used in conjunction with generate_only) TendermintValidator: type: object properties: address: - $ref: '#/definitions/ValidatorAddress' + $ref: "#/definitions/ValidatorAddress" pub_key: type: string example: cosmosvalconspub1zcjduepq7sjfglw7ra4mjxpw4ph7dtdhdheh7nz8dfgl6t8u2n5szuuql9mqsrwquu @@ -2276,7 +2215,7 @@ definitions: proposal_status: type: string final_tally_result: - $ref: "#/definitions/TallyResult" + $ref: "#/definitions/TallyResult" submit_time: type: string total_deposit: @@ -2331,7 +2270,7 @@ definitions: type: object properties: operator_address: - $ref: '#/definitions/ValidatorAddress' + $ref: "#/definitions/ValidatorAddress" consensus_pubkey: type: string example: cosmosvalconspub1zcjduepq7sjfglw7ra4mjxpw4ph7dtdhdheh7nz8dfgl6t8u2n5szuuql9mqsrwquu @@ -2356,31 +2295,31 @@ definitions: type: string bond_height: type: string - example: '0' + example: "0" bond_intra_tx_counter: type: integer example: 0 unbonding_height: type: string - example: '0' + example: "0" unbonding_time: type: string - example: '1970-01-01T00:00:00Z' + example: "1970-01-01T00:00:00Z" commission: type: object properties: rate: type: string - example: '0' + example: "0" max_rate: type: string - example: '0' + example: "0" max_change_rate: type: string - example: '0' + example: "0" update_time: type: string - example: '1970-01-01T00:00:00Z' + example: "1970-01-01T00:00:00Z" Delegation: type: object properties: @@ -2434,13 +2373,13 @@ definitions: community_pool: type: array items: - $ref: '#/definitions/Coin' + $ref: "#/definitions/Coin" val_accum: - $ref: '#/definitions/TotalAccum' + $ref: "#/definitions/TotalAccum" val_pool: type: array items: - $ref: '#/definitions/Coin' + $ref: "#/definitions/Coin" TotalAccum: type: object properties: @@ -2452,16 +2391,16 @@ definitions: type: object properties: operator_addr: - $ref: '#/definitions/ValidatorAddress' + $ref: "#/definitions/ValidatorAddress" fee_pool_withdrawal_height: type: integer del_accum: - $ref: '#/definitions/TotalAccum' + $ref: "#/definitions/TotalAccum" del_pool: type: array items: - $ref: '#/definitions/Coin' + $ref: "#/definitions/Coin" val_commission: type: array items: - $ref: '#/definitions/Coin' + $ref: "#/definitions/Coin" diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index da41e196b..71c2a38d6 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -688,23 +688,32 @@ func doTransfer(t *testing.T, port, seed, name, memo, password string, addr sdk. return receiveAddr, resultTx } -func doTransferWithGas(t *testing.T, port, seed, name, memo, password string, addr sdk.AccAddress, gas string, - gasAdjustment float64, simulate, generateOnly bool, fees sdk.Coins) ( - res *http.Response, body string, receiveAddr sdk.AccAddress) { +func doTransferWithGas( + t *testing.T, port, seed, from, memo, password string, addr sdk.AccAddress, + gas string, gasAdjustment float64, simulate, generateOnly bool, fees sdk.Coins, +) (res *http.Response, body string, receiveAddr sdk.AccAddress) { // create receive address kb := client.MockKeyBase() - receiveInfo, _, err := kb.CreateMnemonic("receive_address", cryptoKeys.English, gapp.DefaultKeyPass, cryptoKeys.SigningAlgo("secp256k1")) - require.Nil(t, err) - receiveAddr = sdk.AccAddress(receiveInfo.GetPubKey().Address()) + receiveInfo, _, err := kb.CreateMnemonic( + "receive_address", cryptoKeys.English, gapp.DefaultKeyPass, cryptoKeys.SigningAlgo("secp256k1"), + ) + require.Nil(t, err) + + receiveAddr = sdk.AccAddress(receiveInfo.GetPubKey().Address()) acc := getAccount(t, port, addr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) + if generateOnly { + // generate only txs do not use a Keybase so the address must be used + from = addr.String() + } + baseReq := utils.NewBaseReq( - name, password, memo, chainID, gas, + from, password, memo, chainID, gas, fmt.Sprintf("%f", gasAdjustment), accnum, sequence, fees, nil, generateOnly, simulate, ) @@ -721,6 +730,39 @@ func doTransferWithGas(t *testing.T, port, seed, name, memo, password string, ad return } +func doTransferWithGasAccAuto( + t *testing.T, port, seed, from, memo, password string, gas string, + gasAdjustment float64, simulate, generateOnly bool, fees sdk.Coins, +) (res *http.Response, body string, receiveAddr sdk.AccAddress) { + + // create receive address + kb := client.MockKeyBase() + + receiveInfo, _, err := kb.CreateMnemonic( + "receive_address", cryptoKeys.English, gapp.DefaultKeyPass, cryptoKeys.SigningAlgo("secp256k1"), + ) + require.Nil(t, err) + + receiveAddr = sdk.AccAddress(receiveInfo.GetPubKey().Address()) + chainID := viper.GetString(client.FlagChainID) + + baseReq := utils.NewBaseReq( + from, password, memo, chainID, gas, + fmt.Sprintf("%f", gasAdjustment), 0, 0, fees, nil, generateOnly, simulate, + ) + + sr := sendReq{ + Amount: sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 1)}, + BaseReq: baseReq, + } + + req, err := cdc.MarshalJSON(sr) + require.NoError(t, err) + + res, body = Request(t, port, "POST", fmt.Sprintf("/bank/accounts/%s/transfers", receiveAddr), req) + return +} + type sendReq struct { Amount sdk.Coins `json:"amount"` BaseReq utils.BaseReq `json:"base_req"` diff --git a/client/utils/rest.go b/client/utils/rest.go index f6bd18a4f..44fc746ad 100644 --- a/client/utils/rest.go +++ b/client/utils/rest.go @@ -16,7 +16,12 @@ import ( authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" ) -//---------------------------------------- +// GasEstimateResponse defines a response definition for tx gas estimation. +type GasEstimateResponse struct { + GasEstimate uint64 `json:"gas_estimate"` +} + +//----------------------------------------------------------------------------- // Basic HTTP utilities // WriteErrorResponse prepares and writes a HTTP error @@ -28,9 +33,17 @@ func WriteErrorResponse(w http.ResponseWriter, status int, err string) { // WriteSimulationResponse prepares and writes an HTTP // response for transactions simulations. -func WriteSimulationResponse(w http.ResponseWriter, gas uint64) { +func WriteSimulationResponse(w http.ResponseWriter, cdc *codec.Codec, gas uint64) { + gasEst := GasEstimateResponse{GasEstimate: gas} + resp, err := cdc.MarshalJSON(gasEst) + if err != nil { + WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) - w.Write([]byte(fmt.Sprintf(`{"gas_estimate":%v}`, gas))) + w.Write(resp) } // ParseInt64OrReturnBadRequest converts s to a int64 value. @@ -77,31 +90,13 @@ func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEm return n, true } -// WriteGenerateStdTxResponse writes response for the generate_only mode. -func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec, txBldr authtxb.TxBuilder, msgs []sdk.Msg) { - stdMsg, err := txBldr.Build(msgs) - if err != nil { - WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return - } - - output, err := cdc.MarshalJSON(auth.NewStdTx(stdMsg.Msgs, stdMsg.Fee, nil, stdMsg.Memo)) - if err != nil { - WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) - return - } - - w.Write(output) - return -} - -//---------------------------------------- +//----------------------------------------------------------------------------- // Building / Sending utilities // BaseReq defines a structure that can be embedded in other request structures // that all share common "base" fields. type BaseReq struct { - Name string `json:"name"` + From string `json:"from"` Password string `json:"password"` Memo string `json:"memo"` ChainID string `json:"chain_id"` @@ -117,12 +112,12 @@ type BaseReq struct { // NewBaseReq creates a new basic request instance and sanitizes its values func NewBaseReq( - name, password, memo, chainID string, gas, gasAdjustment string, + from, password, memo, chainID string, gas, gasAdjustment string, accNumber, seq uint64, fees sdk.Coins, gasPrices sdk.DecCoins, genOnly, simulate bool, ) BaseReq { return BaseReq{ - Name: strings.TrimSpace(name), + From: strings.TrimSpace(from), Password: password, Memo: strings.TrimSpace(memo), ChainID: strings.TrimSpace(chainID), @@ -140,7 +135,7 @@ func NewBaseReq( // Sanitize performs basic sanitization on a BaseReq object. func (br BaseReq) Sanitize() BaseReq { return NewBaseReq( - br.Name, br.Password, br.Memo, br.ChainID, br.Gas, br.GasAdjustment, + br.From, br.Password, br.Memo, br.ChainID, br.Gas, br.GasAdjustment, br.AccountNumber, br.Sequence, br.Fees, br.GasPrices, br.GenerateOnly, br.Simulate, ) } @@ -171,8 +166,8 @@ func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { } } - if len(br.Name) == 0 { - WriteErrorResponse(w, http.StatusUnauthorized, "name required but not specified") + if len(br.From) == 0 { + WriteErrorResponse(w, http.StatusUnauthorized, "name or address required but not specified") return false } @@ -209,59 +204,65 @@ func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req i } // CompleteAndBroadcastTxREST implements a utility function that facilitates -// sending a series of messages in a signed transaction given a TxBuilder and a -// QueryContext. It ensures that the account exists, has a proper number and -// sequence set. In addition, it builds and signs a transaction with the -// supplied messages. Finally, it broadcasts the signed transaction to a node. +// sending a series of messages in a signed tx. In addition, it will handle +// tx gas simulation and estimation. // -// NOTE: Also see CompleteAndBroadcastTxCli. -// NOTE: Also see x/stake/client/rest/tx.go delegationsRequestHandlerFn. +// NOTE: Also see CompleteAndBroadcastTxCLI. func CompleteAndBroadcastTxREST( w http.ResponseWriter, r *http.Request, cliCtx context.CLIContext, baseReq BaseReq, msgs []sdk.Msg, cdc *codec.Codec, ) { - gasAdjustment, ok := ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment) + gasAdj, ok := ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment) if !ok { return } - simulateAndExecute, gas, err := client.ParseGas(baseReq.Gas) + simAndExec, gas, err := client.ParseGas(baseReq.Gas) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } + // derive the from account address and name from the Keybase + fromAddress, fromName, err := context.GetFromFields(baseReq.From) + if err != nil { + WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) txBldr := authtxb.NewTxBuilder( GetTxEncoder(cdc), baseReq.AccountNumber, - baseReq.Sequence, gas, gasAdjustment, baseReq.Simulate, + baseReq.Sequence, gas, gasAdj, baseReq.Simulate, baseReq.ChainID, baseReq.Memo, baseReq.Fees, baseReq.GasPrices, ) - if baseReq.Simulate || simulateAndExecute { - if gasAdjustment < 0 { + txBldr, err = prepareTxBuilder(txBldr, cliCtx) + if err != nil { + WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + if baseReq.Simulate || simAndExec { + if gasAdj < 0 { WriteErrorResponse(w, http.StatusBadRequest, "gas adjustment must be a positive float") return } - txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, msgs) + txBldr, err = EnrichWithGas(txBldr, cliCtx, cliCtx.GetFromName(), msgs) if err != nil { WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } if baseReq.Simulate { - WriteSimulationResponse(w, txBldr.GetGas()) + WriteSimulationResponse(w, cdc, txBldr.GetGas()) return } } - if baseReq.GenerateOnly { - WriteGenerateStdTxResponse(w, cdc, txBldr, msgs) - return - } - - txBytes, err := txBldr.BuildAndSign(baseReq.Name, baseReq.Password, msgs) + txBytes, err := txBldr.BuildAndSign(cliCtx.GetFromName(), baseReq.Password, msgs) if keyerror.IsErrKeyNotFound(err) { WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -282,9 +283,10 @@ func CompleteAndBroadcastTxREST( PostProcessResponse(w, cdc, res, cliCtx.Indent) } -// PostProcessResponse performs post process for rest response +// PostProcessResponse performs post processing for a REST response. func PostProcessResponse(w http.ResponseWriter, cdc *codec.Codec, response interface{}, indent bool) { var output []byte + switch response.(type) { default: var err error @@ -300,6 +302,41 @@ func PostProcessResponse(w http.ResponseWriter, cdc *codec.Codec, response inter case []byte: output = response.([]byte) } + w.Header().Set("Content-Type", "application/json") w.Write(output) } + +// WriteGenerateStdTxResponse writes response for the generate only mode. +func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec, br BaseReq, msgs []sdk.Msg) { + gasAdj, ok := ParseFloat64OrReturnBadRequest(w, br.GasAdjustment, client.DefaultGasAdjustment) + if !ok { + return + } + + _, gas, err := client.ParseGas(br.Gas) + if err != nil { + WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txBldr := authtxb.NewTxBuilder( + GetTxEncoder(cdc), br.AccountNumber, br.Sequence, gas, gasAdj, + br.Simulate, br.ChainID, br.Memo, br.Fees, br.GasPrices, + ) + + stdMsg, err := txBldr.Build(msgs) + if err != nil { + WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + output, err := cdc.MarshalJSON(auth.NewStdTx(stdMsg.Msgs, stdMsg.Fee, nil, stdMsg.Memo)) + if err != nil { + WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + w.Write(output) + return +} diff --git a/client/utils/utils.go b/client/utils/utils.go index 92ce82b99..a30a54cd1 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -18,26 +18,23 @@ import ( authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" ) -// CompleteAndBroadcastTxCli implements a utility function that facilitates +// CompleteAndBroadcastTxCLI implements a utility function that facilitates // sending a series of messages in a signed transaction given a TxBuilder and a // QueryContext. It ensures that the account exists, has a proper number and // sequence set. In addition, it builds and signs a transaction with the // supplied messages. Finally, it broadcasts the signed transaction to a node. // // NOTE: Also see CompleteAndBroadcastTxREST. -func CompleteAndBroadcastTxCli(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) error { +func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) error { txBldr, err := prepareTxBuilder(txBldr, cliCtx) if err != nil { return err } - name, err := cliCtx.GetFromName() - if err != nil { - return err - } + name := cliCtx.GetFromName() if txBldr.GetSimulateAndExecute() || cliCtx.Simulate { - txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs) + txBldr, err = EnrichWithGas(txBldr, cliCtx, name, msgs) if err != nil { return err } @@ -63,9 +60,9 @@ func CompleteAndBroadcastTxCli(txBldr authtxb.TxBuilder, cliCtx context.CLIConte return err } -// EnrichCtxWithGas calculates the gas estimate that would be consumed by the +// EnrichWithGas calculates the gas estimate that would be consumed by the // transaction and set the transaction's respective value accordingly. -func EnrichCtxWithGas(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (authtxb.TxBuilder, error) { +func EnrichWithGas(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (authtxb.TxBuilder, error) { _, adjusted, err := simulateMsgs(txBldr, cliCtx, name, msgs) if err != nil { return txBldr, err @@ -236,10 +233,7 @@ func prepareTxBuilder(txBldr authtxb.TxBuilder, cliCtx context.CLIContext) (auth return txBldr, err } - from, err := cliCtx.GetFromAddress() - if err != nil { - return txBldr, err - } + from := cliCtx.GetFromAddress() // TODO: (ref #1903) Allow for user supplied account number without // automatically doing a manual lookup. @@ -275,22 +269,21 @@ func buildUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msg func buildUnsignedStdTxOffline(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx auth.StdTx, err error) { if txBldr.GetSimulateAndExecute() { - var name string - name, err = cliCtx.GetFromName() + name := cliCtx.GetFromName() + + txBldr, err = EnrichWithGas(txBldr, cliCtx, name, msgs) if err != nil { return } - txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs) - if err != nil { - return - } fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.GetGas()) } + stdSignMsg, err := txBldr.Build(msgs) if err != nil { return } + return auth.NewStdTx(stdSignMsg.Msgs, stdSignMsg.Fee, nil, stdSignMsg.Memo), nil } @@ -300,5 +293,6 @@ func isTxSigner(user sdk.AccAddress, signers []sdk.AccAddress) bool { return true } } + return false } diff --git a/docs/clients/service-providers.md b/docs/clients/service-providers.md index 358031b96..68024d641 100644 --- a/docs/clients/service-providers.md +++ b/docs/clients/service-providers.md @@ -105,10 +105,19 @@ The recommended way to listen for incoming transaction is to periodically query ## Rest API -The Rest API documents all the available endpoints that you can use to interract with your full node. It can be found [here](https://cosmos.network/rpc/). +The Rest API documents all the available endpoints that you can use to interact +with your full node. It can be found [here](https://cosmos.network/rpc/). -The API is divided into ICS standards for each category of endpoints. For example, the [ICS20](https://cosmos.network/rpc/#/ICS20/) describes the API to interact with tokens. +The API is divided into ICS standards for each category of endpoints. For +example, the [ICS20](https://cosmos.network/rpc/#/ICS20/) describes the API to +interact with tokens. -To give more flexibility to implementers, we have included the ability to generate unsigned transactions, [sign](https://cosmos.network/rpc/#/ICS20/post_tx_sign) and [broadcast](https://cosmos.network/rpc/#/ICS20/post_tx_broadcast) them with different API endpoints. This allows service providers to use their own signing mechanism for instance. +To give more flexibility to implementers, we have included the ability to +generate unsigned transactions, [sign](https://cosmos.network/rpc/#/ICS20/post_tx_sign) +and [broadcast](https://cosmos.network/rpc/#/ICS20/post_tx_broadcast) them with +different API endpoints. This allows service providers to use their own signing +mechanism for instance. -In order to generate an unsigned transaction (example with [coin transfer](https://cosmos.network/rpc/#/ICS20/post_bank_accounts__address__transfers)), you need to use the flag `?generate_only`. +In order to generate an unsigned transaction (example with +[coin transfer](https://cosmos.network/rpc/#/ICS20/post_bank_accounts__address__transfers)), +you need to use the field `generate_only` in the body of `base_req`. diff --git a/docs/gaia/gaiacli.md b/docs/gaia/gaiacli.md index 5982c30d4..0b1bafd26 100644 --- a/docs/gaia/gaiacli.md +++ b/docs/gaia/gaiacli.md @@ -209,7 +209,8 @@ You can also check your balance at a given block by using the `--block` flag: gaiacli query account --block= ``` -You can simulate a transaction without actually broadcasting it by appending the `--dry-run` flag to the command line: +You can simulate a transaction without actually broadcasting it by appending the +`--dry-run` flag to the command line: ```bash gaiacli tx send \ @@ -220,7 +221,8 @@ gaiacli tx send \ --dry-run ``` -Furthermore, you can build a transaction and print its JSON format to STDOUT by appending `--generate-only` to the list of the command line arguments: +Furthermore, you can build a transaction and print its JSON format to STDOUT by +appending `--generate-only` to the list of the command line arguments: ```bash gaiacli tx send \ @@ -231,7 +233,14 @@ gaiacli tx send \ --generate-only > unsignedSendTx.json ``` -You can now sign the transaction file generated through the `--generate-only` flag by providing your key to the following command: +::: tip Note +Simulation cannot be used in conjunction with tx generation only functionality +due to the fact that simulation requires a public key and generation only does +not utilize a Keybase. + +You can now sign the transaction file generated through the `--generate-only` +flag by providing your key to the following command: +::: ```bash gaiacli tx sign \ diff --git a/x/auth/client/rest/sign.go b/x/auth/client/rest/sign.go index 7f7787ea4..11b8c083b 100644 --- a/x/auth/client/rest/sign.go +++ b/x/auth/client/rest/sign.go @@ -41,6 +41,15 @@ func SignTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha return } + // derive the from account address and name from the Keybase + fromAddress, fromName, err := context.GetFromFields(m.BaseReq.From) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) + txBldr := authtxb.NewTxBuilder( utils.GetTxEncoder(cdc), m.BaseReq.AccountNumber, @@ -54,7 +63,7 @@ func SignTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha nil, ) - signedTx, err := txBldr.SignStdTx(m.BaseReq.Name, m.BaseReq.Password, m.Tx, m.AppendSig) + signedTx, err := txBldr.SignStdTx(cliCtx.GetFromName(), m.BaseReq.Password, m.Tx, m.AppendSig) if keyerror.IsErrKeyNotFound(err) { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return diff --git a/x/bank/client/cli/sendtx.go b/x/bank/client/cli/sendtx.go index 161261276..9da232789 100644 --- a/x/bank/client/cli/sendtx.go +++ b/x/bank/client/cli/sendtx.go @@ -50,11 +50,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { return err } - from, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - + from := cliCtx.GetFromAddress() account, err := cliCtx.GetAccount(from) if err != nil { return err @@ -71,7 +67,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index d73d7e61e..d087e9316 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" - "github.com/cosmos/cosmos-sdk/x/bank/client" + bankclient "github.com/cosmos/cosmos-sdk/x/bank/client" "github.com/gorilla/mux" ) @@ -37,7 +37,7 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC vars := mux.Vars(r) bech32Addr := vars["address"] - to, err := sdk.AccAddressFromBech32(bech32Addr) + toAddr, err := sdk.AccAddressFromBech32(bech32Addr) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -54,13 +54,30 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC return } - info, err := kb.Get(req.BaseReq.Name) + if req.BaseReq.GenerateOnly { + // When generate only is supplied, the from field must be a valid Bech32 + // address. + fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + msg := bankclient.CreateMsg(fromAddr, toAddr, req.Amount) + utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + return + } + + // derive the from account address and name from the Keybase + fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) if err != nil { utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - msg := client.CreateMsg(sdk.AccAddress(info.GetPubKey().Address()), to, req.Amount) + cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) + msg := bankclient.CreateMsg(cliCtx.GetFromAddress(), toAddr, req.Amount) + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index 67113c2b9..9a761d8de 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -63,18 +63,11 @@ func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command { var msg sdk.Msg switch { case isVal: - addr, err := cliCtx.GetFromAddress() - if err != nil { - return err - } + addr := cliCtx.GetFromAddress() valAddr := sdk.ValAddress(addr.Bytes()) msg = types.NewMsgWithdrawValidatorCommission(valAddr) default: - delAddr, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - + delAddr := cliCtx.GetFromAddress() valAddr, err := sdk.ValAddressFromBech32(onlyFromVal) if err != nil { return err @@ -88,7 +81,7 @@ func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command { } // build and sign the transaction, then broadcast to Tendermint - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } cmd.Flags().String(flagOnlyFromValidator, "", "only withdraw from this validator address (in bech)") @@ -109,11 +102,7 @@ func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command { WithCodec(cdc). WithAccountDecoder(cdc) - delAddr, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - + delAddr := cliCtx.GetFromAddress() withdrawAddr, err := sdk.AccAddressFromBech32(args[0]) if err != nil { return err @@ -122,7 +111,7 @@ func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command { msg := types.NewMsgSetWithdrawAddress(delAddr, withdrawAddr) // build and sign the transaction, then broadcast to Tendermint - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } return cmd diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 89deae745..69e82abf6 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -86,10 +86,7 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome WithAccountDecoder(cdc) // Get from address - from, err := cliCtx.GetFromAddress() - if err != nil { - return err - } + from := cliCtx.GetFromAddress() // Pull associated account account, err := cliCtx.GetAccount(from) @@ -126,7 +123,7 @@ $ gaiacli gov submit-proposal --title="Test Proposal" --description="My awesome // Build and sign the transaction, then broadcast to Tendermint // proposalID must be returned, and it is a part of response. cliCtx.PrintResponse = true - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } @@ -199,11 +196,7 @@ $ gaiacli tx gov deposit 1 10stake --from mykey return fmt.Errorf("Failed to fetch proposal-id %d: %s", proposalID, err) } - // Get from address - from, err := cliCtx.GetFromAddress() - if err != nil { - return err - } + from := cliCtx.GetFromAddress() // Fetch associated account account, err := cliCtx.GetAccount(from) @@ -233,7 +226,7 @@ $ gaiacli tx gov deposit 1 10stake --from mykey } // Build and sign the transaction, then broadcast to a Tendermint node. - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } @@ -258,10 +251,7 @@ $ gaiacli tx gov vote 1 yes --from mykey WithAccountDecoder(cdc) // Get voting address - from, err := cliCtx.GetFromAddress() - if err != nil { - return err - } + from := cliCtx.GetFromAddress() // validate that the proposal id is a uint proposalID, err := strconv.ParseUint(args[0], 10, 64) @@ -294,7 +284,7 @@ $ gaiacli tx gov vote 1 yes --from mykey } // Build and sign the transaction, then broadcast to a Tendermint node. - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 96142941e..b39d2f700 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -101,6 +101,11 @@ func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han return } + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + return + } + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } @@ -140,6 +145,11 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF return } + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + return + } + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } @@ -185,6 +195,11 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc return } + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + return + } + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } diff --git a/x/ibc/client/cli/ibctx.go b/x/ibc/client/cli/ibctx.go index 2bea7cc80..22e70f6a9 100644 --- a/x/ibc/client/cli/ibctx.go +++ b/x/ibc/client/cli/ibctx.go @@ -32,11 +32,7 @@ func IBCTransferCmd(cdc *codec.Codec) *cobra.Command { WithCodec(cdc). WithAccountDecoder(cdc) - from, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - + from := cliCtx.GetFromAddress() msg, err := buildMsg(from) if err != nil { return err @@ -45,7 +41,7 @@ func IBCTransferCmd(cdc *codec.Codec) *cobra.Command { return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index f9928c8ef..231e0917f 100644 --- a/x/ibc/client/cli/relay.go +++ b/x/ibc/client/cli/relay.go @@ -82,11 +82,7 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) { toChainID := viper.GetString(FlagToChainID) toChainNode := viper.GetString(FlagToChainNode) - address, err := context.NewCLIContext().GetFromAddress() - if err != nil { - panic(err) - } - + address := context.NewCLIContext().GetFromAddress() c.address = address c.loop(fromChainID, fromChainNode, toChainID, toChainNode) @@ -96,10 +92,7 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) { func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) { cliCtx := context.NewCLIContext() - name, err := cliCtx.GetFromName() - if err != nil { - panic(err) - } + name := cliCtx.GetFromName() passphrase, err := keys.ReadPassphraseFromStdin(name) if err != nil { panic(err) @@ -207,11 +200,7 @@ func (c relayCommander) refine(bz []byte, ibcSeq, accSeq uint64, passphrase stri txBldr := authtxb.NewTxBuilderFromCLI().WithSequence(accSeq).WithTxEncoder(utils.GetTxEncoder(c.cdc)) cliCtx := context.NewCLIContext() - name, err := cliCtx.GetFromName() - if err != nil { - panic(err) - } - + name := cliCtx.GetFromName() res, err := txBldr.BuildAndSign(name, passphrase, []sdk.Msg{msg}) if err != nil { panic(err) diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index ab26b6e42..a92f9ea19 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -48,18 +48,28 @@ func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. return } - info, err := kb.Get(req.BaseReq.Name) - if err != nil { - utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) - return + var fromAddr sdk.AccAddress + + if req.BaseReq.GenerateOnly { + // When generate only is supplied, the from field must be a valid Bech32 + // address. + addr, err := sdk.AccAddressFromBech32(req.BaseReq.From) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + fromAddr = addr } - packet := ibc.NewIBCPacket( - sdk.AccAddress(info.GetPubKey().Address()), to, - req.Amount, req.BaseReq.ChainID, destChainID, - ) + packet := ibc.NewIBCPacket(fromAddr, to, req.Amount, req.BaseReq.ChainID, destChainID) msg := ibc.IBCTransferMsg{IBCPacket: packet} + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + return + } + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } diff --git a/x/slashing/client/cli/tx.go b/x/slashing/client/cli/tx.go index 4fc1c9cde..968a03e91 100644 --- a/x/slashing/client/cli/tx.go +++ b/x/slashing/client/cli/tx.go @@ -25,16 +25,13 @@ func GetCmdUnjail(cdc *codec.Codec) *cobra.Command { WithCodec(cdc). WithAccountDecoder(cdc) - valAddr, err := cliCtx.GetFromAddress() - if err != nil { - return err - } + valAddr := cliCtx.GetFromAddress() msg := slashing.NewMsgUnjail(sdk.ValAddress(valAddr)) if cliCtx.GenerateOnly { return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index add8c6665..1c4266aa1 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -43,24 +43,38 @@ func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CL return } - info, err := kb.Get(req.BaseReq.Name) - if err != nil { - utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) - return - } - valAddr, err := sdk.ValAddressFromBech32(bech32validator) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - if !bytes.Equal(info.GetPubKey().Address(), valAddr) { + msg := slashing.NewMsgUnjail(valAddr) + err = msg.ValidateBasic() + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + return + } + + // derive the from account address and name from the Keybase + fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) + + if !bytes.Equal(cliCtx.GetFromAddress(), valAddr) { utils.WriteErrorResponse(w, http.StatusUnauthorized, "must use own validator address") return } - msg := slashing.NewMsgUnjail(valAddr) utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } diff --git a/x/staking/client/cli/tx.go b/x/staking/client/cli/tx.go index 4a2ed6eb4..6838cc0e0 100644 --- a/x/staking/client/cli/tx.go +++ b/x/staking/client/cli/tx.go @@ -39,7 +39,7 @@ func GetCmdCreateValidator(cdc *codec.Codec) *cobra.Command { } // build and sign the transaction, then broadcast to Tendermint - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } @@ -71,11 +71,7 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { WithCodec(cdc). WithAccountDecoder(cdc) - valAddr, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - + valAddr := cliCtx.GetFromAddress() description := staking.Description{ Moniker: viper.GetString(FlagMoniker), Identity: viper.GetString(FlagIdentity), @@ -102,7 +98,7 @@ func GetCmdEditValidator(cdc *codec.Codec) *cobra.Command { } // build and sign the transaction, then broadcast to Tendermint - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } @@ -128,11 +124,7 @@ func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { return err } - delAddr, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - + delAddr := cliCtx.GetFromAddress() valAddr, err := sdk.ValAddressFromBech32(viper.GetString(FlagAddressValidator)) if err != nil { return err @@ -144,7 +136,7 @@ func GetCmdDelegate(cdc *codec.Codec) *cobra.Command { return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // build and sign the transaction, then broadcast to Tendermint - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } @@ -167,11 +159,7 @@ func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { var err error - delAddr, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - + delAddr := cliCtx.GetFromAddress() valSrcAddr, err := sdk.ValAddressFromBech32(viper.GetString(FlagAddressValidatorSrc)) if err != nil { return err @@ -199,7 +187,7 @@ func GetCmdRedelegate(storeName string, cdc *codec.Codec) *cobra.Command { return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // build and sign the transaction, then broadcast to Tendermint - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } @@ -220,11 +208,7 @@ func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command { WithCodec(cdc). WithAccountDecoder(cdc) - delAddr, err := cliCtx.GetFromAddress() - if err != nil { - return err - } - + delAddr := cliCtx.GetFromAddress() valAddr, err := sdk.ValAddressFromBech32(viper.GetString(FlagAddressValidator)) if err != nil { return err @@ -247,7 +231,7 @@ func GetCmdUnbond(storeName string, cdc *codec.Codec) *cobra.Command { return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } // build and sign the transaction, then broadcast to Tendermint - return utils.CompleteAndBroadcastTxCli(txBldr, cliCtx, []sdk.Msg{msg}) + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, []sdk.Msg{msg}) }, } @@ -265,12 +249,9 @@ func BuildCreateValidatorMsg(cliCtx context.CLIContext, txBldr authtxb.TxBuilder return txBldr, nil, err } - valAddr, err := cliCtx.GetFromAddress() - if err != nil { - return txBldr, nil, err - } - + valAddr := cliCtx.GetFromAddress() pkStr := viper.GetString(FlagPubKey) + pk, err := sdk.GetConsPubKeyBech32(pkStr) if err != nil { return txBldr, nil, err diff --git a/x/staking/client/rest/tx.go b/x/staking/client/rest/tx.go index 73d86ce42..26b821f1d 100644 --- a/x/staking/client/rest/tx.go +++ b/x/staking/client/rest/tx.go @@ -68,17 +68,6 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. return } - info, err := kb.Get(req.BaseReq.Name) - if err != nil { - utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) - return - } - - if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") - return - } - msg := staking.NewMsgDelegate(req.DelegatorAddr, req.ValidatorAddr, req.Delegation) err = msg.ValidateBasic() if err != nil { @@ -86,6 +75,25 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. return } + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + return + } + + // derive the from account address and name from the Keybase + fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) + + if !bytes.Equal(cliCtx.GetFromAddress(), req.DelegatorAddr) { + utils.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + return + } + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } @@ -105,17 +113,6 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex return } - info, err := kb.Get(req.BaseReq.Name) - if err != nil { - utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) - return - } - - if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") - return - } - msg := staking.NewMsgBeginRedelegate(req.DelegatorAddr, req.ValidatorSrcAddr, req.ValidatorDstAddr, req.SharesAmount) err = msg.ValidateBasic() if err != nil { @@ -123,6 +120,25 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex return } + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + return + } + + // derive the from account address and name from the Keybase + fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) + + if !bytes.Equal(cliCtx.GetFromAddress(), req.DelegatorAddr) { + utils.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + return + } + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } @@ -142,17 +158,6 @@ func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx return } - info, err := kb.Get(req.BaseReq.Name) - if err != nil { - utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) - return - } - - if !bytes.Equal(info.GetPubKey().Address(), req.DelegatorAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "Must use own delegator address") - return - } - msg := staking.NewMsgUndelegate(req.DelegatorAddr, req.ValidatorAddr, req.SharesAmount) err = msg.ValidateBasic() if err != nil { @@ -160,6 +165,25 @@ func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx return } + if req.BaseReq.GenerateOnly { + utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + return + } + + // derive the from account address and name from the Keybase + fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) + + if !bytes.Equal(cliCtx.GetFromAddress(), req.DelegatorAddr) { + utils.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + return + } + utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } From f66b7b6310eaa1b3406dc81b436040d804930a59 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 29 Jan 2019 21:28:41 +0100 Subject: [PATCH 19/99] Merge PR #3435: Assert store implementations never store nil values * Check non-nil values on IAVL store * Reuse assertValidValue --- PENDING.md | 1 + store/cachekvstore.go | 23 ++++------------------- store/cachekvstore_test.go | 6 ++++++ store/gaskvstore.go | 1 + store/gaskvstore_test.go | 5 +++++ store/iavlstore.go | 1 + store/iavlstore_test.go | 7 +++++++ store/prefixstore.go | 1 + store/prefixstore_test.go | 7 +++++++ store/validity.go | 13 +++++++++++++ 10 files changed, 46 insertions(+), 19 deletions(-) create mode 100644 store/validity.go diff --git a/PENDING.md b/PENDING.md index 1ce08770d..7814facca 100644 --- a/PENDING.md +++ b/PENDING.md @@ -47,6 +47,7 @@ IMPROVEMENTS genesis validation checks to `GaiaValidateGenesisState`. * SDK + * \#3435 Test that store implementations do not allow nil values * Tendermint diff --git a/store/cachekvstore.go b/store/cachekvstore.go index 4860566c4..28c344fd0 100644 --- a/store/cachekvstore.go +++ b/store/cachekvstore.go @@ -44,7 +44,7 @@ func (ci *cacheKVStore) GetStoreType() StoreType { func (ci *cacheKVStore) Get(key []byte) (value []byte) { ci.mtx.Lock() defer ci.mtx.Unlock() - ci.assertValidKey(key) + assertValidKey(key) cacheValue, ok := ci.cache[string(key)] if !ok { @@ -61,8 +61,8 @@ func (ci *cacheKVStore) Get(key []byte) (value []byte) { func (ci *cacheKVStore) Set(key []byte, value []byte) { ci.mtx.Lock() defer ci.mtx.Unlock() - ci.assertValidKey(key) - ci.assertValidValue(value) + assertValidKey(key) + assertValidValue(value) ci.setCacheValue(key, value, false, true) } @@ -77,7 +77,7 @@ func (ci *cacheKVStore) Has(key []byte) bool { func (ci *cacheKVStore) Delete(key []byte) { ci.mtx.Lock() defer ci.mtx.Unlock() - ci.assertValidKey(key) + assertValidKey(key) ci.setCacheValue(key, nil, true, true) } @@ -189,21 +189,6 @@ func (ci *cacheKVStore) dirtyItems(start, end []byte, ascending bool) []cmn.KVPa return items } -//---------------------------------------- -// etc - -func (ci *cacheKVStore) assertValidKey(key []byte) { - if key == nil { - panic("key is nil") - } -} - -func (ci *cacheKVStore) assertValidValue(value []byte) { - if value == nil { - panic("value is nil") - } -} - // Only entrypoint to mutate ci.cache. func (ci *cacheKVStore) setCacheValue(key, value []byte, deleted bool, dirty bool) { ci.cache[string(key)] = cValue{ diff --git a/store/cachekvstore_test.go b/store/cachekvstore_test.go index 37e0364fb..8f43b0b3b 100644 --- a/store/cachekvstore_test.go +++ b/store/cachekvstore_test.go @@ -60,6 +60,12 @@ func TestCacheKVStore(t *testing.T) { require.Empty(t, mem.Get(keyFmt(1)), "Expected `key1` to be empty") } +func TestCacheKVStoreNoNilSet(t *testing.T) { + mem := dbStoreAdapter{dbm.NewMemDB()} + st := NewCacheKVStore(mem) + require.Panics(t, func() { st.Set([]byte("key"), nil) }, "setting a nil value should panic") +} + func TestCacheKVStoreNested(t *testing.T) { mem := dbStoreAdapter{dbm.NewMemDB()} st := NewCacheKVStore(mem) diff --git a/store/gaskvstore.go b/store/gaskvstore.go index 1100f8433..790ae612f 100644 --- a/store/gaskvstore.go +++ b/store/gaskvstore.go @@ -45,6 +45,7 @@ func (gs *gasKVStore) Get(key []byte) (value []byte) { // Implements KVStore. func (gs *gasKVStore) Set(key []byte, value []byte) { + assertValidValue(value) gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostFlat, sdk.GasWriteCostFlatDesc) // TODO overflow-safe math? gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostPerByte*sdk.Gas(len(value)), sdk.GasWritePerByteDesc) diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go index 2426cf334..c3a3e3928 100644 --- a/store/gaskvstore_test.go +++ b/store/gaskvstore_test.go @@ -29,6 +29,11 @@ func TestGasKVStoreBasic(t *testing.T) { require.Equal(t, meter.GasConsumed(), sdk.Gas(6429)) } +func TestGasKVStoreNoNilSet(t *testing.T) { + st := newGasKVStore() + require.Panics(t, func() { st.Set([]byte("key"), nil) }, "setting a nil value should panic") +} + func TestGasKVStoreIterator(t *testing.T) { mem := dbStoreAdapter{dbm.NewMemDB()} meter := sdk.NewGasMeter(10000) diff --git a/store/iavlstore.go b/store/iavlstore.go index 26c739da3..9bb6a8cb1 100644 --- a/store/iavlstore.go +++ b/store/iavlstore.go @@ -128,6 +128,7 @@ func (st *iavlStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap // Implements KVStore. func (st *iavlStore) Set(key, value []byte) { + assertValidValue(value) st.tree.Set(key, value) } diff --git a/store/iavlstore_test.go b/store/iavlstore_test.go index 4d8605e4e..78140ba32 100644 --- a/store/iavlstore_test.go +++ b/store/iavlstore_test.go @@ -69,6 +69,13 @@ func TestIAVLStoreGetSetHasDelete(t *testing.T) { require.False(t, exists) } +func TestIAVLStoreNoNilSet(t *testing.T) { + db := dbm.NewMemDB() + tree, _ := newAlohaTree(t, db) + iavlStore := newIAVLStore(tree, numRecent, storeEvery) + require.Panics(t, func() { iavlStore.Set([]byte("key"), nil) }, "setting a nil value should panic") +} + func TestIAVLIterator(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) diff --git a/store/prefixstore.go b/store/prefixstore.go index 1f52a99fe..66a08f0eb 100644 --- a/store/prefixstore.go +++ b/store/prefixstore.go @@ -61,6 +61,7 @@ func (s prefixStore) Has(key []byte) bool { // Implements KVStore func (s prefixStore) Set(key, value []byte) { + assertValidValue(value) s.parent.Set(s.key(key), value) } diff --git a/store/prefixstore_test.go b/store/prefixstore_test.go index 48e6cd17f..4a4253af4 100644 --- a/store/prefixstore_test.go +++ b/store/prefixstore_test.go @@ -95,6 +95,13 @@ func TestGasKVStorePrefix(t *testing.T) { testPrefixStore(t, gasStore, []byte("test")) } +func TestPrefixKVStoreNoNilSet(t *testing.T) { + meter := sdk.NewGasMeter(100000000) + mem := dbStoreAdapter{dbm.NewMemDB()} + gasStore := NewGasKVStore(meter, sdk.KVGasConfig(), mem) + require.Panics(t, func() { gasStore.Set([]byte("key"), nil) }, "setting a nil value should panic") +} + func TestPrefixStoreIterate(t *testing.T) { db := dbm.NewMemDB() baseStore := dbStoreAdapter{db} diff --git a/store/validity.go b/store/validity.go new file mode 100644 index 000000000..5a20f1fb3 --- /dev/null +++ b/store/validity.go @@ -0,0 +1,13 @@ +package store + +func assertValidKey(key []byte) { + if key == nil { + panic("key is nil") + } +} + +func assertValidValue(value []byte) { + if value == nil { + panic("value is nil") + } +} From 14dcaa64581579fcf85f4314ed47b04e28954ad7 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Tue, 29 Jan 2019 13:45:30 -0800 Subject: [PATCH 20/99] Merge PR #3421: Upgrade ledger-cosmos-go dep to 0.9.3 * Upgrading to ledger-cosmos-go * Add cli test and update PENDING.md --- Gopkg.lock | 6 +++--- Gopkg.toml | 2 +- PENDING.md | 3 +++ cmd/gaia/cli_test/cli_test.go | 8 ++++++++ cmd/gaia/cli_test/test_helpers.go | 6 ++++++ 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index fa9eec6a6..97fcf6734 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -522,12 +522,12 @@ revision = "v0.29.0" [[projects]] - digest = "1:a7485b2a69f996923f9d3406a9a853fd8eb31818515e985a830d71f88f6a925b" + digest = "1:29b886f00694ae7c18c4559a2901f2a057d5a62308ed5eb6cd52b9a31016fb14" name = "github.com/zondax/ledger-cosmos-go" packages = ["."] pruneopts = "UT" - revision = "d4aed6d929a703bb555a2d79fe9c470afe61f648" - version = "v0.9.2" + revision = "71aa0ab6e03d2d320c82bbe13678a36584a5b813" + version = "v0.9.3" [[projects]] digest = "1:6f6dc6060c4e9ba73cf28aa88f12a69a030d3d19d518ef8e931879eaa099628d" diff --git a/Gopkg.toml b/Gopkg.toml index 54a64d419..1bf115d61 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -44,7 +44,7 @@ [[constraint]] name = "github.com/zondax/ledger-cosmos-go" - version = "=v0.9.2" + version = "=v0.9.3" ## deps without releases: diff --git a/PENDING.md b/PENDING.md index 7814facca..09daf7658 100644 --- a/PENDING.md +++ b/PENDING.md @@ -58,6 +58,9 @@ BUG FIXES * Gaia CLI (`gaiacli`) - [\#3417](https://github.com/cosmos/cosmos-sdk/pull/3417) Fix `q slashing signing-info` panic by ensuring safety of user input and properly returning not found error + - [\#3345](https://github.com/cosmos/cosmos-sdk/issues/3345) Upgrade ledger-cosmos-go dependency to v0.9.3 to pull + https://github.com/ZondaX/ledger-cosmos-go/commit/ed9aa39ce8df31bad1448c72d3d226bf2cb1a8d1 in order to fix a derivation path issue that causes `gaiacli keys add --recover` + to malfunction. - [\#3419](https://github.com/cosmos/cosmos-sdk/pull/3419) Fix `q distr slashes` panic * Gaia diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 845f4c95d..6e2e60536 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -45,6 +45,14 @@ func TestGaiaCLIKeysAddMultisig(t *testing.T) { require.NotEqual(t, f.KeysShow("msig3").Address, f.KeysShow("msig4").Address) } +func TestGaiaCLIKeysAddRecover(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + f.KeysAddRecover("test-recover", "dentist task convince chimney quality leave banana trade firm crawl eternal easily") + require.Equal(t, f.KeyAddress("test-recover").String(), "cosmos1qcfdf69js922qrdr4yaww3ax7gjml6pdds46f4") +} + func TestGaiaCLIMinimumFees(t *testing.T) { t.Parallel() f := InitFixtures(t) diff --git a/cmd/gaia/cli_test/test_helpers.go b/cmd/gaia/cli_test/test_helpers.go index 9b42e592b..b2288382f 100644 --- a/cmd/gaia/cli_test/test_helpers.go +++ b/cmd/gaia/cli_test/test_helpers.go @@ -226,6 +226,12 @@ func (f *Fixtures) KeysAdd(name string, flags ...string) { executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } +// KeysAdd is gaiacli keys add --recover +func (f *Fixtures) KeysAddRecover(name, mnemonic string, flags ...string) { + cmd := fmt.Sprintf("gaiacli keys add --home=%s --recover %s", f.GCLIHome, name) + executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass, mnemonic) +} + // KeysShow is gaiacli keys show func (f *Fixtures) KeysShow(name string, flags ...string) keys.KeyOutput { cmd := fmt.Sprintf("gaiacli keys show --home=%s %s", f.GCLIHome, name) From 172a4510c74cf026b79e142684e75b6d676cfde1 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Tue, 29 Jan 2019 14:23:25 -0800 Subject: [PATCH 21/99] Add length caps for governance proposal titles and descriptions (#3434) --- PENDING.md | 1 + x/gov/errors.go | 8 ++++---- x/gov/msgs.go | 13 +++++++++++-- x/gov/msgs_test.go | 3 +++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/PENDING.md b/PENDING.md index 09daf7658..7fc54004b 100644 --- a/PENDING.md +++ b/PENDING.md @@ -45,6 +45,7 @@ IMPROVEMENTS * Gaia * [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account genesis validation checks to `GaiaValidateGenesisState`. + * [\#3420](https://github.com/cosmos/cosmos-sdk/issues/3420) Added maximum length to governance proposal descriptions and titles * SDK * \#3435 Test that store implementations do not allow nil values diff --git a/x/gov/errors.go b/x/gov/errors.go index 9b9aa25db..0bf904d33 100644 --- a/x/gov/errors.go +++ b/x/gov/errors.go @@ -46,12 +46,12 @@ func ErrAddressNotStaked(codespace sdk.CodespaceType, address sdk.AccAddress) sd return sdk.NewError(codespace, CodeAddressNotStaked, fmt.Sprintf("Address %s is not staked and is thus ineligible to vote", address)) } -func ErrInvalidTitle(codespace sdk.CodespaceType, title string) sdk.Error { - return sdk.NewError(codespace, CodeInvalidTitle, fmt.Sprintf("Proposal Title '%s' is not valid", title)) +func ErrInvalidTitle(codespace sdk.CodespaceType, errorMsg string) sdk.Error { + return sdk.NewError(codespace, CodeInvalidTitle, errorMsg) } -func ErrInvalidDescription(codespace sdk.CodespaceType, description string) sdk.Error { - return sdk.NewError(codespace, CodeInvalidDescription, fmt.Sprintf("Proposal Desciption '%s' is not valid", description)) +func ErrInvalidDescription(codespace sdk.CodespaceType, errorMsg string) sdk.Error { + return sdk.NewError(codespace, CodeInvalidDescription, errorMsg) } func ErrInvalidProposalType(codespace sdk.CodespaceType, proposalType ProposalKind) sdk.Error { diff --git a/x/gov/msgs.go b/x/gov/msgs.go index 3be7ab583..534316e4f 100644 --- a/x/gov/msgs.go +++ b/x/gov/msgs.go @@ -11,6 +11,9 @@ const ( TypeMsgDeposit = "deposit" TypeMsgVote = "vote" TypeMsgSubmitProposal = "submit_proposal" + + MaxDescriptionLength int = 5000 + MaxTitleLength int = 140 ) var _, _, _ sdk.Msg = MsgSubmitProposal{}, MsgDeposit{}, MsgVote{} @@ -42,10 +45,16 @@ func (msg MsgSubmitProposal) Type() string { return TypeMsgSubmitProposal } // Implements Msg. func (msg MsgSubmitProposal) ValidateBasic() sdk.Error { if len(msg.Title) == 0 { - return ErrInvalidTitle(DefaultCodespace, msg.Title) // TODO: Proper Error + return ErrInvalidTitle(DefaultCodespace, "No title present in proposal") + } + if len(msg.Title) > MaxTitleLength { + return ErrInvalidTitle(DefaultCodespace, fmt.Sprintf("Proposal title is longer than max length of %d", MaxTitleLength)) } if len(msg.Description) == 0 { - return ErrInvalidDescription(DefaultCodespace, msg.Description) // TODO: Proper Error + return ErrInvalidDescription(DefaultCodespace, "No description present in proposal") + } + if len(msg.Description) > MaxDescriptionLength { + return ErrInvalidDescription(DefaultCodespace, fmt.Sprintf("Proposal description is longer than max length of %d", MaxDescriptionLength)) } if !validProposalType(msg.ProposalType) { return ErrInvalidProposalType(DefaultCodespace, msg.ProposalType) diff --git a/x/gov/msgs_test.go b/x/gov/msgs_test.go index 83881834f..3c4472137 100644 --- a/x/gov/msgs_test.go +++ b/x/gov/msgs_test.go @@ -1,6 +1,7 @@ package gov import ( + "strings" "testing" "github.com/stretchr/testify/require" @@ -40,6 +41,8 @@ func TestMsgSubmitProposal(t *testing.T) { {"Test Proposal", "the purpose of this proposal is to test", ProposalTypeText, sdk.AccAddress{}, coinsPos, false}, {"Test Proposal", "the purpose of this proposal is to test", ProposalTypeText, addrs[0], coinsZero, true}, {"Test Proposal", "the purpose of this proposal is to test", ProposalTypeText, addrs[0], coinsMulti, true}, + {strings.Repeat("#", MaxTitleLength*2), "the purpose of this proposal is to test", ProposalTypeText, addrs[0], coinsMulti, false}, + {"Test Proposal", strings.Repeat("#", MaxDescriptionLength*2), ProposalTypeText, addrs[0], coinsMulti, false}, } for i, tc := range tests { From 0ed6de0cbd7367ce17a3e42711fcab54025b04db Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Tue, 29 Jan 2019 14:25:43 -0800 Subject: [PATCH 22/99] Merge PR #3426: Various changes to version cmd, revert those which previously broke ABI * version prints out short info by default Handle -o json, add --long flag to print full version info. * Add distclean target to Makefile * Update PENDING.md * Add missing targets in .PHONY --- Makefile | 5 ++++- PENDING.md | 1 + baseapp/baseapp.go | 2 +- client/rpc/root.go | 2 +- version/command.go | 45 +++++++++++++++++++++++++++++++-------------- version/version.go | 25 +++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 96be180fd..037366c41 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,9 @@ draw_deps: tools clean: rm -f devtools-stamp vendor-deps snapcraft.yaml +distclean: clean + rm -rf vendor/ + ######################################## ### Documentation @@ -250,7 +253,7 @@ build-snap-edge: snapcraft.yaml # To avoid unintended conflicts with file names, always add to .PHONY # unless there is a reason not to. # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html -.PHONY: build install install_debug dist \ +.PHONY: build install install_debug dist clean distclean \ check_tools check_dev_tools get_vendor_deps draw_deps test test_cli test_unit \ test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \ build-linux build-docker-gaiadnode localnet-start localnet-stop \ diff --git a/PENDING.md b/PENDING.md index 7fc54004b..d2a365fde 100644 --- a/PENDING.md +++ b/PENDING.md @@ -8,6 +8,7 @@ BREAKING CHANGES * Gaia CLI (`gaiacli`) - [#3399](https://github.com/cosmos/cosmos-sdk/pull/3399) Add `gaiad validate-genesis` command to facilitate checking of genesis files + - [\#1894](https://github.com/cosmos/cosmos-sdk/issues/1894) `version` prints out short info by default. Add `--long` flag. Proper handling of `--format` flag introduced. * Gaia diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index cfeb97214..950125180 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -396,7 +396,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc return abci.ResponseQuery{ Code: uint32(sdk.CodeOK), Codespace: string(sdk.CodespaceRoot), - Value: []byte(version.GetVersion()), + Value: []byte(version.Version), } default: result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() diff --git a/client/rpc/root.go b/client/rpc/root.go index cb25b47f3..83ee51786 100644 --- a/client/rpc/root.go +++ b/client/rpc/root.go @@ -26,7 +26,7 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router) { // cli version REST handler endpoint func CLIVersionRequestHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - w.Write([]byte(fmt.Sprintf("{\"version\": \"%s\"}", version.GetVersion()))) + w.Write([]byte(fmt.Sprintf("{\"version\": \"%s\"}", version.Version))) } // connected node version REST handler endpoint diff --git a/version/command.go b/version/command.go index 49cf1e021..c3ff71df1 100644 --- a/version/command.go +++ b/version/command.go @@ -1,31 +1,48 @@ package version import ( + "encoding/json" "fmt" - "runtime" "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/tendermint/tendermint/libs/cli" +) + +const ( + flagLong = "long" ) var ( + // VersionCmd prints out the current sdk version VersionCmd = &cobra.Command{ Use: "version", Short: "Print the app version", - Run: printVersion, + RunE: func(_ *cobra.Command, _ []string) error { + verInfo := newVersionInfo() + + if !viper.GetBool(flagLong) { + fmt.Println(verInfo.CosmosSDK) + return nil + } + + if viper.GetString(cli.OutputFlag) != "json" { + fmt.Print(verInfo) + return nil + } + + bz, err := json.Marshal(verInfo) + if err != nil { + return err + } + fmt.Println(string(bz)) + return nil + }, } ) -// return version of CLI/node and commit hash -func GetVersion() string { - return Version -} - -// CMD -func printVersion(cmd *cobra.Command, args []string) { - fmt.Println("cosmos-sdk:", GetVersion()) - fmt.Println("git commit:", Commit) - fmt.Println("vendor hash:", VendorDirHash) - fmt.Printf("go version %s %s/%s\n", - runtime.Version(), runtime.GOOS, runtime.GOARCH) +func init() { + VersionCmd.Flags().Bool(flagLong, false, "Print long version information") } diff --git a/version/version.go b/version/version.go index 031976ecd..6affcfbd4 100644 --- a/version/version.go +++ b/version/version.go @@ -1,9 +1,34 @@ //nolint package version +import ( + "fmt" + "runtime" +) + // Variables set by build flags var ( Commit = "" Version = "" VendorDirHash = "" ) + +type versionInfo struct { + CosmosSDK string `json:"cosmos_sdk"` + GitCommit string `json:"commit"` + VendorDirHash string `json:"vendor_hash"` + GoVersion string `json:"go"` +} + +func (v versionInfo) String() string { + return fmt.Sprintf(`cosmos-sdk: %s +git commit: %s +vendor hash: %s +%s`, v.CosmosSDK, v.GitCommit, v.VendorDirHash, v.GoVersion) +} + +func newVersionInfo() versionInfo { + return versionInfo{ + Version, Commit, VendorDirHash, fmt.Sprintf("go version %s %s/%s\n", + runtime.Version(), runtime.GOOS, runtime.GOARCH)} +} From 9320dbb2dad19fa23261621498b1ab8524df7e49 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 29 Jan 2019 23:53:42 +0100 Subject: [PATCH 23/99] Merge PR #3428: Simulate from a genesis file --- Makefile | 20 +++++++++++-- PENDING.md | 1 + cmd/gaia/app/sim_test.go | 53 +++++++++++++++++++++++++++++------ scripts/multisim.sh | 4 ++- x/mock/simulation/simulate.go | 19 +++++++++---- 5 files changed, 78 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 037366c41..83da9aecb 100644 --- a/Makefile +++ b/Makefile @@ -153,9 +153,15 @@ test_sim_gaia_nondeterminism: @echo "Running nondeterminism test..." @go test ./cmd/gaia/app -run TestAppStateDeterminism -SimulationEnabled=true -v -timeout 10m +test_sim_gaia_custom_genesis_fast: + @echo "Running custom genesis simulation..." + @echo "By default, ${HOME}/.gaiad/config/genesis.json will be used." + @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationGenesis=${HOME}/.gaiad/config/genesis.json \ + -SimulationEnabled=true -SimulationNumBlocks=100 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -SimulationPeriod=5 -v -timeout 24h + test_sim_gaia_fast: @echo "Running quick Gaia simulation. This may take several minutes..." - @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=1000 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -v -timeout 24h + @go test ./cmd/gaia/app -run TestFullGaiaSimulation -SimulationEnabled=true -SimulationNumBlocks=100 -SimulationBlockSize=200 -SimulationCommit=true -SimulationSeed=99 -SimulationPeriod=5 -v -timeout 24h test_sim_gaia_import_export: @echo "Running Gaia import/export simulation. This may take several minutes..." @@ -165,6 +171,11 @@ test_sim_gaia_simulation_after_import: @echo "Running Gaia simulation-after-import. This may take several minutes..." @bash scripts/multisim.sh 50 5 TestGaiaSimulationAfterImport +test_sim_gaia_custom_genesis_multi_seed: + @echo "Running multi-seed custom genesis simulation..." + @echo "By default, ${HOME}/.gaiad/config/genesis.json will be used." + @bash scripts/multisim.sh 400 5 TestFullGaiaSimulation ${HOME}/.gaiad/config/genesis.json + test_sim_gaia_multi_seed: @echo "Running multi-seed Gaia simulation. This may take awhile!" @bash scripts/multisim.sh 400 5 TestFullGaiaSimulation @@ -174,11 +185,13 @@ SIM_BLOCK_SIZE ?= 200 SIM_COMMIT ?= true test_sim_gaia_benchmark: @echo "Running Gaia benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" - @go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h + @go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ \ + -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h test_sim_gaia_profile: @echo "Running Gaia benchmark for numBlocks=$(SIM_NUM_BLOCKS), blockSize=$(SIM_BLOCK_SIZE). This may take awhile!" - @go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out + @go test -benchmem -run=^$$ github.com/cosmos/cosmos-sdk/cmd/gaia/app -bench ^BenchmarkFullGaiaSimulation$$ \ + -SimulationEnabled=true -SimulationNumBlocks=$(SIM_NUM_BLOCKS) -SimulationBlockSize=$(SIM_BLOCK_SIZE) -SimulationCommit=$(SIM_COMMIT) -timeout 24h -cpuprofile cpu.out -memprofile mem.out test_cover: @export VERSION=$(VERSION); bash tests/test_cover.sh @@ -258,5 +271,6 @@ check_tools check_dev_tools get_vendor_deps draw_deps test test_cli test_unit \ test_cover test_lint benchmark devdoc_init devdoc devdoc_save devdoc_update \ build-linux build-docker-gaiadnode localnet-start localnet-stop \ format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \ +test_sim_gaia_custom_genesis_fast test_sim_gaia_custom_genesis_multi_seed \ test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools \ build-snap-edge diff --git a/PENDING.md b/PENDING.md index d2a365fde..266b15a37 100644 --- a/PENDING.md +++ b/PENDING.md @@ -25,6 +25,7 @@ FEATURES * Gaia - [\#3397](https://github.com/cosmos/cosmos-sdk/pull/3397) Implement genesis file sanitization to avoid failures at chain init. + * \#3428 Run the simulation from a particular genesis state loaded from a file * SDK * \#3270 [x/staking] limit number of ongoing unbonding delegations /redelegations per pair/trio diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 44e53425c..bbb5a2b4e 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -13,8 +13,10 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/secp256k1" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" @@ -36,16 +38,18 @@ import ( ) var ( - seed int64 - numBlocks int - blockSize int - enabled bool - verbose bool - commit bool - period int + genesisFile string + seed int64 + numBlocks int + blockSize int + enabled bool + verbose bool + commit bool + period int ) func init() { + flag.StringVar(&genesisFile, "SimulationGenesis", "", "Custom simulation genesis file") flag.Int64Var(&seed, "SimulationSeed", 42, "Simulation random seed") flag.IntVar(&numBlocks, "SimulationNumBlocks", 500, "Number of blocks") flag.IntVar(&blockSize, "SimulationBlockSize", 200, "Operations per block") @@ -55,7 +59,31 @@ func init() { flag.IntVar(&period, "SimulationPeriod", 1, "Run slow invariants only once every period assertions") } -func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) json.RawMessage { +func appStateFromGenesisFileFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) (json.RawMessage, []simulation.Account, string) { + var genesis tmtypes.GenesisDoc + cdc := MakeCodec() + bytes, err := ioutil.ReadFile(genesisFile) + if err != nil { + panic(err) + } + cdc.MustUnmarshalJSON(bytes, &genesis) + var appState GenesisState + cdc.MustUnmarshalJSON(genesis.AppState, &appState) + var newAccs []simulation.Account + for _, acc := range appState.Accounts { + // Pick a random private key, since we don't know the actual key + // This should be fine as it's only used for mock Tendermint validators + // and these keys are never actually used to sign by mock Tendermint. + privkeySeed := make([]byte, 15) + r.Read(privkeySeed) + privKey := secp256k1.GenPrivKeySecp256k1(privkeySeed) + newAccs = append(newAccs, simulation.Account{privKey, privKey.PubKey(), acc.Address}) + } + return json.RawMessage(genesis.AppState), newAccs, genesis.ChainID +} + +func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) (json.RawMessage, []simulation.Account, string) { + var genesisAccounts []GenesisAccount amount := int64(r.Intn(1e6)) @@ -221,7 +249,14 @@ func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.T panic(err) } - return appState + return appState, accs, "simulation" +} + +func appStateFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) (json.RawMessage, []simulation.Account, string) { + if genesisFile != "" { + return appStateFromGenesisFileFn(r, accs, genesisTimestamp) + } + return appStateRandomizedFn(r, accs, genesisTimestamp) } func randIntBetween(r *rand.Rand, min, max int) int { diff --git a/scripts/multisim.sh b/scripts/multisim.sh index e27035983..e8df463dc 100755 --- a/scripts/multisim.sh +++ b/scripts/multisim.sh @@ -5,10 +5,12 @@ seeds=(1 2 4 7 9 20 32 123 124 582 1893 2989 3012 4728 37827 981928 87821 891823 blocks=$1 period=$2 testname=$3 +genesis=$4 echo "Running multi-seed simulation with seeds ${seeds[@]}" echo "Running $blocks blocks per seed" echo "Running test $testname" +echo "Using genesis file $genesis" echo "Edit scripts/multisim.sh to add new seeds. Keeping parameters in the file makes failures easy to reproduce." echo "This script will kill all sub-simulations on SIGINT/SIGTERM (i.e. Ctrl-C)." @@ -22,7 +24,7 @@ sim() { echo "Running Gaia simulation with seed $seed. This may take awhile!" file="$tmpdir/gaia-simulation-seed-$seed-date-$(date -u +"%Y-%m-%dT%H:%M:%S+00:00").stdout" echo "Writing stdout to $file..." - go test ./cmd/gaia/app -run $testname -SimulationEnabled=true -SimulationNumBlocks=$blocks \ + go test ./cmd/gaia/app -run $testname -SimulationEnabled=true -SimulationNumBlocks=$blocks -SimulationGenesis=$genesis \ -SimulationVerbose=true -SimulationCommit=true -SimulationSeed=$seed -SimulationPeriod=$period -v -timeout 24h > $file } diff --git a/x/mock/simulation/simulate.go b/x/mock/simulation/simulate.go index 81e360a1a..723955f6b 100644 --- a/x/mock/simulation/simulate.go +++ b/x/mock/simulation/simulate.go @@ -18,8 +18,8 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -// AppStateFn returns the app state json bytes -type AppStateFn func(r *rand.Rand, accs []Account, genesisTimestamp time.Time) json.RawMessage +// AppStateFn returns the app state json bytes, the genesis accounts, and the chain identifier +type AppStateFn func(r *rand.Rand, accs []Account, genesisTimestamp time.Time) (appState json.RawMessage, accounts []Account, chainId string) // Simulate tests application by sending random messages. func Simulate(t *testing.T, app *baseapp.BaseApp, @@ -35,15 +35,18 @@ func Simulate(t *testing.T, app *baseapp.BaseApp, func initChain( r *rand.Rand, params Params, accounts []Account, app *baseapp.BaseApp, appStateFn AppStateFn, genesisTimestamp time.Time, -) mockValidators { +) (mockValidators, []Account) { + + appState, accounts, chainID := appStateFn(r, accounts, genesisTimestamp) req := abci.RequestInitChain{ - AppStateBytes: appStateFn(r, accounts, genesisTimestamp), + AppStateBytes: appState, + ChainId: chainID, } res := app.InitChain(req) validators := newMockValidators(r, res.Validators, params) - return validators + return validators, accounts } // SimulateFromSeed tests an application by running the provided @@ -73,7 +76,11 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, // Second variable to keep pending validator set (delayed one block since // TM 0.24) Initially this is the same as the initial validator set - validators := initChain(r, params, accs, app, appStateFn, genesisTimestamp) + validators, accs := initChain(r, params, accs, app, appStateFn, genesisTimestamp) + if len(accs) == 0 { + return true, fmt.Errorf("must have greater than zero genesis accounts") + } + nextValidators := validators header := abci.Header{ From 26ee435a618af30497bc361dc80acf02b4390501 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Tue, 29 Jan 2019 16:02:39 -0800 Subject: [PATCH 24/99] Upgrade gometalinter (#3439) --- .circleci/config.yml | 1 + Makefile | 3 ++- scripts/Makefile | 7 +++++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a8e718b10..1fc23c59e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -82,6 +82,7 @@ jobs: name: Get metalinter command: | export PATH="$GOBIN:$PATH" + make devtools-clean make devtools - run: name: Lint source diff --git a/Makefile b/Makefile index 83da9aecb..10d347069 100644 --- a/Makefile +++ b/Makefile @@ -99,6 +99,7 @@ update_dev_tools: go get -u github.com/tendermint/lint/golint devtools: devtools-stamp +devtools-clean: tools-clean devtools-stamp: tools @echo "--> Downloading linters (this may take awhile)" $(GOPATH)/src/github.com/alecthomas/gometalinter/scripts/install.sh -b $(GOBIN) @@ -273,4 +274,4 @@ build-linux build-docker-gaiadnode localnet-start localnet-stop \ format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \ test_sim_gaia_custom_genesis_fast test_sim_gaia_custom_genesis_multi_seed \ test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools \ -build-snap-edge +build-snap-edge devtools-clean diff --git a/scripts/Makefile b/scripts/Makefile index 910c6f68d..4a0a7d157 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -45,7 +45,7 @@ $(GOPATH)/bin/dep: #v2.0.11 $(GOPATH)/bin/gometalinter: - $(call go_install,alecthomas,gometalinter,17a7ffa42374937bfecabfb8d2efbd4db0c26741) + $(call go_install,alecthomas,gometalinter,v3.0.0) $(GOPATH)/bin/statik: $(call go_install,rakyll,statik,v0.1.5) @@ -53,4 +53,7 @@ $(GOPATH)/bin/statik: $(GOPATH)/bin/goimports: go get golang.org/x/tools/cmd/goimports -.PHONY: all +tools-clean: + cd $(GOPATH)/bin && rm -f dep gometalinter statik goimports + +.PHONY: all clean-tools From 3864d5b1bd9d7081fb5f8bc3f9a014e9a0462756 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 29 Jan 2019 19:06:38 -0500 Subject: [PATCH 25/99] Query all Delegator Distribution Rewards (#3433) --- PENDING.md | 2 ++ docs/gaia/gaiacli.md | 8 +++++ x/distribution/client/cli/query.go | 35 ++++++++++++------ x/distribution/keeper/querier.go | 58 ++++++++++++++++++++++++++---- 4 files changed, 86 insertions(+), 17 deletions(-) diff --git a/PENDING.md b/PENDING.md index 266b15a37..d84b7ddb9 100644 --- a/PENDING.md +++ b/PENDING.md @@ -22,6 +22,8 @@ FEATURES * Gaia REST API * Gaia CLI (`gaiacli`) + * [\#3429](https://github.com/cosmos/cosmos-sdk/issues/3429) Support querying + for all delegator distribution rewards. * Gaia - [\#3397](https://github.com/cosmos/cosmos-sdk/pull/3397) Implement genesis file sanitization to avoid failures at chain init. diff --git a/docs/gaia/gaiacli.md b/docs/gaia/gaiacli.md index 0b1bafd26..4886167e2 100644 --- a/docs/gaia/gaiacli.md +++ b/docs/gaia/gaiacli.md @@ -685,6 +685,14 @@ To check current rewards for a delegation (were they to be withdrawn), run: gaiacli query distr rewards ``` +#### Query all delegator rewards + +To check all current rewards for a delegation (were they to be withdrawn), run: + +```bash +gaiacli query distr rewards +``` + ### Multisig transactions Multisig transactions require signatures of multiple private keys. Thus, generating and signing diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 09b372278..4f4eb9390 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -154,8 +154,8 @@ func GetCmdQueryValidatorSlashes(queryRoute string, cdc *codec.Codec) *cobra.Com func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "rewards [delegator] [validator]", - Args: cobra.ExactArgs(2), - Short: "Query distribution delegator rewards", + Args: cobra.RangeArgs(1, 2), + Short: "Query all distribution delegator rewards or rewards from a particular validator", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) @@ -164,26 +164,39 @@ func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Com return err } - validatorAddr, err := sdk.ValAddressFromBech32(args[1]) - if err != nil { - return err + var ( + route string + params distr.QueryDelegationRewardsParams + result sdk.DecCoins + ) + + if len(args) == 1 { + // query for all rewards + params = distr.NewQueryDelegationRewardsParams(delegatorAddr, nil) + route = fmt.Sprintf("custom/%s/all_delegation_rewards", queryRoute) + } else { + // query for rewards from a particular validator + validatorAddr, err := sdk.ValAddressFromBech32(args[1]) + if err != nil { + return err + } + + params = distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr) + route = fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) } - params := distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr) bz, err := cdc.MarshalJSON(params) if err != nil { return err } - route := fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) - res, err := cliCtx.QueryWithData(route, bz) + resp, err := cliCtx.QueryWithData(route, bz) if err != nil { return err } - var coins sdk.DecCoins - cdc.MustUnmarshalJSON(res, &coins) - return cliCtx.PrintOutput(coins) + cdc.MustUnmarshalJSON(resp, &result) + return cliCtx.PrintOutput(result) }, } } diff --git a/x/distribution/keeper/querier.go b/x/distribution/keeper/querier.go index e1323aa34..619f2c39e 100644 --- a/x/distribution/keeper/querier.go +++ b/x/distribution/keeper/querier.go @@ -12,11 +12,12 @@ import ( // nolint const ( - QueryParams = "params" - QueryOutstandingRewards = "outstanding_rewards" - QueryValidatorCommission = "validator_commission" - QueryValidatorSlashes = "validator_slashes" - QueryDelegationRewards = "delegation_rewards" + QueryParams = "params" + QueryOutstandingRewards = "outstanding_rewards" + QueryValidatorCommission = "validator_commission" + QueryValidatorSlashes = "validator_slashes" + QueryDelegationRewards = "delegation_rewards" + QueryAllDelegationRewards = "all_delegation_rewards" ParamCommunityTax = "community_tax" ParamBaseProposerReward = "base_proposer_reward" @@ -29,14 +30,22 @@ func NewQuerier(k Keeper) sdk.Querier { switch path[0] { case QueryParams: return queryParams(ctx, path[1:], req, k) + case QueryOutstandingRewards: return queryOutstandingRewards(ctx, path[1:], req, k) + case QueryValidatorCommission: return queryValidatorCommission(ctx, path[1:], req, k) + case QueryValidatorSlashes: return queryValidatorSlashes(ctx, path[1:], req, k) + case QueryDelegationRewards: return queryDelegationRewards(ctx, path[1:], req, k) + + case QueryAllDelegationRewards: + return queryAllDelegationRewards(ctx, path[1:], req, k) + default: return nil, sdk.ErrUnknownRequest("unknown distr query endpoint") } @@ -158,20 +167,57 @@ func NewQueryDelegationRewardsParams(delegatorAddr sdk.AccAddress, validatorAddr } } -func queryDelegationRewards(ctx sdk.Context, path []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { +func queryDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { var params QueryDelegationRewardsParams err := k.cdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) } + + // cache-wrap context as to not persist state changes during querying ctx, _ = ctx.CacheContext() + val := k.stakingKeeper.Validator(ctx, params.ValidatorAddr) del := k.stakingKeeper.Delegation(ctx, params.DelegatorAddr, params.ValidatorAddr) endingPeriod := k.incrementValidatorPeriod(ctx, val) rewards := k.calculateDelegationRewards(ctx, val, del, endingPeriod) + bz, err := codec.MarshalJSONIndent(k.cdc, rewards) if err != nil { return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) } + + return bz, nil +} + +func queryAllDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params QueryDelegationRewardsParams + err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + // cache-wrap context as to not persist state changes during querying + ctx, _ = ctx.CacheContext() + + var totalRewards sdk.DecCoins + + k.stakingKeeper.IterateDelegations( + ctx, params.DelegatorAddr, + func(_ int64, del sdk.Delegation) (stop bool) { + val := k.stakingKeeper.Validator(ctx, del.GetValidatorAddr()) + endingPeriod := k.incrementValidatorPeriod(ctx, val) + rewards := k.calculateDelegationRewards(ctx, val, del, endingPeriod) + + totalRewards = totalRewards.Plus(rewards) + return false + }, + ) + + bz, err := codec.MarshalJSONIndent(k.cdc, totalRewards) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil } From d2b453e4205e0e681e7c2e5ec001edf5b8827e66 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 30 Jan 2019 05:59:19 +0100 Subject: [PATCH 26/99] Merge PR #3440: Fix long multi-seed simulation --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1fc23c59e..98df2e67f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -172,7 +172,7 @@ jobs: name: Test multi-seed Gaia simulation long command: | export PATH="$GOBIN:$PATH" - scripts/multisim.sh 800 50 TestFullGaiaSimulation + scripts/multisim.sh 500 50 TestFullGaiaSimulation test_sim_gaia_multi_seed: <<: *linux_defaults From 24876aa47b57e931e1988e20c7dbbeab8b59372a Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Tue, 29 Jan 2019 21:00:06 -0800 Subject: [PATCH 27/99] Merge PR #3441: Small fixes for Makefile - Remove --long from git describe. Appending commit info on tagged revisions is redundant. - $(subst v,,$(shell git describe --tags --long)) was removing any occurrence of 'v'. Rather we want to remove only the 'v' prefix from tags. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 10d347069..423179a0a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ PACKAGES_NOSIMULATION=$(shell go list ./... | grep -v '/simulation') PACKAGES_SIMTEST=$(shell go list ./... | grep '/simulation') -VERSION := $(subst v,,$(shell git describe --tags --long)) +VERSION := $(shell echo $(shell git describe --tags) | sed 's/^v//') COMMIT := $(shell git log -1 --format='%H') BUILD_TAGS = netgo CAT := $(if $(filter $(OS),Windows_NT),type,cat) From fd7135154195bf03ebde9dc8d1bcc7616c9f12ef Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Wed, 30 Jan 2019 13:22:20 -0500 Subject: [PATCH 28/99] Merge PR #3446: Register x/gov Message Types on init * register x/gov message types on message codec on init * add TestMsgDepositGetSignBytes --- x/gov/codec.go | 7 +++++-- x/gov/msgs_test.go | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/x/gov/codec.go b/x/gov/codec.go index 6df535449..10fecb3bc 100644 --- a/x/gov/codec.go +++ b/x/gov/codec.go @@ -4,9 +4,10 @@ import ( "github.com/cosmos/cosmos-sdk/codec" ) +var msgCdc = codec.New() + // Register concrete types on codec codec func RegisterCodec(cdc *codec.Codec) { - cdc.RegisterConcrete(MsgSubmitProposal{}, "cosmos-sdk/MsgSubmitProposal", nil) cdc.RegisterConcrete(MsgDeposit{}, "cosmos-sdk/MsgDeposit", nil) cdc.RegisterConcrete(MsgVote{}, "cosmos-sdk/MsgVote", nil) @@ -15,4 +16,6 @@ func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(&TextProposal{}, "gov/TextProposal", nil) } -var msgCdc = codec.New() +func init() { + RegisterCodec(msgCdc) +} diff --git a/x/gov/msgs_test.go b/x/gov/msgs_test.go index 3c4472137..a9fa88396 100644 --- a/x/gov/msgs_test.go +++ b/x/gov/msgs_test.go @@ -55,6 +55,15 @@ func TestMsgSubmitProposal(t *testing.T) { } } +func TestMsgDepositGetSignBytes(t *testing.T) { + addr := sdk.AccAddress("addr1") + msg := NewMsgDeposit(addr, 0, coinsPos) + res := msg.GetSignBytes() + + expected := `{"type":"cosmos-sdk/MsgDeposit","value":{"amount":[{"amount":"1000","denom":"stake"}],"depositor":"cosmos1v9jxgu33kfsgr5","proposal_id":"0"}}` + require.Equal(t, expected, string(res)) +} + // test ValidateBasic for MsgDeposit func TestMsgDeposit(t *testing.T) { _, addrs, _, _ := mock.CreateGenAccounts(1, sdk.Coins{}) From bc193df8e06008df29307b30e9721c91ee07dfb0 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 30 Jan 2019 22:48:20 +0100 Subject: [PATCH 29/99] Merge PR #3449: LCD proof verification --- PENDING.md | 1 + client/context/context.go | 9 +++++++-- client/context/query.go | 7 +++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/PENDING.md b/PENDING.md index d84b7ddb9..fe0d438fa 100644 --- a/PENDING.md +++ b/PENDING.md @@ -24,6 +24,7 @@ FEATURES * Gaia CLI (`gaiacli`) * [\#3429](https://github.com/cosmos/cosmos-sdk/issues/3429) Support querying for all delegator distribution rewards. + * \#3449 Proof verification now works with absence proofs * Gaia - [\#3397](https://github.com/cosmos/cosmos-sdk/pull/3397) Implement genesis file sanitization to avoid failures at chain init. diff --git a/client/context/context.go b/client/context/context.go index a253a88fa..a9744408d 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -24,7 +24,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -var verifier tmlite.Verifier +var ( + verifier tmlite.Verifier + verifierHome string +) // CLIContext implements a typical CLI context created in SDK modules for // transaction handling and queries. @@ -43,6 +46,7 @@ type CLIContext struct { Async bool PrintResponse bool Verifier tmlite.Verifier + VerifierHome string Simulate bool GenerateOnly bool FromAddress sdk.AccAddress @@ -68,8 +72,9 @@ func NewCLIContext() CLIContext { } // We need to use a single verifier for all contexts - if verifier == nil { + if verifier == nil || verifierHome != viper.GetString(cli.HomeFlag) { verifier = createVerifier() + verifierHome = viper.GetString(cli.HomeFlag) } return CLIContext{ diff --git a/client/context/query.go b/client/context/query.go index cf70b3cea..9a7dea39a 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -219,6 +219,13 @@ func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) err kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL) kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL) + if resp.Value == nil { + err = prt.VerifyAbsence(resp.Proof, commit.Header.AppHash, kp.String()) + if err != nil { + return errors.Wrap(err, "failed to prove merkle proof") + } + return nil + } err = prt.VerifyValue(resp.Proof, commit.Header.AppHash, kp.String(), resp.Value) if err != nil { return errors.Wrap(err, "failed to prove merkle proof") From f4cf45e17af53f84698044a62454046ae8f39585 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Wed, 30 Jan 2019 16:49:14 -0800 Subject: [PATCH 30/99] Merge PR #3453: Make rest-server respect persistent flags --- PENDING.md | 2 ++ client/flags.go | 8 +------- client/lcd/root.go | 7 +++---- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/PENDING.md b/PENDING.md index fe0d438fa..531509b00 100644 --- a/PENDING.md +++ b/PENDING.md @@ -68,6 +68,8 @@ BUG FIXES https://github.com/ZondaX/ledger-cosmos-go/commit/ed9aa39ce8df31bad1448c72d3d226bf2cb1a8d1 in order to fix a derivation path issue that causes `gaiacli keys add --recover` to malfunction. - [\#3419](https://github.com/cosmos/cosmos-sdk/pull/3419) Fix `q distr slashes` panic + - [\#3453](https://github.com/cosmos/cosmos-sdk/pull/3453) The `rest-server` command didn't respect persistent flags such as `--chain-id` and `--trust-node` if they were + passed on the command line. * Gaia diff --git a/client/flags.go b/client/flags.go index 9ee121c20..0c5d3423a 100644 --- a/client/flags.go +++ b/client/flags.go @@ -103,21 +103,15 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { // RegisterRestServerFlags registers the flags required for rest server func RegisterRestServerFlags(cmd *cobra.Command) *cobra.Command { + cmd = GetCommands(cmd)[0] cmd.Flags().String(FlagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") cmd.Flags().Bool(FlagInsecure, false, "Do not set up SSL/TLS layer") cmd.Flags().String(FlagSSLHosts, "", "Comma-separated hostnames and IPs to generate a certificate for") cmd.Flags().String(FlagSSLCertFile, "", "Path to a SSL certificate file. If not supplied, a self-signed certificate will be generated.") cmd.Flags().String(FlagSSLKeyFile, "", "Path to a key file; ignored if a certificate file is not supplied.") cmd.Flags().String(FlagCORS, "", "Set the domains that can make CORS requests (* for all)") - cmd.Flags().String(FlagChainID, "", "Chain ID of Tendermint node") - cmd.Flags().String(FlagNode, "tcp://localhost:26657", "Address of the node to connect to") cmd.Flags().Int(FlagMaxOpenConnections, 1000, "The number of maximum open connections") - cmd.Flags().Bool(FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") - cmd.Flags().Bool(FlagIndentResponse, false, "Add indent to JSON response") - viper.BindPFlag(FlagTrustNode, cmd.Flags().Lookup(FlagTrustNode)) - viper.BindPFlag(FlagChainID, cmd.Flags().Lookup(FlagChainID)) - viper.BindPFlag(FlagNode, cmd.Flags().Lookup(FlagNode)) return cmd } diff --git a/client/lcd/root.go b/client/lcd/root.go index 07aade96b..8014fdd92 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -84,7 +84,8 @@ func (rs *RestServer) Start(listenAddr string, sslHosts string, if err != nil { return } - rs.log.Info("Starting Gaia Lite REST service...") + rs.log.Info(fmt.Sprintf("Starting Gaia Lite REST service (chain-id: %q)...", + viper.GetString(client.FlagChainID))) // launch rest-server in insecure mode if insecure { @@ -151,9 +152,7 @@ func ServeCommand(cdc *codec.Codec, registerRoutesFn func(*RestServer)) *cobra.C }, } - client.RegisterRestServerFlags(cmd) - - return cmd + return client.RegisterRestServerFlags(cmd) } func (rs *RestServer) registerSwaggerUI() { From 0e1f61949e1f268988c04fb4b4d8fb9d257cea2e Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 31 Jan 2019 09:59:19 -0800 Subject: [PATCH 31/99] Autobuild snap (#3455) --- .circleci/config.yml | 54 ++++++++++++++++++++++++++++++++++++++++++ snapcraft-full.yaml.in | 51 +++++++++++++++++++++++++++++++++++++++ snapcraft.yaml.in | 22 ++++------------- 3 files changed, 109 insertions(+), 18 deletions(-) create mode 100644 snapcraft-full.yaml.in diff --git a/.circleci/config.yml b/.circleci/config.yml index 98df2e67f..c6bd1e518 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -264,6 +264,52 @@ jobs: make localnet-start ./scripts/localnet-blocks-test.sh 40 5 10 localhost + snap-edge: + working_directory: /home/circleci/.snap_workspace/src/github.com/cosmos/cosmos-sdk + machine: true + environment: + LC_ALL: C.UTF-8 + LANG: C.UTF-8 + GOPATH: /tmp/workspace + GOBIN: /tmp/workspace/bin + parallelism: 1 + steps: + - checkout + - attach_workspace: + at: /tmp/workspace + - run: + name: Install prerequitistes + command: | + sudo sh -c "echo '#!/bin/sh' > /usr/sbin/update-initramfs" + sudo sh -c "echo 'exit 0' >> /usr/sbin/update-initramfs" + sudo apt update -y &>/dev/null + sudo apt install -y build-essential snapd + sudo snap install snapcraft --edge --classic + - run: + name: prepping snap build + command: | + /snap/bin/snapcraft version + make snapcraft.yaml + - run: + name: build edge snap + command: /snap/bin/snapcraft + - run: + name: Create credentials file + command: | + echo '[login.ubuntu.com]' > credentials + echo "macaroon = $SNAPCRAFT_CREDENTIALS_MACAROON" >> credentials + echo "unbound_discharge = $SNAPCRAFT_CREDENTIALS_UNBOUND_DISCHARGE" >> credentials + echo "email = $SNAPCRAFT_CREDENTIALS_EMAIL" >> credentials + - run: + name: Authenticate snapcraft + command: /snap/bin/snapcraft login --with credentials + - run: + name: Push/release snap + command: /snap/bin/snapcraft push *.snap --release edge + - run: + name: Clean up credentials file + command: rm -vf credentials + deploy_docs: <<: *docs_deploy steps: @@ -384,6 +430,14 @@ workflows: - localnet: requires: - setup_dependencies + - snap-edge: + requires: + - setup_dependencies + filters: + branches: + only: + - master + - develop - upload_coverage: requires: - test_cover diff --git a/snapcraft-full.yaml.in b/snapcraft-full.yaml.in new file mode 100644 index 000000000..2b2b43feb --- /dev/null +++ b/snapcraft-full.yaml.in @@ -0,0 +1,51 @@ +name: gaia # you probably want to 'snapcraft register ' +# base: core18 # the base snap is the execution environment for this snap +version: '@VERSION@' # just for humans, typically '1.2+git' or '1.3.2' +summary: Gaia Daemon # 79 char long summary +description: | + This snap provides the Gaia daemon gaiad and the command line + tool gaiacli. +grade: devel # must be 'stable' to release into candidate/stable channels +confinement: strict # use 'strict' once you have the right plugs and slots + +apps: + gaiad: + command: bin/gaiad + plugs: [home,network,network-bind] + gaiacli: + command: bin/gaiacli + plugs: [home,network,network-bind] + +parts: + gaia: + plugin: dump + source: ./ + override-pull: | + rootdir=$(pwd) + gitroot=$(git rev-parse --show-toplevel) + cd ${gitroot} && git archive \ + -o ${rootdir}/gaia-@VERSION@.tar.gz \ + --format tar.gz -9 --prefix gaia-@VERSION@/ HEAD + cd ${rootdir} + tar xf gaia-@VERSION@.tar.gz ; rm -f gaia-@VERSION@.tar.gz + mkdir -p go/src/github.com/cosmos bin + mv gaia-@VERSION@/ go/src/github.com/cosmos/cosmos-sdk/ + + # Use the following instructions to build a package from a release. + # wget https://github.com/cosmos/cosmos-sdk/archive/v@VERSION@.tar.gz + # tar xvf v@VERSION@.tar.gz + # rm v@VERSION@.tar.gz + + build-snaps: [go] + override-build: | + base=`pwd` + export GOPATH=`pwd`/go + export GOBIN=$GOPATH/bin + export PATH=$GOBIN:$PATH + cd $GOPATH/src/github.com/cosmos/cosmos-sdk + make tools + make vendor-deps + make install + mkdir $SNAPCRAFT_PART_INSTALL/bin + cp $GOPATH/bin/gaiad $SNAPCRAFT_PART_INSTALL/bin + cp $GOPATH/bin/gaiacli $SNAPCRAFT_PART_INSTALL/bin diff --git a/snapcraft.yaml.in b/snapcraft.yaml.in index 7a7878a3c..8bb2ad73d 100644 --- a/snapcraft.yaml.in +++ b/snapcraft.yaml.in @@ -21,15 +21,7 @@ parts: plugin: dump source: ./ override-pull: | - rootdir=$(pwd) - gitroot=$(git rev-parse --show-toplevel) - cd ${gitroot} && git archive \ - -o ${rootdir}/gaia-@VERSION@.tar.gz \ - --format tar.gz -9 --prefix gaia-@VERSION@/ HEAD - cd ${rootdir} - tar xvf gaia-@VERSION@.tar.gz ; rm -f gaia-@VERSION@.tar.gz - mkdir -p go/src/github.com/cosmos bin - mv gaia-@VERSION@/ go/src/github.com/cosmos/cosmos-sdk/ + echo "Installing files from $GOBIN ..." # Use the following instructions to build a package from a release. # wget https://github.com/cosmos/cosmos-sdk/archive/v@VERSION@.tar.gz @@ -38,12 +30,6 @@ parts: build-snaps: [go] override-build: | - base=`pwd` - export GOPATH=`pwd`/go - cd $GOPATH/src/github.com/cosmos/cosmos-sdk - make tools - make vendor-deps - make install - mkdir $SNAPCRAFT_PART_INSTALL/bin - cp $GOPATH/bin/gaiad $SNAPCRAFT_PART_INSTALL/bin - cp $GOPATH/bin/gaiacli $SNAPCRAFT_PART_INSTALL/bin + mkdir -p $SNAPCRAFT_PART_INSTALL/bin + cp $GOBIN/gaiad $SNAPCRAFT_PART_INSTALL/bin + cp $GOBIN/gaiacli $SNAPCRAFT_PART_INSTALL/bin From 5ea2be1adc1b9007ff4ea10a99a2120a53c9cd00 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Thu, 31 Jan 2019 10:39:46 -0800 Subject: [PATCH 32/99] Merge PR #3458: Fix usage of the config command in applications importing the SDK * Fix usage of the config command in applications importing the SDK * Update client/config.go --- client/config.go | 6 ++---- cmd/gaia/cmd/gaiacli/main.go | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/client/config.go b/client/config.go index 675a35296..ce57434a5 100644 --- a/client/config.go +++ b/client/config.go @@ -9,8 +9,6 @@ import ( "github.com/tendermint/tendermint/libs/cli" - "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - "github.com/pelletier/go-toml" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -32,7 +30,7 @@ func init() { // ConfigCmd returns a CLI command to interactively create a // Gaia CLI config file. -func ConfigCmd() *cobra.Command { +func ConfigCmd(defaultCLIHome string) *cobra.Command { cmd := &cobra.Command{ Use: "config [value]", Short: "Create or query a Gaia CLI configuration file", @@ -40,7 +38,7 @@ func ConfigCmd() *cobra.Command { Args: cobra.RangeArgs(0, 2), } - cmd.Flags().String(cli.HomeFlag, app.DefaultCLIHome, + cmd.Flags().String(cli.HomeFlag, defaultCLIHome, "set client's home directory for configuration") cmd.Flags().Bool(flagGet, false, "print configuration value or its default if unset") diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index f390324aa..3042c35d6 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -84,7 +84,7 @@ func main() { // Construct Root Command rootCmd.AddCommand( rpc.StatusCommand(), - client.ConfigCmd(), + client.ConfigCmd(app.DefaultCLIHome), queryCmd(cdc, mc), txCmd(cdc, mc), client.LineBreak, From 45d59b07926f48ee9caaff760f2fc060f81dd389 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 31 Jan 2019 17:20:58 -0800 Subject: [PATCH 33/99] Merge PR #3464: Upgrade to go 1.11.5 --- .circleci/config.yml | 6 +++--- README.md | 2 +- docs/gaia/installation.md | 2 +- scripts/install/install_sdk_arm.sh | 2 +- scripts/install/install_sdk_bsd.sh | 2 +- scripts/install/install_sdk_ubuntu.sh | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c6bd1e518..c48ee899d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2 defaults: &linux_defaults working_directory: /go/src/github.com/cosmos/cosmos-sdk docker: - - image: circleci/golang:1.11.4 + - image: circleci/golang:1.11.5 environment: GOBIN: /tmp/workspace/bin @@ -17,7 +17,7 @@ macos_config: &macos_defaults xcode: "10.1.0" working_directory: /Users/distiller/project/src/github.com/cosmos/cosmos-sdk environment: - GO_VERSION: "1.11.4" + GO_VERSION: "1.11.5" set_macos_env: &macos_env run: @@ -244,7 +244,7 @@ jobs: GOPATH: /home/circleci/.go_workspace/ GOOS: linux GOARCH: amd64 - GO_VERSION: "1.11.4" + GO_VERSION: "1.11.5" parallelism: 1 steps: - checkout diff --git a/README.md b/README.md index 2ab5cf4e2..b69baae05 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ It is being used to build `Gaia`, the first implementation of the Cosmos Hub. **WARNING**: The SDK has mostly stabilized, but we are still making some breaking changes. -**Note**: Requires [Go 1.11.4+](https://golang.org/dl/) +**Note**: Requires [Go 1.11.5+](https://golang.org/dl/) ## Cosmos Hub Public Testnet diff --git a/docs/gaia/installation.md b/docs/gaia/installation.md index de48bf27d..84b4f9269 100644 --- a/docs/gaia/installation.md +++ b/docs/gaia/installation.md @@ -14,7 +14,7 @@ echo "export PATH=$PATH:$GOBIN" >> ~/.bash_profile ``` ::: tip -**Go 1.11.4+** is required for the Cosmos SDK. +**Go 1.11.5+** is required for the Cosmos SDK. ::: ### Install the binaries diff --git a/scripts/install/install_sdk_arm.sh b/scripts/install/install_sdk_arm.sh index 951e2830c..f7aa0ec86 100644 --- a/scripts/install/install_sdk_arm.sh +++ b/scripts/install/install_sdk_arm.sh @@ -4,7 +4,7 @@ BRANCH=master REPO=github.com/cosmos/cosmos-sdk -GO_VERSION=1.11.4 +GO_VERSION=1.11.5 sudo apt-get update -y sudo apt-get upgrade -y diff --git a/scripts/install/install_sdk_bsd.sh b/scripts/install/install_sdk_bsd.sh index fda0eb1e1..684274e99 100644 --- a/scripts/install/install_sdk_bsd.sh +++ b/scripts/install/install_sdk_bsd.sh @@ -16,7 +16,7 @@ set BRANCH=master set REPO=github.com/cosmos/cosmos-sdk -set GO_VERSION=1.11.4 +set GO_VERSION=1.11.5 sudo pkg update diff --git a/scripts/install/install_sdk_ubuntu.sh b/scripts/install/install_sdk_ubuntu.sh index 3ac23ccae..9d235f41c 100644 --- a/scripts/install/install_sdk_ubuntu.sh +++ b/scripts/install/install_sdk_ubuntu.sh @@ -7,7 +7,7 @@ BRANCH=master REPO=github.com/cosmos/cosmos-sdk -GO_VERSION=1.11.4 +GO_VERSION=1.11.5 sudo apt-get update -y sudo apt-get upgrade -y From 08e62fb1575f00d79f7ca066652b27619acdfbb3 Mon Sep 17 00:00:00 2001 From: Joon Date: Fri, 1 Feb 2019 17:03:09 -0800 Subject: [PATCH 34/99] Store Refactor 1 (#2985) --- PENDING.md | 1 + baseapp/baseapp.go | 9 +- baseapp/baseapp_test.go | 2 +- client/context/query.go | 7 +- cmd/gaia/cmd/gaiad/main.go | 2 +- cmd/gaia/cmd/gaiadebug/hack.go | 2 +- server/mock/store.go | 8 +- store/README.md | 130 ++++++ store/{ => cachekv}/memiterator.go | 2 +- .../mergeiterator.go} | 12 +- store/cachekv/store.go | 196 ++++++++ .../store_test.go} | 46 +- store/cachekvstore.go | 199 --------- store/cachemulti/store.go | 135 ++++++ store/cachemultistore.go | 141 ------ store/dbadapter/store.go | 34 ++ store/dbstoreadapter.go | 67 --- store/firstlast.go | 4 +- store/{gaskvstore.go => gaskv/store.go} | 86 ++-- store/gaskv/store_test.go | 78 ++++ store/gaskvstore_test.go | 111 ----- store/{iavlstore.go => iavl/store.go} | 129 +++--- .../{iavlstore_test.go => iavl/store_test.go} | 72 +-- store/iavl/wire.go | 7 + store/{ => list}/list.go | 11 +- store/{ => list}/list_test.go | 38 +- store/{prefixstore.go => prefix/store.go} | 73 ++- .../store_test.go} | 110 +++-- store/pruning.go | 29 -- store/{ => queue}/queue.go | 7 +- store/queue_test.go | 102 ----- store/{codec.go => reexport.go} | 14 +- store/rootmulti/dbadapter.go | 33 ++ .../proof.go} | 2 +- .../proof_test.go} | 32 +- .../{rootmultistore.go => rootmulti/store.go} | 179 ++++---- .../store_test.go} | 69 +-- store/rootmulti/wire.go | 7 + store/store.go | 26 ++ store/{tracekvstore.go => tracekv/store.go} | 68 ++- .../store_test.go} | 31 +- store/transient/store.go | 42 ++ .../store_test.go} | 4 +- store/transientstore.go | 50 --- {types => store/types}/gas.go | 19 +- {types => store/types}/gas_test.go | 26 ++ store/types/pruning.go | 35 ++ store/types/store.go | 274 ++++++++++++ store/types/utils.go | 98 ++++ store/types/validity.go | 15 + store/validity.go | 13 - types/context.go | 9 +- types/int.go | 11 - types/int_test.go | 25 -- types/store.go | 422 ++++-------------- x/params/keeper_test.go | 5 +- x/params/subspace/subspace.go | 6 +- x/params/subspace/test_common.go | 4 +- 58 files changed, 1758 insertions(+), 1611 deletions(-) create mode 100644 store/README.md rename store/{ => cachekv}/memiterator.go (98%) rename store/{cachemergeiterator.go => cachekv/mergeiterator.go} (95%) create mode 100644 store/cachekv/store.go rename store/{cachekvstore_test.go => cachekv/store_test.go} (91%) delete mode 100644 store/cachekvstore.go create mode 100644 store/cachemulti/store.go delete mode 100644 store/cachemultistore.go create mode 100644 store/dbadapter/store.go delete mode 100644 store/dbstoreadapter.go rename store/{gaskvstore.go => gaskv/store.go} (55%) create mode 100644 store/gaskv/store_test.go delete mode 100644 store/gaskvstore_test.go rename store/{iavlstore.go => iavl/store.go} (75%) rename store/{iavlstore_test.go => iavl/store_test.go} (88%) create mode 100644 store/iavl/wire.go rename store/{ => list}/list.go (93%) rename store/{ => list}/list_test.go (61%) rename store/{prefixstore.go => prefix/store.go} (71%) rename store/{prefixstore_test.go => prefix/store_test.go} (79%) delete mode 100644 store/pruning.go rename store/{ => queue}/queue.go (95%) delete mode 100644 store/queue_test.go rename store/{codec.go => reexport.go} (72%) create mode 100644 store/rootmulti/dbadapter.go rename store/{multistoreproof.go => rootmulti/proof.go} (99%) rename store/{multistoreproof_test.go => rootmulti/proof_test.go} (84%) rename store/{rootmultistore.go => rootmulti/store.go} (67%) rename store/{rootmultistore_test.go => rootmulti/store_test.go} (71%) create mode 100644 store/rootmulti/wire.go create mode 100644 store/store.go rename store/{tracekvstore.go => tracekv/store.go} (68%) rename store/{tracekvstore_test.go => tracekv/store_test.go} (89%) create mode 100644 store/transient/store.go rename store/{transientstore_test.go => transient/store_test.go} (86%) delete mode 100644 store/transientstore.go rename {types => store/types}/gas.go (89%) rename {types => store/types}/gas_test.go (70%) create mode 100644 store/types/pruning.go create mode 100644 store/types/store.go create mode 100644 store/types/utils.go create mode 100644 store/types/validity.go delete mode 100644 store/validity.go diff --git a/PENDING.md b/PENDING.md index 531509b00..75c3c799b 100644 --- a/PENDING.md +++ b/PENDING.md @@ -53,6 +53,7 @@ IMPROVEMENTS * [\#3420](https://github.com/cosmos/cosmos-sdk/issues/3420) Added maximum length to governance proposal descriptions and titles * SDK + * [\#2986](https://github.com/cosmos/cosmos-sdk/pull/2986) Store Refactor * \#3435 Test that store implementations do not allow nil values * Tendermint diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 950125180..ef5c692b9 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -114,7 +114,7 @@ func (app *BaseApp) Name() string { // SetCommitMultiStoreTracer sets the store tracer on the BaseApp's underlying // CommitMultiStore. func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) { - app.cms.WithTracer(w) + app.cms.SetTracer(w) } // Mount IAVL or DB stores to the provided keys in the BaseApp multistore @@ -483,8 +483,7 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res // BeginBlock implements the ABCI application interface. func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) { if app.cms.TracingEnabled() { - app.cms.ResetTraceContext() - app.cms.WithTracingContext(sdk.TraceContext( + app.cms.SetTracingContext(sdk.TraceContext( map[string]interface{}{"blockHeight": req.Header.Height}, )) } @@ -679,7 +678,7 @@ func (app *BaseApp) cacheTxContext(ctx sdk.Context, txBytes []byte) ( // TODO: https://github.com/cosmos/cosmos-sdk/issues/2824 msCache := ms.CacheMultiStore() if msCache.TracingEnabled() { - msCache = msCache.WithTracingContext( + msCache = msCache.SetTracingContext( sdk.TraceContext( map[string]interface{}{ "txHash": fmt.Sprintf("%X", tmhash.Sum(txBytes)), @@ -813,7 +812,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk // EndBlock implements the ABCI application interface. func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { if app.deliverState.ms.TracingEnabled() { - app.deliverState.ms = app.deliverState.ms.ResetTraceContext().(sdk.CacheMultiStore) + app.deliverState.ms = app.deliverState.ms.SetTracingContext(nil).(sdk.CacheMultiStore) } if app.endBlocker != nil { diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index acccd1fbf..82ee0bd9c 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/cosmos/cosmos-sdk/store" + store "github.com/cosmos/cosmos-sdk/store/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/client/context/query.go b/client/context/query.go index 9a7dea39a..36229ad59 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -10,6 +10,7 @@ import ( "strings" + "github.com/cosmos/cosmos-sdk/store/rootmulti" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/merkle" cmn "github.com/tendermint/tendermint/libs/common" @@ -17,8 +18,6 @@ import ( tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" tmtypes "github.com/tendermint/tendermint/types" - - "github.com/cosmos/cosmos-sdk/store" ) // GetNode returns an RPC client. If the context's client is not defined, an @@ -207,7 +206,7 @@ func (ctx CLIContext) verifyProof(queryPath string, resp abci.ResponseQuery) err } // TODO: Instead of reconstructing, stash on CLIContext field? - prt := store.DefaultProofRuntime() + prt := rootmulti.DefaultProofRuntime() // TODO: Better convention for path? storeName, err := parseQueryStorePath(queryPath) @@ -254,7 +253,7 @@ func isQueryStoreWithProof(path string) bool { return false case paths[0] != "store": return false - case store.RequireProof("/" + paths[2]): + case rootmulti.RequireProof("/" + paths[2]): return true } diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index 0d6e8a92f..00ebb9667 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -60,7 +60,7 @@ func main() { func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application { return app.NewGaiaApp( logger, db, traceStore, true, - baseapp.SetPruning(store.NewPruningOptions(viper.GetString("pruning"))), + baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning"))), baseapp.SetMinGasPrices(viper.GetString(server.FlagMinGasPrices)), ) } diff --git a/cmd/gaia/cmd/gaiadebug/hack.go b/cmd/gaia/cmd/gaiadebug/hack.go index aa70b3fb0..eb4c8749b 100644 --- a/cmd/gaia/cmd/gaiadebug/hack.go +++ b/cmd/gaia/cmd/gaiadebug/hack.go @@ -50,7 +50,7 @@ func runHackCmd(cmd *cobra.Command, args []string) error { fmt.Println(err) os.Exit(1) } - app := NewGaiaApp(logger, db, baseapp.SetPruning(store.NewPruningOptions(viper.GetString("pruning")))) + app := NewGaiaApp(logger, db, baseapp.SetPruning(store.NewPruningOptionsFromString(viper.GetString("pruning")))) // print some info id := app.LastCommitID() diff --git a/server/mock/store.go b/server/mock/store.go index 3aecc1734..2d18159ae 100644 --- a/server/mock/store.go +++ b/server/mock/store.go @@ -26,19 +26,15 @@ func (ms multiStore) CacheWrapWithTrace(_ io.Writer, _ sdk.TraceContext) sdk.Cac panic("not implemented") } -func (ms multiStore) ResetTraceContext() sdk.MultiStore { - panic("not implemented") -} - func (ms multiStore) TracingEnabled() bool { panic("not implemented") } -func (ms multiStore) WithTracingContext(tc sdk.TraceContext) sdk.MultiStore { +func (ms multiStore) SetTracingContext(tc sdk.TraceContext) sdk.MultiStore { panic("not implemented") } -func (ms multiStore) WithTracer(w io.Writer) sdk.MultiStore { +func (ms multiStore) SetTracer(w io.Writer) sdk.MultiStore { panic("not implemented") } diff --git a/store/README.md b/store/README.md new file mode 100644 index 000000000..54994374b --- /dev/null +++ b/store/README.md @@ -0,0 +1,130 @@ +# Store + +## CacheKV + +`cachekv.Store` is a wrapper `KVStore` which provides buffered writing / cached reading functionalities over the underlying `KVStore`. + +```go +type Store struct { + cache map[string]cValue + parent types.KVStore +} +``` + +### Get + +`Store.Get()` checks `Store.cache` first in order to find if there is any cached value associated with the key. If the value exists, the function returns it. If not, the function calls `Store.parent.Get()`, sets the key-value pair to the `Store.cache`, and returns it. + +### Set + +`Store.Set()` sets the key-value pair to the `Store.cache`. `cValue` has the field `dirty bool` which indicates whether the cached value is different from the underlying value. When `Store.Set()` cache new pair, the `cValue.dirty` is set true so when `Store.Write()` is called it can be written to the underlying store. + +### Iterator + +`Store.Iterator()` have to traverse on both caches items and the original items. In `Store.iterator()`, two iterators are generated for each of them, and merged. `memIterator` is essentially a slice of the `KVPair`s, used for cached items. `mergeIterator` is a combination of two iterators, where traverse happens ordered on both iterators. + +## CacheMulti + +`cachemulti.Store` is a wrapper `MultiStore` which provides buffered writing / cached reading functionalities over the underlying `MutliStore` + +```go +type Store struct { + db types.CacheKVStore + stores map[types.StoreKey] types.CacheWrap +} +``` + +`cachemulti.Store` cache wraps all substores in its constructor and hold them in `Store.stores`. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on the substores. + +## DBAdapter + +`dbadapter.Store` is a adapter for `dbm.DB` making it fulfilling the `KVStore` interface. + +```go +type Store struct { + dbm.DB +} +``` + +`dbadapter.Store` embeds `dbm.DB`, so most of the `KVStore` interface functions are implemented. The other functions(mostly miscellaneous) are manually implemented. + +## IAVL + +`iavl.Store` is a base-layer self-balancing merkle tree. It is guaranteed that + +1. Get & set operations are `O(log n)`, where `n` is the number of elements in the tree +2. Iteration efficiently returns the sorted elements within the range +3. Each tree version is immutable and can be retrieved even after a commit(depending on the pruning settings) + +Specification and implementation of IAVL tree can be found in [https://github.com/tendermint/iavl]. + +## GasKV + +`gaskv.Store` is a wrapper `KVStore` which provides gas consuming functionalities over the underlying `KVStore`. + +```go +type Store struct { + gasMeter types.GasMeter + gasConfig types.GasConfig + parent types.KVStore +} +``` + +When each `KVStore` methods are called, `gaskv.Store` automatically consumes appropriate amount of gas depending on the `Store.gasConfig`. + + +## Prefix + +`prefix.Store` is a wrapper `KVStore` which provides automatic key-prefixing functionalities over the underlying `KVStore`. + +```go +type Store struct { + parent types.KVStore + prefix []byte +} +``` + +When `Store.{Get, Set}()` is called, the store forwards the call to its parent, with the key prefixed with the `Store.prefix`. + +When `Store.Iterator()` is called, it does not simply prefix the `Store.prefix`, since it does not work as intended. In that case, some of the elements are traversed even they are not starting with the prefix. + +## RootMulti + +`rootmulti.Store` is a base-layer `MultiStore` where multiple `KVStore` can be mounted on it and retrieved via object-capability keys. The keys are memory addresses, so it is impossible to forge the key unless an object is a valid owner(or a receiver) of the key, according to the object capability principles. + +## TraceKV + +`tracekv.Store` is a wrapper `KVStore` which provides operation tracing functionalities over the underlying `KVStore`. + +```go +type Store struct { + parent types.KVStore + writer io.Writer + context types.TraceContext +} +``` + +When each `KVStore` methods are called, `tracekv.Store` automatically logs `traceOperation` to the `Store.writer`. + +```go +type traceOperation struct { + Operation operation + Key string + Value string + Metadata map[string]interface{} +} +``` + +`traceOperation.Metadata` is filled with `Store.context` when it is not nil. `TraceContext` is a `map[string]interface{}`. + +## Transient + +`transient.Store` is a base-layer `KVStore` which is automatically discarded at the end of the block. + +```go +type Store struct { + dbadapter.Store +} +``` + +`Store.Store` is a `dbadapter.Store` with a `dbm.NewMemDB()`. All `KVStore` methods are reused. When `Store.Commit()` is called, new `dbadapter.Store` is assigned, discarding previous reference and making it garbage collected. diff --git a/store/memiterator.go b/store/cachekv/memiterator.go similarity index 98% rename from store/memiterator.go rename to store/cachekv/memiterator.go index c9a026cb5..972280c42 100644 --- a/store/memiterator.go +++ b/store/cachekv/memiterator.go @@ -1,4 +1,4 @@ -package store +package cachekv import ( "bytes" diff --git a/store/cachemergeiterator.go b/store/cachekv/mergeiterator.go similarity index 95% rename from store/cachemergeiterator.go rename to store/cachekv/mergeiterator.go index cd739500d..d45303e07 100644 --- a/store/cachemergeiterator.go +++ b/store/cachekv/mergeiterator.go @@ -1,7 +1,9 @@ -package store +package cachekv import ( "bytes" + + "github.com/cosmos/cosmos-sdk/store/types" ) // cacheMergeIterator merges a parent Iterator and a cache Iterator. @@ -12,14 +14,14 @@ import ( // // TODO: Optimize by memoizing. type cacheMergeIterator struct { - parent Iterator - cache Iterator + parent types.Iterator + cache types.Iterator ascending bool } -var _ Iterator = (*cacheMergeIterator)(nil) +var _ types.Iterator = (*cacheMergeIterator)(nil) -func newCacheMergeIterator(parent, cache Iterator, ascending bool) *cacheMergeIterator { +func newCacheMergeIterator(parent, cache types.Iterator, ascending bool) *cacheMergeIterator { iter := &cacheMergeIterator{ parent: parent, cache: cache, diff --git a/store/cachekv/store.go b/store/cachekv/store.go new file mode 100644 index 000000000..673a101ef --- /dev/null +++ b/store/cachekv/store.go @@ -0,0 +1,196 @@ +package cachekv + +import ( + "bytes" + "io" + "sort" + "sync" + + cmn "github.com/tendermint/tendermint/libs/common" + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/types" + + "github.com/cosmos/cosmos-sdk/store/tracekv" +) + +// If value is nil but deleted is false, it means the parent doesn't have the +// key. (No need to delete upon Write()) +type cValue struct { + value []byte + deleted bool + dirty bool +} + +// Store wraps an in-memory cache around an underlying types.KVStore. +type Store struct { + mtx sync.Mutex + cache map[string]cValue + parent types.KVStore +} + +var _ types.CacheKVStore = (*Store)(nil) + +// nolint +func NewStore(parent types.KVStore) *Store { + return &Store{ + cache: make(map[string]cValue), + parent: parent, + } +} + +// Implements Store. +func (store *Store) GetStoreType() types.StoreType { + return store.parent.GetStoreType() +} + +// Implements types.KVStore. +func (store *Store) Get(key []byte) (value []byte) { + store.mtx.Lock() + defer store.mtx.Unlock() + types.AssertValidKey(key) + + cacheValue, ok := store.cache[string(key)] + if !ok { + value = store.parent.Get(key) + store.setCacheValue(key, value, false, false) + } else { + value = cacheValue.value + } + + return value +} + +// Implements types.KVStore. +func (store *Store) Set(key []byte, value []byte) { + store.mtx.Lock() + defer store.mtx.Unlock() + types.AssertValidKey(key) + types.AssertValidValue(value) + + store.setCacheValue(key, value, false, true) +} + +// Implements types.KVStore. +func (store *Store) Has(key []byte) bool { + value := store.Get(key) + return value != nil +} + +// Implements types.KVStore. +func (store *Store) Delete(key []byte) { + store.mtx.Lock() + defer store.mtx.Unlock() + types.AssertValidKey(key) + + store.setCacheValue(key, nil, true, true) +} + +// Implements Cachetypes.KVStore. +func (store *Store) Write() { + store.mtx.Lock() + defer store.mtx.Unlock() + + // We need a copy of all of the keys. + // Not the best, but probably not a bottleneck depending. + keys := make([]string, 0, len(store.cache)) + for key, dbValue := range store.cache { + if dbValue.dirty { + keys = append(keys, key) + } + } + + sort.Strings(keys) + + // TODO: Consider allowing usage of Batch, which would allow the write to + // at least happen atomically. + for _, key := range keys { + cacheValue := store.cache[key] + if cacheValue.deleted { + store.parent.Delete([]byte(key)) + } else if cacheValue.value == nil { + // Skip, it already doesn't exist in parent. + } else { + store.parent.Set([]byte(key), cacheValue.value) + } + } + + // Clear the cache + store.cache = make(map[string]cValue) +} + +//---------------------------------------- +// To cache-wrap this Store further. + +// Implements CacheWrapper. +func (store *Store) CacheWrap() types.CacheWrap { + return NewStore(store) +} + +// CacheWrapWithTrace implements the CacheWrapper interface. +func (store *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + return NewStore(tracekv.NewStore(store, w, tc)) +} + +//---------------------------------------- +// Iteration + +// Implements types.KVStore. +func (store *Store) Iterator(start, end []byte) types.Iterator { + return store.iterator(start, end, true) +} + +// Implements types.KVStore. +func (store *Store) ReverseIterator(start, end []byte) types.Iterator { + return store.iterator(start, end, false) +} + +func (store *Store) iterator(start, end []byte, ascending bool) types.Iterator { + var parent, cache types.Iterator + + if ascending { + parent = store.parent.Iterator(start, end) + } else { + parent = store.parent.ReverseIterator(start, end) + } + + items := store.dirtyItems(start, end, ascending) + cache = newMemIterator(start, end, items) + + return newCacheMergeIterator(parent, cache, ascending) +} + +// Constructs a slice of dirty items, to use w/ memIterator. +func (store *Store) dirtyItems(start, end []byte, ascending bool) []cmn.KVPair { + items := make([]cmn.KVPair, 0) + + for key, cacheValue := range store.cache { + if !cacheValue.dirty { + continue + } + if dbm.IsKeyInDomain([]byte(key), start, end) { + items = append(items, cmn.KVPair{Key: []byte(key), Value: cacheValue.value}) + } + } + + sort.Slice(items, func(i, j int) bool { + if ascending { + return bytes.Compare(items[i].Key, items[j].Key) < 0 + } + return bytes.Compare(items[i].Key, items[j].Key) > 0 + }) + + return items +} + +//---------------------------------------- +// etc + +// Only entrypoint to mutate store.cache. +func (store *Store) setCacheValue(key, value []byte, deleted bool, dirty bool) { + store.cache[string(key)] = cValue{ + value: value, + deleted: deleted, + dirty: dirty, + } +} diff --git a/store/cachekvstore_test.go b/store/cachekv/store_test.go similarity index 91% rename from store/cachekvstore_test.go rename to store/cachekv/store_test.go index 8f43b0b3b..30c8d148a 100644 --- a/store/cachekvstore_test.go +++ b/store/cachekv/store_test.go @@ -1,4 +1,4 @@ -package store +package cachekv_test import ( "fmt" @@ -7,19 +7,23 @@ import ( "github.com/stretchr/testify/require" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/types" ) -func newCacheKVStore() CacheKVStore { - mem := dbStoreAdapter{dbm.NewMemDB()} - return NewCacheKVStore(mem) +func newCacheKVStore() types.CacheKVStore { + mem := dbadapter.Store{dbm.NewMemDB()} + return cachekv.NewStore(mem) } func keyFmt(i int) []byte { return bz(fmt.Sprintf("key%0.8d", i)) } func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) } func TestCacheKVStore(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - st := NewCacheKVStore(mem) + mem := dbadapter.Store{dbm.NewMemDB()} + st := cachekv.NewStore(mem) require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") @@ -45,11 +49,11 @@ func TestCacheKVStore(t *testing.T) { require.Equal(t, valFmt(2), st.Get(keyFmt(1))) // make a new one, check it - st = NewCacheKVStore(mem) + st = cachekv.NewStore(mem) require.Equal(t, valFmt(2), st.Get(keyFmt(1))) // make a new one and delete - should not be removed from mem - st = NewCacheKVStore(mem) + st = cachekv.NewStore(mem) st.Delete(keyFmt(1)) require.Empty(t, st.Get(keyFmt(1))) require.Equal(t, mem.Get(keyFmt(1)), valFmt(2)) @@ -61,14 +65,14 @@ func TestCacheKVStore(t *testing.T) { } func TestCacheKVStoreNoNilSet(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - st := NewCacheKVStore(mem) + mem := dbadapter.Store{dbm.NewMemDB()} + st := cachekv.NewStore(mem) require.Panics(t, func() { st.Set([]byte("key"), nil) }, "setting a nil value should panic") } func TestCacheKVStoreNested(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - st := NewCacheKVStore(mem) + mem := dbadapter.Store{dbm.NewMemDB()} + st := cachekv.NewStore(mem) // set. check its there on st and not on mem. st.Set(keyFmt(1), valFmt(1)) @@ -76,7 +80,7 @@ func TestCacheKVStoreNested(t *testing.T) { require.Equal(t, valFmt(1), st.Get(keyFmt(1))) // make a new from st and check - st2 := NewCacheKVStore(st) + st2 := cachekv.NewStore(st) require.Equal(t, valFmt(1), st2.Get(keyFmt(1))) // update the value on st2, check it only effects st2 @@ -318,7 +322,7 @@ func randInt(n int) int { } // useful for replaying a error case if we find one -func doOp(st CacheKVStore, truth dbm.DB, op int, args ...int) { +func doOp(st types.CacheKVStore, truth dbm.DB, op int, args ...int) { switch op { case opSet: k := args[0] @@ -341,7 +345,7 @@ func doOp(st CacheKVStore, truth dbm.DB, op int, args ...int) { } } -func doRandomOp(st CacheKVStore, truth dbm.DB, maxKey int) { +func doRandomOp(st types.CacheKVStore, truth dbm.DB, maxKey int) { r := randInt(totalOps) switch r { case opSet: @@ -368,7 +372,7 @@ func doRandomOp(st CacheKVStore, truth dbm.DB, maxKey int) { //------------------------------------------------------------------------------------------- // iterate over whole domain -func assertIterateDomain(t *testing.T, st KVStore, expectedN int) { +func assertIterateDomain(t *testing.T, st types.KVStore, expectedN int) { itr := st.Iterator(nil, nil) var i = 0 for ; itr.Valid(); itr.Next() { @@ -380,7 +384,7 @@ func assertIterateDomain(t *testing.T, st KVStore, expectedN int) { require.Equal(t, expectedN, i) } -func assertIterateDomainCheck(t *testing.T, st KVStore, mem dbm.DB, r []keyRange) { +func assertIterateDomainCheck(t *testing.T, st types.KVStore, mem dbm.DB, r []keyRange) { // iterate over each and check they match the other itr := st.Iterator(nil, nil) itr2 := mem.Iterator(nil, nil) // ground truth @@ -410,7 +414,7 @@ func assertIterateDomainCheck(t *testing.T, st KVStore, mem dbm.DB, r []keyRange require.False(t, itr2.Valid()) } -func assertIterateDomainCompare(t *testing.T, st KVStore, mem dbm.DB) { +func assertIterateDomainCompare(t *testing.T, st types.KVStore, mem dbm.DB) { // iterate over each and check they match the other itr := st.Iterator(nil, nil) itr2 := mem.Iterator(nil, nil) // ground truth @@ -418,7 +422,7 @@ func assertIterateDomainCompare(t *testing.T, st KVStore, mem dbm.DB) { checkIterators(t, itr2, itr) } -func checkIterators(t *testing.T, itr, itr2 Iterator) { +func checkIterators(t *testing.T, itr, itr2 types.Iterator) { for ; itr.Valid(); itr.Next() { require.True(t, itr2.Valid()) k, v := itr.Key(), itr.Value() @@ -433,14 +437,14 @@ func checkIterators(t *testing.T, itr, itr2 Iterator) { //-------------------------------------------------------- -func setRange(st KVStore, mem dbm.DB, start, end int) { +func setRange(st types.KVStore, mem dbm.DB, start, end int) { for i := start; i < end; i++ { st.Set(keyFmt(i), valFmt(i)) mem.Set(keyFmt(i), valFmt(i)) } } -func deleteRange(st KVStore, mem dbm.DB, start, end int) { +func deleteRange(st types.KVStore, mem dbm.DB, start, end int) { for i := start; i < end; i++ { st.Delete(keyFmt(i)) mem.Delete(keyFmt(i)) diff --git a/store/cachekvstore.go b/store/cachekvstore.go deleted file mode 100644 index 28c344fd0..000000000 --- a/store/cachekvstore.go +++ /dev/null @@ -1,199 +0,0 @@ -package store - -import ( - "bytes" - "io" - "sort" - "sync" - - cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" -) - -// If value is nil but deleted is false, it means the parent doesn't have the -// key. (No need to delete upon Write()) -type cValue struct { - value []byte - deleted bool - dirty bool -} - -// cacheKVStore wraps an in-memory cache around an underlying KVStore. -type cacheKVStore struct { - mtx sync.Mutex - cache map[string]cValue - parent KVStore -} - -var _ CacheKVStore = (*cacheKVStore)(nil) - -// nolint -func NewCacheKVStore(parent KVStore) *cacheKVStore { - return &cacheKVStore{ - cache: make(map[string]cValue), - parent: parent, - } -} - -// Implements Store. -func (ci *cacheKVStore) GetStoreType() StoreType { - return ci.parent.GetStoreType() -} - -// Implements KVStore. -func (ci *cacheKVStore) Get(key []byte) (value []byte) { - ci.mtx.Lock() - defer ci.mtx.Unlock() - assertValidKey(key) - - cacheValue, ok := ci.cache[string(key)] - if !ok { - value = ci.parent.Get(key) - ci.setCacheValue(key, value, false, false) - } else { - value = cacheValue.value - } - - return value -} - -// Implements KVStore. -func (ci *cacheKVStore) Set(key []byte, value []byte) { - ci.mtx.Lock() - defer ci.mtx.Unlock() - assertValidKey(key) - assertValidValue(value) - - ci.setCacheValue(key, value, false, true) -} - -// Implements KVStore. -func (ci *cacheKVStore) Has(key []byte) bool { - value := ci.Get(key) - return value != nil -} - -// Implements KVStore. -func (ci *cacheKVStore) Delete(key []byte) { - ci.mtx.Lock() - defer ci.mtx.Unlock() - assertValidKey(key) - - ci.setCacheValue(key, nil, true, true) -} - -// Implements KVStore -func (ci *cacheKVStore) Prefix(prefix []byte) KVStore { - return prefixStore{ci, prefix} -} - -// Implements KVStore -func (ci *cacheKVStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, ci) -} - -// Implements CacheKVStore. -func (ci *cacheKVStore) Write() { - ci.mtx.Lock() - defer ci.mtx.Unlock() - - // We need a copy of all of the keys. - // Not the best, but probably not a bottleneck depending. - keys := make([]string, 0, len(ci.cache)) - for key, dbValue := range ci.cache { - if dbValue.dirty { - keys = append(keys, key) - } - } - - sort.Strings(keys) - - // TODO: Consider allowing usage of Batch, which would allow the write to - // at least happen atomically. - for _, key := range keys { - cacheValue := ci.cache[key] - if cacheValue.deleted { - ci.parent.Delete([]byte(key)) - } else if cacheValue.value == nil { - // Skip, it already doesn't exist in parent. - } else { - ci.parent.Set([]byte(key), cacheValue.value) - } - } - - // Clear the cache - ci.cache = make(map[string]cValue) -} - -//---------------------------------------- -// To cache-wrap this cacheKVStore further. - -// Implements CacheWrapper. -func (ci *cacheKVStore) CacheWrap() CacheWrap { - return NewCacheKVStore(ci) -} - -// CacheWrapWithTrace implements the CacheWrapper interface. -func (ci *cacheKVStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap { - return NewCacheKVStore(NewTraceKVStore(ci, w, tc)) -} - -//---------------------------------------- -// Iteration - -// Implements KVStore. -func (ci *cacheKVStore) Iterator(start, end []byte) Iterator { - return ci.iterator(start, end, true) -} - -// Implements KVStore. -func (ci *cacheKVStore) ReverseIterator(start, end []byte) Iterator { - return ci.iterator(start, end, false) -} - -func (ci *cacheKVStore) iterator(start, end []byte, ascending bool) Iterator { - var parent, cache Iterator - - if ascending { - parent = ci.parent.Iterator(start, end) - } else { - parent = ci.parent.ReverseIterator(start, end) - } - - items := ci.dirtyItems(start, end, ascending) - cache = newMemIterator(start, end, items) - - return newCacheMergeIterator(parent, cache, ascending) -} - -// Constructs a slice of dirty items, to use w/ memIterator. -func (ci *cacheKVStore) dirtyItems(start, end []byte, ascending bool) []cmn.KVPair { - items := make([]cmn.KVPair, 0) - - for key, cacheValue := range ci.cache { - if !cacheValue.dirty { - continue - } - if dbm.IsKeyInDomain([]byte(key), start, end) { - items = append(items, cmn.KVPair{Key: []byte(key), Value: cacheValue.value}) - } - } - - sort.Slice(items, func(i, j int) bool { - if ascending { - return bytes.Compare(items[i].Key, items[j].Key) < 0 - } - return bytes.Compare(items[i].Key, items[j].Key) > 0 - }) - - return items -} - -// Only entrypoint to mutate ci.cache. -func (ci *cacheKVStore) setCacheValue(key, value []byte, deleted bool, dirty bool) { - ci.cache[string(key)] = cValue{ - value: value, - deleted: deleted, - dirty: dirty, - } -} diff --git a/store/cachemulti/store.go b/store/cachemulti/store.go new file mode 100644 index 000000000..e1257a338 --- /dev/null +++ b/store/cachemulti/store.go @@ -0,0 +1,135 @@ +package cachemulti + +import ( + "io" + + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/types" +) + +//---------------------------------------- +// Store + +// Store holds many cache-wrapped stores. +// Implements MultiStore. +// NOTE: a Store (and MultiStores in general) should never expose the +// keys for the substores. +type Store struct { + db types.CacheKVStore + stores map[types.StoreKey]types.CacheWrap + keys map[string]types.StoreKey + + traceWriter io.Writer + traceContext types.TraceContext +} + +var _ types.CacheMultiStore = Store{} + +func NewFromKVStore( + store types.KVStore, + stores map[types.StoreKey]types.CacheWrapper, keys map[string]types.StoreKey, + traceWriter io.Writer, traceContext types.TraceContext, +) Store { + cms := Store{ + db: cachekv.NewStore(store), + stores: make(map[types.StoreKey]types.CacheWrap, len(stores)), + keys: keys, + traceWriter: traceWriter, + traceContext: traceContext, + } + + for key, store := range stores { + if cms.TracingEnabled() { + cms.stores[key] = store.CacheWrapWithTrace(cms.traceWriter, cms.traceContext) + } else { + cms.stores[key] = store.CacheWrap() + } + } + + return cms +} + +func NewStore( + db dbm.DB, + stores map[types.StoreKey]types.CacheWrapper, keys map[string]types.StoreKey, + traceWriter io.Writer, traceContext types.TraceContext, +) Store { + return NewFromKVStore(dbadapter.Store{db}, stores, keys, traceWriter, traceContext) +} + +func newCacheMultiStoreFromCMS(cms Store) Store { + stores := make(map[types.StoreKey]types.CacheWrapper) + for k, v := range cms.stores { + stores[k] = v + } + return NewFromKVStore(cms.db, stores, nil, cms.traceWriter, cms.traceContext) +} + +// SetTracer sets the tracer for the MultiStore that the underlying +// stores will utilize to trace operations. A MultiStore is returned. +func (cms Store) SetTracer(w io.Writer) types.MultiStore { + cms.traceWriter = w + return cms +} + +// SetTracingContext updates the tracing context for the MultiStore by merging +// the given context with the existing context by key. Any existing keys will +// be overwritten. It is implied that the caller should update the context when +// necessary between tracing operations. It returns a modified MultiStore. +func (cms Store) SetTracingContext(tc types.TraceContext) types.MultiStore { + if cms.traceContext != nil { + for k, v := range tc { + cms.traceContext[k] = v + } + } else { + cms.traceContext = tc + } + + return cms +} + +// TracingEnabled returns if tracing is enabled for the MultiStore. +func (cms Store) TracingEnabled() bool { + return cms.traceWriter != nil +} + +// GetStoreType returns the type of the store. +func (cms Store) GetStoreType() types.StoreType { + return types.StoreTypeMulti +} + +// Write calls Write on each underlying store. +func (cms Store) Write() { + cms.db.Write() + for _, store := range cms.stores { + store.Write() + } +} + +// Implements CacheWrapper. +func (cms Store) CacheWrap() types.CacheWrap { + return cms.CacheMultiStore().(types.CacheWrap) +} + +// CacheWrapWithTrace implements the CacheWrapper interface. +func (cms Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { + return cms.CacheWrap() +} + +// Implements MultiStore. +func (cms Store) CacheMultiStore() types.CacheMultiStore { + return newCacheMultiStoreFromCMS(cms) +} + +// GetStore returns an underlying Store by key. +func (cms Store) GetStore(key types.StoreKey) types.Store { + return cms.stores[key].(types.Store) +} + +// GetKVStore returns an underlying KVStore by key. +func (cms Store) GetKVStore(key types.StoreKey) types.KVStore { + return cms.stores[key].(types.KVStore) +} diff --git a/store/cachemultistore.go b/store/cachemultistore.go deleted file mode 100644 index ee1977855..000000000 --- a/store/cachemultistore.go +++ /dev/null @@ -1,141 +0,0 @@ -package store - -import ( - "io" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -//---------------------------------------- -// cacheMultiStore - -// cacheMultiStore holds many cache-wrapped stores. -// Implements MultiStore. -// NOTE: a cacheMultiStore (and MultiStores in general) should never expose the -// keys for the substores. -type cacheMultiStore struct { - db CacheKVStore - stores map[StoreKey]CacheWrap - keysByName map[string]StoreKey - - traceWriter io.Writer - traceContext TraceContext -} - -var _ CacheMultiStore = cacheMultiStore{} - -func newCacheMultiStoreFromRMS(rms *rootMultiStore) cacheMultiStore { - cms := cacheMultiStore{ - db: NewCacheKVStore(dbStoreAdapter{rms.db}), - stores: make(map[StoreKey]CacheWrap, len(rms.stores)), - keysByName: rms.keysByName, - traceWriter: rms.traceWriter, - traceContext: rms.traceContext, - } - - for key, store := range rms.stores { - if cms.TracingEnabled() { - cms.stores[key] = store.CacheWrapWithTrace(cms.traceWriter, cms.traceContext) - } else { - cms.stores[key] = store.CacheWrap() - } - } - - return cms -} - -func newCacheMultiStoreFromCMS(cms cacheMultiStore) cacheMultiStore { - cms2 := cacheMultiStore{ - db: NewCacheKVStore(cms.db), - stores: make(map[StoreKey]CacheWrap, len(cms.stores)), - traceWriter: cms.traceWriter, - traceContext: cms.traceContext, - } - - for key, store := range cms.stores { - if cms2.TracingEnabled() { - cms2.stores[key] = store.CacheWrapWithTrace(cms2.traceWriter, cms2.traceContext) - } else { - cms2.stores[key] = store.CacheWrap() - } - } - - return cms2 -} - -// WithTracer sets the tracer for the MultiStore that the underlying -// stores will utilize to trace operations. A MultiStore is returned. -func (cms cacheMultiStore) WithTracer(w io.Writer) MultiStore { - cms.traceWriter = w - return cms -} - -// WithTracingContext updates the tracing context for the MultiStore by merging -// the given context with the existing context by key. Any existing keys will -// be overwritten. It is implied that the caller should update the context when -// necessary between tracing operations. It returns a modified MultiStore. -func (cms cacheMultiStore) WithTracingContext(tc TraceContext) MultiStore { - if cms.traceContext != nil { - for k, v := range tc { - cms.traceContext[k] = v - } - } else { - cms.traceContext = tc - } - - return cms -} - -// TracingEnabled returns if tracing is enabled for the MultiStore. -func (cms cacheMultiStore) TracingEnabled() bool { - return cms.traceWriter != nil -} - -// ResetTraceContext resets the current tracing context. -func (cms cacheMultiStore) ResetTraceContext() MultiStore { - cms.traceContext = nil - return cms -} - -// Implements Store. -func (cms cacheMultiStore) GetStoreType() StoreType { - return sdk.StoreTypeMulti -} - -// Implements CacheMultiStore. -func (cms cacheMultiStore) Write() { - cms.db.Write() - for _, store := range cms.stores { - store.Write() - } -} - -// Implements CacheWrapper. -func (cms cacheMultiStore) CacheWrap() CacheWrap { - return cms.CacheMultiStore().(CacheWrap) -} - -// CacheWrapWithTrace implements the CacheWrapper interface. -func (cms cacheMultiStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap { - return cms.CacheWrap() -} - -// Implements MultiStore. -func (cms cacheMultiStore) CacheMultiStore() CacheMultiStore { - return newCacheMultiStoreFromCMS(cms) -} - -// Implements MultiStore. -func (cms cacheMultiStore) GetStore(key StoreKey) Store { - return cms.stores[key].(Store) -} - -// Implements MultiStore. -func (cms cacheMultiStore) GetKVStore(key StoreKey) KVStore { - return cms.stores[key].(KVStore) -} - -// Implements MultiStore. -func (cms cacheMultiStore) GetKVStoreWithGas(meter sdk.GasMeter, config sdk.GasConfig, key StoreKey) KVStore { - return NewGasKVStore(meter, config, cms.GetKVStore(key)) -} diff --git a/store/dbadapter/store.go b/store/dbadapter/store.go new file mode 100644 index 000000000..b96c6f5cc --- /dev/null +++ b/store/dbadapter/store.go @@ -0,0 +1,34 @@ +package dbadapter + +import ( + "io" + + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/tracekv" + "github.com/cosmos/cosmos-sdk/store/types" +) + +// Wrapper type for dbm.Db with implementation of KVStore +type Store struct { + dbm.DB +} + +// GetStoreType returns the type of the store. +func (Store) GetStoreType() types.StoreType { + return types.StoreTypeDB +} + +// CacheWrap cache wraps the underlying store. +func (dsa Store) CacheWrap() types.CacheWrap { + return cachekv.NewStore(dsa) +} + +// CacheWrapWithTrace implements KVStore. +func (dsa Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + return cachekv.NewStore(tracekv.NewStore(dsa, w, tc)) +} + +// dbm.DB implements KVStore so we can CacheKVStore it. +var _ types.KVStore = Store{} diff --git a/store/dbstoreadapter.go b/store/dbstoreadapter.go deleted file mode 100644 index b662bcf45..000000000 --- a/store/dbstoreadapter.go +++ /dev/null @@ -1,67 +0,0 @@ -package store - -import ( - "io" - - dbm "github.com/tendermint/tendermint/libs/db" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Wrapper type for dbm.Db with implementation of KVStore -type dbStoreAdapter struct { - dbm.DB -} - -// Implements Store. -func (dbStoreAdapter) GetStoreType() StoreType { - return sdk.StoreTypeDB -} - -// Implements KVStore. -func (dsa dbStoreAdapter) CacheWrap() CacheWrap { - return NewCacheKVStore(dsa) -} - -// CacheWrapWithTrace implements the KVStore interface. -func (dsa dbStoreAdapter) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap { - return NewCacheKVStore(NewTraceKVStore(dsa, w, tc)) -} - -// Implements KVStore -func (dsa dbStoreAdapter) Prefix(prefix []byte) KVStore { - return prefixStore{dsa, prefix} -} - -// Implements KVStore -func (dsa dbStoreAdapter) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, dsa) -} - -// dbm.DB implements KVStore so we can CacheKVStore it. -var _ KVStore = dbStoreAdapter{} - -//---------------------------------------- -// commitDBStoreWrapper should only be used for simulation/debugging, -// as it doesn't compute any commit hash, and it cannot load older state. - -// Wrapper type for dbm.Db with implementation of KVStore -type commitDBStoreAdapter struct { - dbStoreAdapter -} - -func (cdsa commitDBStoreAdapter) Commit() CommitID { - return CommitID{ - Version: -1, - Hash: []byte("FAKE_HASH"), - } -} - -func (cdsa commitDBStoreAdapter) LastCommitID() CommitID { - return CommitID{ - Version: -1, - Hash: []byte("FAKE_HASH"), - } -} - -func (cdsa commitDBStoreAdapter) SetPruning(_ PruningOptions) {} diff --git a/store/firstlast.go b/store/firstlast.go index a47f1396d..708a7d0d3 100644 --- a/store/firstlast.go +++ b/store/firstlast.go @@ -4,6 +4,8 @@ import ( "bytes" cmn "github.com/tendermint/tendermint/libs/common" + + "github.com/cosmos/cosmos-sdk/store/types" ) // Gets the first item. @@ -22,7 +24,7 @@ func Last(st KVStore, start, end []byte) (kv cmn.KVPair, ok bool) { iter := st.ReverseIterator(end, start) if !iter.Valid() { if v := st.Get(start); v != nil { - return cmn.KVPair{Key: cp(start), Value: cp(v)}, true + return cmn.KVPair{Key: types.Cp(start), Value: types.Cp(v)}, true } return kv, false } diff --git a/store/gaskvstore.go b/store/gaskv/store.go similarity index 55% rename from store/gaskvstore.go rename to store/gaskv/store.go index 790ae612f..e6bdbdc21 100644 --- a/store/gaskvstore.go +++ b/store/gaskv/store.go @@ -1,25 +1,25 @@ -package store +package gaskv import ( "io" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/types" ) -var _ KVStore = &gasKVStore{} +var _ types.KVStore = &Store{} -// gasKVStore applies gas tracking to an underlying KVStore. It implements the +// Store applies gas tracking to an underlying KVStore. It implements the // KVStore interface. -type gasKVStore struct { - gasMeter sdk.GasMeter - gasConfig sdk.GasConfig - parent sdk.KVStore +type Store struct { + gasMeter types.GasMeter + gasConfig types.GasConfig + parent types.KVStore } -// NewGasKVStore returns a reference to a new GasKVStore. +// NewStore returns a reference to a new GasKVStore. // nolint -func NewGasKVStore(gasMeter sdk.GasMeter, gasConfig sdk.GasConfig, parent sdk.KVStore) *gasKVStore { - kvs := &gasKVStore{ +func NewStore(parent types.KVStore, gasMeter types.GasMeter, gasConfig types.GasConfig) *Store { + kvs := &Store{ gasMeter: gasMeter, gasConfig: gasConfig, parent: parent, @@ -28,62 +28,47 @@ func NewGasKVStore(gasMeter sdk.GasMeter, gasConfig sdk.GasConfig, parent sdk.KV } // Implements Store. -func (gs *gasKVStore) GetStoreType() sdk.StoreType { +func (gs *Store) GetStoreType() types.StoreType { return gs.parent.GetStoreType() } // Implements KVStore. -func (gs *gasKVStore) Get(key []byte) (value []byte) { - gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostFlat, sdk.GasReadCostFlatDesc) +func (gs *Store) Get(key []byte) (value []byte) { + gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostFlat, types.GasReadCostFlatDesc) value = gs.parent.Get(key) // TODO overflow-safe math? - gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostPerByte*sdk.Gas(len(value)), sdk.GasReadPerByteDesc) + gs.gasMeter.ConsumeGas(gs.gasConfig.ReadCostPerByte*types.Gas(len(value)), types.GasReadPerByteDesc) return value } // Implements KVStore. -func (gs *gasKVStore) Set(key []byte, value []byte) { - assertValidValue(value) - gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostFlat, sdk.GasWriteCostFlatDesc) +func (gs *Store) Set(key []byte, value []byte) { + types.AssertValidValue(value) + gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostFlat, types.GasWriteCostFlatDesc) // TODO overflow-safe math? - gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostPerByte*sdk.Gas(len(value)), sdk.GasWritePerByteDesc) + gs.gasMeter.ConsumeGas(gs.gasConfig.WriteCostPerByte*types.Gas(len(value)), types.GasWritePerByteDesc) gs.parent.Set(key, value) } // Implements KVStore. -func (gs *gasKVStore) Has(key []byte) bool { - gs.gasMeter.ConsumeGas(gs.gasConfig.HasCost, sdk.GasHasDesc) +func (gs *Store) Has(key []byte) bool { + gs.gasMeter.ConsumeGas(gs.gasConfig.HasCost, types.GasHasDesc) return gs.parent.Has(key) } // Implements KVStore. -func (gs *gasKVStore) Delete(key []byte) { +func (gs *Store) Delete(key []byte) { // charge gas to prevent certain attack vectors even though space is being freed - gs.gasMeter.ConsumeGas(gs.gasConfig.DeleteCost, sdk.GasDeleteDesc) + gs.gasMeter.ConsumeGas(gs.gasConfig.DeleteCost, types.GasDeleteDesc) gs.parent.Delete(key) } -// Implements KVStore -func (gs *gasKVStore) Prefix(prefix []byte) KVStore { - // Keep gasstore layer at the top - return &gasKVStore{ - gasMeter: gs.gasMeter, - gasConfig: gs.gasConfig, - parent: prefixStore{gs.parent, prefix}, - } -} - -// Implements KVStore -func (gs *gasKVStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, gs) -} - // Iterator implements the KVStore interface. It returns an iterator which // incurs a flat gas cost for seeking to the first key/value pair and a variable // gas cost based on the current value's length if the iterator is valid. -func (gs *gasKVStore) Iterator(start, end []byte) sdk.Iterator { +func (gs *Store) Iterator(start, end []byte) types.Iterator { return gs.iterator(start, end, true) } @@ -91,22 +76,22 @@ func (gs *gasKVStore) Iterator(start, end []byte) sdk.Iterator { // iterator which incurs a flat gas cost for seeking to the first key/value pair // and a variable gas cost based on the current value's length if the iterator // is valid. -func (gs *gasKVStore) ReverseIterator(start, end []byte) sdk.Iterator { +func (gs *Store) ReverseIterator(start, end []byte) types.Iterator { return gs.iterator(start, end, false) } // Implements KVStore. -func (gs *gasKVStore) CacheWrap() sdk.CacheWrap { +func (gs *Store) CacheWrap() types.CacheWrap { panic("cannot CacheWrap a GasKVStore") } // CacheWrapWithTrace implements the KVStore interface. -func (gs *gasKVStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap { +func (gs *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { panic("cannot CacheWrapWithTrace a GasKVStore") } -func (gs *gasKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator { - var parent sdk.Iterator +func (gs *Store) iterator(start, end []byte, ascending bool) types.Iterator { + var parent types.Iterator if ascending { parent = gs.parent.Iterator(start, end) } else { @@ -122,12 +107,12 @@ func (gs *gasKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator { } type gasIterator struct { - gasMeter sdk.GasMeter - gasConfig sdk.GasConfig - parent sdk.Iterator + gasMeter types.GasMeter + gasConfig types.GasConfig + parent types.Iterator } -func newGasIterator(gasMeter sdk.GasMeter, gasConfig sdk.GasConfig, parent sdk.Iterator) sdk.Iterator { +func newGasIterator(gasMeter types.GasMeter, gasConfig types.GasConfig, parent types.Iterator) types.Iterator { return &gasIterator{ gasMeter: gasMeter, gasConfig: gasConfig, @@ -180,6 +165,7 @@ func (gi *gasIterator) Close() { func (gi *gasIterator) consumeSeekGas() { value := gi.Value() - gi.gasMeter.ConsumeGas(gi.gasConfig.ReadCostPerByte*sdk.Gas(len(value)), sdk.GasValuePerByteDesc) - gi.gasMeter.ConsumeGas(gi.gasConfig.IterNextCostFlat, sdk.GasIterNextCostFlatDesc) + gi.gasMeter.ConsumeGas(gi.gasConfig.ReadCostPerByte*types.Gas(len(value)), types.GasValuePerByteDesc) + gi.gasMeter.ConsumeGas(gi.gasConfig.IterNextCostFlat, types.GasIterNextCostFlatDesc) + } diff --git a/store/gaskv/store_test.go b/store/gaskv/store_test.go new file mode 100644 index 000000000..8d6acd86e --- /dev/null +++ b/store/gaskv/store_test.go @@ -0,0 +1,78 @@ +package gaskv_test + +import ( + "fmt" + "testing" + + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/gaskv" + stypes "github.com/cosmos/cosmos-sdk/store/types" + + "github.com/stretchr/testify/require" +) + +func newGasKVStore() stypes.KVStore { + meter := stypes.NewGasMeter(10000) + mem := dbadapter.Store{dbm.NewMemDB()} + return gaskv.NewStore(mem, meter, stypes.KVGasConfig()) +} + +func bz(s string) []byte { return []byte(s) } + +func keyFmt(i int) []byte { return bz(fmt.Sprintf("key%0.8d", i)) } +func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) } + +func TestGasKVStoreBasic(t *testing.T) { + mem := dbadapter.Store{dbm.NewMemDB()} + meter := stypes.NewGasMeter(10000) + st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + st.Set(keyFmt(1), valFmt(1)) + require.Equal(t, valFmt(1), st.Get(keyFmt(1))) + st.Delete(keyFmt(1)) + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + require.Equal(t, meter.GasConsumed(), stypes.Gas(6429)) +} + +func TestGasKVStoreIterator(t *testing.T) { + mem := dbadapter.Store{dbm.NewMemDB()} + meter := stypes.NewGasMeter(10000) + st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") + require.Empty(t, st.Get(keyFmt(2)), "Expected `key2` to be empty") + st.Set(keyFmt(1), valFmt(1)) + st.Set(keyFmt(2), valFmt(2)) + iterator := st.Iterator(nil, nil) + ka := iterator.Key() + require.Equal(t, ka, keyFmt(1)) + va := iterator.Value() + require.Equal(t, va, valFmt(1)) + iterator.Next() + kb := iterator.Key() + require.Equal(t, kb, keyFmt(2)) + vb := iterator.Value() + require.Equal(t, vb, valFmt(2)) + iterator.Next() + require.False(t, iterator.Valid()) + require.Panics(t, iterator.Next) + require.Equal(t, meter.GasConsumed(), stypes.Gas(6987)) +} + +func TestGasKVStoreOutOfGasSet(t *testing.T) { + mem := dbadapter.Store{dbm.NewMemDB()} + meter := stypes.NewGasMeter(0) + st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") +} + +func TestGasKVStoreOutOfGasIterator(t *testing.T) { + mem := dbadapter.Store{dbm.NewMemDB()} + meter := stypes.NewGasMeter(20000) + st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + st.Set(keyFmt(1), valFmt(1)) + iterator := st.Iterator(nil, nil) + iterator.Next() + require.Panics(t, func() { iterator.Value() }, "Expected out-of-gas") +} diff --git a/store/gaskvstore_test.go b/store/gaskvstore_test.go deleted file mode 100644 index c3a3e3928..000000000 --- a/store/gaskvstore_test.go +++ /dev/null @@ -1,111 +0,0 @@ -package store - -import ( - "testing" - - "github.com/tendermint/iavl" - dbm "github.com/tendermint/tendermint/libs/db" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/stretchr/testify/require" -) - -func newGasKVStore() KVStore { - meter := sdk.NewGasMeter(10000) - mem := dbStoreAdapter{dbm.NewMemDB()} - return NewGasKVStore(meter, sdk.KVGasConfig(), mem) -} - -func TestGasKVStoreBasic(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := sdk.NewGasMeter(10000) - st := NewGasKVStore(meter, sdk.KVGasConfig(), mem) - require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - st.Set(keyFmt(1), valFmt(1)) - require.Equal(t, valFmt(1), st.Get(keyFmt(1))) - st.Delete(keyFmt(1)) - require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - require.Equal(t, meter.GasConsumed(), sdk.Gas(6429)) -} - -func TestGasKVStoreNoNilSet(t *testing.T) { - st := newGasKVStore() - require.Panics(t, func() { st.Set([]byte("key"), nil) }, "setting a nil value should panic") -} - -func TestGasKVStoreIterator(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := sdk.NewGasMeter(10000) - st := NewGasKVStore(meter, sdk.KVGasConfig(), mem) - require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - require.Empty(t, st.Get(keyFmt(2)), "Expected `key2` to be empty") - st.Set(keyFmt(1), valFmt(1)) - st.Set(keyFmt(2), valFmt(2)) - iterator := st.Iterator(nil, nil) - ka := iterator.Key() - require.Equal(t, ka, keyFmt(1)) - va := iterator.Value() - require.Equal(t, va, valFmt(1)) - iterator.Next() - kb := iterator.Key() - require.Equal(t, kb, keyFmt(2)) - vb := iterator.Value() - require.Equal(t, vb, valFmt(2)) - iterator.Next() - require.False(t, iterator.Valid()) - require.Panics(t, iterator.Next) - require.Equal(t, meter.GasConsumed(), sdk.Gas(6987)) -} - -func TestGasKVStoreOutOfGasSet(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := sdk.NewGasMeter(0) - st := NewGasKVStore(meter, sdk.KVGasConfig(), mem) - require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") -} - -func TestGasKVStoreOutOfGasIterator(t *testing.T) { - mem := dbStoreAdapter{dbm.NewMemDB()} - meter := sdk.NewGasMeter(20000) - st := NewGasKVStore(meter, sdk.KVGasConfig(), mem) - st.Set(keyFmt(1), valFmt(1)) - iterator := st.Iterator(nil, nil) - iterator.Next() - require.Panics(t, func() { iterator.Value() }, "Expected out-of-gas") -} - -func testGasKVStoreWrap(t *testing.T, store KVStore) { - meter := sdk.NewGasMeter(100000) - - store = store.Gas(meter, sdk.GasConfig{HasCost: 10}) - require.Equal(t, uint64(0), meter.GasConsumed()) - - store.Has([]byte("key")) - require.Equal(t, uint64(10), meter.GasConsumed()) - - store = store.Gas(meter, sdk.GasConfig{HasCost: 20}) - - store.Has([]byte("key")) - require.Equal(t, uint64(40), meter.GasConsumed()) -} - -func TestGasKVStoreWrap(t *testing.T) { - db := dbm.NewMemDB() - tree := iavl.NewMutableTree(db, cacheSize) - iavl := newIAVLStore(tree, numRecent, storeEvery) - testGasKVStoreWrap(t, iavl) - - st := NewCacheKVStore(iavl) - testGasKVStoreWrap(t, st) - - pref := st.Prefix([]byte("prefix")) - testGasKVStoreWrap(t, pref) - - dsa := dbStoreAdapter{dbm.NewMemDB()} - testGasKVStoreWrap(t, dsa) - - ts := newTransientStore() - testGasKVStoreWrap(t, ts) - -} diff --git a/store/iavlstore.go b/store/iavl/store.go similarity index 75% rename from store/iavlstore.go rename to store/iavl/store.go index 9bb6a8cb1..29e375b55 100644 --- a/store/iavlstore.go +++ b/store/iavl/store.go @@ -1,4 +1,4 @@ -package store +package iavl import ( "fmt" @@ -11,7 +11,11 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" - sdk "github.com/cosmos/cosmos-sdk/types" + stypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/tracekv" ) const ( @@ -19,25 +23,25 @@ const ( ) // load the iavl store -func LoadIAVLStore(db dbm.DB, id CommitID, pruning sdk.PruningOptions) (CommitStore, error) { +func LoadStore(db dbm.DB, id types.CommitID, pruning types.PruningOptions) (types.CommitStore, error) { tree := iavl.NewMutableTree(db, defaultIAVLCacheSize) _, err := tree.LoadVersion(id.Version) if err != nil { return nil, err } - iavl := newIAVLStore(tree, int64(0), int64(0)) + iavl := UnsafeNewStore(tree, int64(0), int64(0)) iavl.SetPruning(pruning) return iavl, nil } //---------------------------------------- -var _ KVStore = (*iavlStore)(nil) -var _ CommitStore = (*iavlStore)(nil) -var _ Queryable = (*iavlStore)(nil) +var _ types.KVStore = (*Store)(nil) +var _ types.CommitStore = (*Store)(nil) +var _ types.Queryable = (*Store)(nil) -// iavlStore Implements KVStore and CommitStore. -type iavlStore struct { +// Store Implements types.KVStore and CommitStore. +type Store struct { // The underlying tree. tree *iavl.MutableTree @@ -56,8 +60,8 @@ type iavlStore struct { // CONTRACT: tree should be fully loaded. // nolint: unparam -func newIAVLStore(tree *iavl.MutableTree, numRecent int64, storeEvery int64) *iavlStore { - st := &iavlStore{ +func UnsafeNewStore(tree *iavl.MutableTree, numRecent int64, storeEvery int64) *Store { + st := &Store{ tree: tree, numRecent: numRecent, storeEvery: storeEvery, @@ -66,7 +70,7 @@ func newIAVLStore(tree *iavl.MutableTree, numRecent int64, storeEvery int64) *ia } // Implements Committer. -func (st *iavlStore) Commit() CommitID { +func (st *Store) Commit() types.CommitID { // Save a new version. hash, version, err := st.tree.SaveVersion() if err != nil { @@ -86,85 +90,75 @@ func (st *iavlStore) Commit() CommitID { } } - return CommitID{ + return types.CommitID{ Version: version, Hash: hash, } } // Implements Committer. -func (st *iavlStore) LastCommitID() CommitID { - return CommitID{ +func (st *Store) LastCommitID() types.CommitID { + return types.CommitID{ Version: st.tree.Version(), Hash: st.tree.Hash(), } } // Implements Committer. -func (st *iavlStore) SetPruning(opt sdk.PruningOptions) { +func (st *Store) SetPruning(opt types.PruningOptions) { st.numRecent = opt.KeepRecent() st.storeEvery = opt.KeepEvery() } // VersionExists returns whether or not a given version is stored. -func (st *iavlStore) VersionExists(version int64) bool { +func (st *Store) VersionExists(version int64) bool { return st.tree.VersionExists(version) } // Implements Store. -func (st *iavlStore) GetStoreType() StoreType { - return sdk.StoreTypeIAVL +func (st *Store) GetStoreType() types.StoreType { + return types.StoreTypeIAVL } // Implements Store. -func (st *iavlStore) CacheWrap() CacheWrap { - return NewCacheKVStore(st) +func (st *Store) CacheWrap() types.CacheWrap { + return cachekv.NewStore(st) } // CacheWrapWithTrace implements the Store interface. -func (st *iavlStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap { - return NewCacheKVStore(NewTraceKVStore(st, w, tc)) +func (st *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + return cachekv.NewStore(tracekv.NewStore(st, w, tc)) } -// Implements KVStore. -func (st *iavlStore) Set(key, value []byte) { - assertValidValue(value) +// Implements types.KVStore. +func (st *Store) Set(key, value []byte) { + stypes.AssertValidValue(value) st.tree.Set(key, value) } -// Implements KVStore. -func (st *iavlStore) Get(key []byte) (value []byte) { +// Implements types.KVStore. +func (st *Store) Get(key []byte) (value []byte) { _, v := st.tree.Get(key) return v } -// Implements KVStore. -func (st *iavlStore) Has(key []byte) (exists bool) { +// Implements types.KVStore. +func (st *Store) Has(key []byte) (exists bool) { return st.tree.Has(key) } -// Implements KVStore. -func (st *iavlStore) Delete(key []byte) { +// Implements types.KVStore. +func (st *Store) Delete(key []byte) { st.tree.Remove(key) } -// Implements KVStore -func (st *iavlStore) Prefix(prefix []byte) KVStore { - return prefixStore{st, prefix} -} - -// Implements KVStore -func (st *iavlStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, st) -} - -// Implements KVStore. -func (st *iavlStore) Iterator(start, end []byte) Iterator { +// Implements types.KVStore. +func (st *Store) Iterator(start, end []byte) types.Iterator { return newIAVLIterator(st.tree.ImmutableTree, start, end, true) } -// Implements KVStore. -func (st *iavlStore) ReverseIterator(start, end []byte) Iterator { +// Implements types.KVStore. +func (st *Store) ReverseIterator(start, end []byte) types.Iterator { return newIAVLIterator(st.tree.ImmutableTree, start, end, false) } @@ -189,10 +183,10 @@ func getHeight(tree *iavl.MutableTree, req abci.RequestQuery) int64 { // If latest-1 is not present, use latest (which must be present) // if you care to have the latest data to see a tx results, you must // explicitly set the height you want to see -func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { +func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { if len(req.Data) == 0 { msg := "Query cannot be zero length" - return sdk.ErrTxDecode(msg).QueryResult() + return types.ErrTxDecode(msg).QueryResult() } tree := st.tree @@ -237,14 +231,14 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { } case "/subspace": - var KVs []KVPair + var KVs []types.KVPair subspace := req.Data res.Key = subspace - iterator := sdk.KVStorePrefixIterator(st, subspace) + iterator := stypes.KVStorePrefixIterator(st, subspace) for ; iterator.Valid(); iterator.Next() { - KVs = append(KVs, KVPair{Key: iterator.Key(), Value: iterator.Value()}) + KVs = append(KVs, types.KVPair{Key: iterator.Key(), Value: iterator.Value()}) } iterator.Close() @@ -252,7 +246,7 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { default: msg := fmt.Sprintf("Unexpected Query path: %v", req.Path) - return sdk.ErrUnknownRequest(msg).QueryResult() + return types.ErrUnknownRequest(msg).QueryResult() } return @@ -260,7 +254,7 @@ func (st *iavlStore) Query(req abci.RequestQuery) (res abci.ResponseQuery) { //---------------------------------------- -// Implements Iterator. +// Implements types.Iterator. type iavlIterator struct { // Underlying store tree *iavl.ImmutableTree @@ -289,7 +283,7 @@ type iavlIterator struct { value []byte // The current value } -var _ Iterator = (*iavlIterator)(nil) +var _ types.Iterator = (*iavlIterator)(nil) // newIAVLIterator will create a new iavlIterator. // CONTRACT: Caller must release the iavlIterator, as each one creates a new @@ -297,8 +291,8 @@ var _ Iterator = (*iavlIterator)(nil) func newIAVLIterator(tree *iavl.ImmutableTree, start, end []byte, ascending bool) *iavlIterator { iter := &iavlIterator{ tree: tree, - start: cp(start), - end: cp(end), + start: stypes.Cp(start), + end: stypes.Cp(end), ascending: ascending, iterCh: make(chan cmn.KVPair, 0), // Set capacity > 0? quitCh: make(chan struct{}), @@ -331,12 +325,12 @@ func (iter *iavlIterator) initRoutine() { close(iter.initCh) } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Domain() (start, end []byte) { return iter.start, iter.end } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Valid() bool { iter.waitInit() iter.mtx.Lock() @@ -346,7 +340,7 @@ func (iter *iavlIterator) Valid() bool { return validity } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Next() { iter.waitInit() iter.mtx.Lock() @@ -356,7 +350,7 @@ func (iter *iavlIterator) Next() { iter.mtx.Unlock() } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Key() []byte { iter.waitInit() iter.mtx.Lock() @@ -367,7 +361,7 @@ func (iter *iavlIterator) Key() []byte { return key } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Value() []byte { iter.waitInit() iter.mtx.Lock() @@ -378,7 +372,7 @@ func (iter *iavlIterator) Value() []byte { return val } -// Implements Iterator. +// Implements types.Iterator. func (iter *iavlIterator) Close() { close(iter.quitCh) } @@ -422,14 +416,3 @@ func (iter *iavlIterator) assertIsValid(unlockMutex bool) { panic("invalid iterator") } } - -//---------------------------------------- - -func cp(bz []byte) (ret []byte) { - if bz == nil { - return nil - } - ret = make([]byte, len(bz)) - copy(ret, bz) - return ret -} diff --git a/store/iavlstore_test.go b/store/iavl/store_test.go similarity index 88% rename from store/iavlstore_test.go rename to store/iavl/store_test.go index 78140ba32..360fa712c 100644 --- a/store/iavlstore_test.go +++ b/store/iavl/store_test.go @@ -1,4 +1,4 @@ -package store +package iavl import ( "fmt" @@ -11,7 +11,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types" ) var ( @@ -29,7 +29,7 @@ var ( ) // make a tree with data from above and save it -func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, CommitID) { +func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, types.CommitID) { tree := iavl.NewMutableTree(db, cacheSize) for k, v := range treeData { tree.Set([]byte(k), []byte(v)) @@ -41,13 +41,13 @@ func newAlohaTree(t *testing.T, db dbm.DB) (*iavl.MutableTree, CommitID) { } hash, ver, err := tree.SaveVersion() require.Nil(t, err) - return tree, CommitID{ver, hash} + return tree, types.CommitID{ver, hash} } func TestIAVLStoreGetSetHasDelete(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) key := "hello" @@ -72,14 +72,14 @@ func TestIAVLStoreGetSetHasDelete(t *testing.T) { func TestIAVLStoreNoNilSet(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) require.Panics(t, func() { iavlStore.Set([]byte("key"), nil) }, "setting a nil value should panic") } func TestIAVLIterator(t *testing.T) { db := dbm.NewMemDB() tree, _ := newAlohaTree(t, db) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) iter := iavlStore.Iterator([]byte("aloha"), []byte("hellz")) expected := []string{"aloha", "hello"} var i int @@ -152,7 +152,7 @@ func TestIAVLIterator(t *testing.T) { func TestIAVLReverseIterator(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) iavlStore.Set([]byte{0x00}, []byte("0")) iavlStore.Set([]byte{0x00, 0x00}, []byte("0 0")) @@ -183,7 +183,7 @@ func TestIAVLReverseIterator(t *testing.T) { func TestIAVLPrefixIterator(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) iavlStore.Set([]byte("test1"), []byte("test1")) iavlStore.Set([]byte("test2"), []byte("test2")) @@ -197,7 +197,7 @@ func TestIAVLPrefixIterator(t *testing.T) { var i int - iter := sdk.KVStorePrefixIterator(iavlStore, []byte("test")) + iter := types.KVStorePrefixIterator(iavlStore, []byte("test")) expected := []string{"test1", "test2", "test3"} for i = 0; iter.Valid(); iter.Next() { expectedKey := expected[i] @@ -209,7 +209,7 @@ func TestIAVLPrefixIterator(t *testing.T) { iter.Close() require.Equal(t, len(expected), i) - iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)}) + iter = types.KVStorePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)}) expected2 := [][]byte{ {byte(55), byte(255), byte(255), byte(0)}, {byte(55), byte(255), byte(255), byte(1)}, @@ -225,7 +225,7 @@ func TestIAVLPrefixIterator(t *testing.T) { iter.Close() require.Equal(t, len(expected), i) - iter = sdk.KVStorePrefixIterator(iavlStore, []byte{byte(255), byte(255)}) + iter = types.KVStorePrefixIterator(iavlStore, []byte{byte(255), byte(255)}) expected2 = [][]byte{ {byte(255), byte(255), byte(0)}, {byte(255), byte(255), byte(1)}, @@ -245,7 +245,7 @@ func TestIAVLPrefixIterator(t *testing.T) { func TestIAVLReversePrefixIterator(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) iavlStore.Set([]byte("test1"), []byte("test1")) iavlStore.Set([]byte("test2"), []byte("test2")) @@ -259,7 +259,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) { var i int - iter := sdk.KVStoreReversePrefixIterator(iavlStore, []byte("test")) + iter := types.KVStoreReversePrefixIterator(iavlStore, []byte("test")) expected := []string{"test3", "test2", "test1"} for i = 0; iter.Valid(); iter.Next() { expectedKey := expected[i] @@ -270,7 +270,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) { } require.Equal(t, len(expected), i) - iter = sdk.KVStoreReversePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)}) + iter = types.KVStoreReversePrefixIterator(iavlStore, []byte{byte(55), byte(255), byte(255)}) expected2 := [][]byte{ {byte(55), byte(255), byte(255), byte(255)}, {byte(55), byte(255), byte(255), byte(1)}, @@ -285,7 +285,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) { } require.Equal(t, len(expected), i) - iter = sdk.KVStoreReversePrefixIterator(iavlStore, []byte{byte(255), byte(255)}) + iter = types.KVStoreReversePrefixIterator(iavlStore, []byte{byte(255), byte(255)}) expected2 = [][]byte{ {byte(255), byte(255), byte(255)}, {byte(255), byte(255), byte(1)}, @@ -301,7 +301,7 @@ func TestIAVLReversePrefixIterator(t *testing.T) { require.Equal(t, len(expected), i) } -func nextVersion(iavl *iavlStore) { +func nextVersion(iavl *Store) { key := []byte(fmt.Sprintf("Key for tree: %d", iavl.LastCommitID().Version)) value := []byte(fmt.Sprintf("Value for tree: %d", iavl.LastCommitID().Version)) iavl.Set(key, value) @@ -364,7 +364,7 @@ type pruneState struct { func testPruning(t *testing.T, numRecent int64, storeEvery int64, states []pruneState) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) for step, state := range states { for _, ver := range state.stored { require.True(t, iavlStore.VersionExists(ver), @@ -383,7 +383,7 @@ func testPruning(t *testing.T, numRecent int64, storeEvery int64, states []prune func TestIAVLNoPrune(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, int64(1)) + iavlStore := UnsafeNewStore(tree, numRecent, int64(1)) nextVersion(iavlStore) for i := 1; i < 100; i++ { for j := 1; j <= i; j++ { @@ -398,7 +398,7 @@ func TestIAVLNoPrune(t *testing.T) { func TestIAVLPruneEverything(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, int64(0), int64(0)) + iavlStore := UnsafeNewStore(tree, int64(0), int64(0)) nextVersion(iavlStore) for i := 1; i < 100; i++ { for j := 1; j < i; j++ { @@ -416,19 +416,19 @@ func TestIAVLPruneEverything(t *testing.T) { func TestIAVLStoreQuery(t *testing.T) { db := dbm.NewMemDB() tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) k1, v1 := []byte("key1"), []byte("val1") k2, v2 := []byte("key2"), []byte("val2") v3 := []byte("val3") ksub := []byte("key") - KVs0 := []KVPair{} - KVs1 := []KVPair{ + KVs0 := []types.KVPair{} + KVs1 := []types.KVPair{ {Key: k1, Value: v1}, {Key: k2, Value: v2}, } - KVs2 := []KVPair{ + KVs2 := []types.KVPair{ {Key: k1, Value: v3}, {Key: k2, Value: v2}, } @@ -443,7 +443,7 @@ func TestIAVLStoreQuery(t *testing.T) { // query subspace before anything set qres := iavlStore.Query(querySub) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, valExpSubEmpty, qres.Value) // set data @@ -452,24 +452,24 @@ func TestIAVLStoreQuery(t *testing.T) { // set data without commit, doesn't show up qres = iavlStore.Query(query) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Nil(t, qres.Value) // commit it, but still don't see on old version cid = iavlStore.Commit() qres = iavlStore.Query(query) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Nil(t, qres.Value) // but yes on the new version query.Height = cid.Version qres = iavlStore.Query(query) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, v1, qres.Value) // and for the subspace qres = iavlStore.Query(querySub) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, valExpSub1, qres.Value) // modify @@ -478,28 +478,28 @@ func TestIAVLStoreQuery(t *testing.T) { // query will return old values, as height is fixed qres = iavlStore.Query(query) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, v1, qres.Value) // update to latest in the query and we are happy query.Height = cid.Version qres = iavlStore.Query(query) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, v3, qres.Value) query2 := abci.RequestQuery{Path: "/key", Data: k2, Height: cid.Version} qres = iavlStore.Query(query2) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, v2, qres.Value) // and for the subspace qres = iavlStore.Query(querySub) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, valExpSub2, qres.Value) // default (height 0) will show latest -1 query0 := abci.RequestQuery{Path: "/key", Data: k1} qres = iavlStore.Query(query0) - require.Equal(t, uint32(sdk.CodeOK), qres.Code) + require.Equal(t, uint32(types.CodeOK), qres.Code) require.Equal(t, v1, qres.Value) } @@ -512,8 +512,8 @@ func BenchmarkIAVLIteratorNext(b *testing.B) { value := cmn.RandBytes(50) tree.Set(key, value) } - iavlStore := newIAVLStore(tree, numRecent, storeEvery) - iterators := make([]Iterator, b.N/treeSize) + iavlStore := UnsafeNewStore(tree, numRecent, storeEvery) + iterators := make([]types.Iterator, b.N/treeSize) for i := 0; i < len(iterators); i++ { iterators[i] = iavlStore.Iterator([]byte{0}, []byte{255, 255, 255, 255, 255}) } diff --git a/store/iavl/wire.go b/store/iavl/wire.go new file mode 100644 index 000000000..43173f3e7 --- /dev/null +++ b/store/iavl/wire.go @@ -0,0 +1,7 @@ +package iavl + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +var cdc = codec.New() diff --git a/store/list.go b/store/list/list.go similarity index 93% rename from store/list.go rename to store/list/list.go index 542a77bf9..934dfaa0f 100644 --- a/store/list.go +++ b/store/list/list.go @@ -1,11 +1,12 @@ -package store +package list import ( "fmt" "strconv" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/store/types" ) // Key for the length of the list @@ -22,11 +23,11 @@ func ElemKey(index uint64) []byte { // It panics when the element type cannot be (un/)marshalled by the codec type List struct { cdc *codec.Codec - store sdk.KVStore + store types.KVStore } // NewList constructs new List -func NewList(cdc *codec.Codec, store sdk.KVStore) List { +func NewList(cdc *codec.Codec, store types.KVStore) List { return List{ cdc: cdc, store: store, @@ -83,7 +84,7 @@ func (m List) Push(value interface{}) { // CONTRACT: No writes may happen within a domain while iterating over it. func (m List) Iterate(ptr interface{}, fn func(uint64) bool) { - iter := sdk.KVStorePrefixIterator(m.store, []byte{0x01}) + iter := types.KVStorePrefixIterator(m.store, []byte{0x01}) defer iter.Close() for ; iter.Valid(); iter.Next() { v := iter.Value() diff --git a/store/list_test.go b/store/list/list_test.go similarity index 61% rename from store/list_test.go rename to store/list/list_test.go index 6767457cd..970c836bc 100644 --- a/store/list_test.go +++ b/store/list/list_test.go @@ -1,34 +1,54 @@ -package store +package list import ( "math/rand" "testing" - "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + abci "github.com/tendermint/tendermint/abci/types" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store/rootmulti" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" ) +type TestStruct struct { + I uint64 + B bool +} + +func defaultComponents(key sdk.StoreKey) (sdk.Context, *codec.Codec) { + db := dbm.NewMemDB() + cms := rootmulti.NewStore(db) + cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) + cms.LoadLatestVersion() + ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) + cdc := codec.New() + return ctx, cdc +} func TestList(t *testing.T) { key := sdk.NewKVStoreKey("test") ctx, cdc := defaultComponents(key) store := ctx.KVStore(key) lm := NewList(cdc, store) - val := S{1, true} - var res S + val := TestStruct{1, true} + var res TestStruct lm.Push(val) require.Equal(t, uint64(1), lm.Len()) lm.Get(uint64(0), &res) require.Equal(t, val, res) - val = S{2, false} + val = TestStruct{2, false} lm.Set(uint64(0), val) lm.Get(uint64(0), &res) require.Equal(t, val, res) - val = S{100, false} + val = TestStruct{100, false} lm.Push(val) require.Equal(t, uint64(2), lm.Len()) lm.Get(uint64(1), &res) @@ -38,7 +58,7 @@ func TestList(t *testing.T) { require.Equal(t, uint64(2), lm.Len()) lm.Iterate(&res, func(index uint64) (brk bool) { - var temp S + var temp TestStruct lm.Get(index, &temp) require.Equal(t, temp, res) @@ -47,12 +67,12 @@ func TestList(t *testing.T) { }) lm.Iterate(&res, func(index uint64) (brk bool) { - lm.Set(index, S{res.I + 1, !res.B}) + lm.Set(index, TestStruct{res.I + 1, !res.B}) return }) lm.Get(uint64(0), &res) - require.Equal(t, S{3, true}, res) + require.Equal(t, TestStruct{3, true}, res) } func TestListRandom(t *testing.T) { diff --git a/store/prefixstore.go b/store/prefix/store.go similarity index 71% rename from store/prefixstore.go rename to store/prefix/store.go index 66a08f0eb..a04995273 100644 --- a/store/prefixstore.go +++ b/store/prefix/store.go @@ -1,23 +1,31 @@ -package store +package prefix import ( "bytes" "io" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/tracekv" + "github.com/cosmos/cosmos-sdk/store/types" ) -var _ KVStore = prefixStore{} +var _ types.KVStore = Store{} -// prefixStore is similar with tendermint/tendermint/libs/db/prefix_db +// Store is similar with tendermint/tendermint/libs/db/prefix_db // both gives access only to the limited subset of the store // for convinience or safety - -type prefixStore struct { - parent KVStore +type Store struct { + parent types.KVStore prefix []byte } +func NewStore(parent types.KVStore, prefix []byte) Store { + return Store{ + parent: parent, + prefix: prefix, + } +} + func cloneAppend(bz []byte, tail []byte) (res []byte) { res = make([]byte, len(bz)+len(tail)) copy(res, bz) @@ -25,64 +33,55 @@ func cloneAppend(bz []byte, tail []byte) (res []byte) { return } -func (s prefixStore) key(key []byte) (res []byte) { +func (s Store) key(key []byte) (res []byte) { if key == nil { - panic("nil key on prefixStore") + panic("nil key on Store") } res = cloneAppend(s.prefix, key) return } // Implements Store -func (s prefixStore) GetStoreType() StoreType { +func (s Store) GetStoreType() types.StoreType { return s.parent.GetStoreType() } // Implements CacheWrap -func (s prefixStore) CacheWrap() CacheWrap { - return NewCacheKVStore(s) +func (s Store) CacheWrap() types.CacheWrap { + return cachekv.NewStore(s) } // CacheWrapWithTrace implements the KVStore interface. -func (s prefixStore) CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap { - return NewCacheKVStore(NewTraceKVStore(s, w, tc)) +func (s Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { + return cachekv.NewStore(tracekv.NewStore(s, w, tc)) } // Implements KVStore -func (s prefixStore) Get(key []byte) []byte { +func (s Store) Get(key []byte) []byte { res := s.parent.Get(s.key(key)) return res } // Implements KVStore -func (s prefixStore) Has(key []byte) bool { +func (s Store) Has(key []byte) bool { return s.parent.Has(s.key(key)) } // Implements KVStore -func (s prefixStore) Set(key, value []byte) { - assertValidValue(value) +func (s Store) Set(key, value []byte) { + types.AssertValidKey(key) + types.AssertValidValue(value) s.parent.Set(s.key(key), value) } // Implements KVStore -func (s prefixStore) Delete(key []byte) { +func (s Store) Delete(key []byte) { s.parent.Delete(s.key(key)) } -// Implements KVStore -func (s prefixStore) Prefix(prefix []byte) KVStore { - return prefixStore{s, prefix} -} - -// Implements KVStore -func (s prefixStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, s) -} - // Implements KVStore // Check https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db.go#L106 -func (s prefixStore) Iterator(start, end []byte) Iterator { +func (s Store) Iterator(start, end []byte) types.Iterator { newstart := cloneAppend(s.prefix, start) var newend []byte @@ -99,7 +98,7 @@ func (s prefixStore) Iterator(start, end []byte) Iterator { // Implements KVStore // Check https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db.go#L129 -func (s prefixStore) ReverseIterator(start, end []byte) Iterator { +func (s Store) ReverseIterator(start, end []byte) types.Iterator { newstart := cloneAppend(s.prefix, start) var newend []byte @@ -114,16 +113,16 @@ func (s prefixStore) ReverseIterator(start, end []byte) Iterator { return newPrefixIterator(s.prefix, start, end, iter) } -var _ sdk.Iterator = (*prefixIterator)(nil) +var _ types.Iterator = (*prefixIterator)(nil) type prefixIterator struct { prefix []byte start, end []byte - iter Iterator + iter types.Iterator valid bool } -func newPrefixIterator(prefix, start, end []byte, parent Iterator) *prefixIterator { +func newPrefixIterator(prefix, start, end []byte, parent types.Iterator) *prefixIterator { return &prefixIterator{ prefix: prefix, start: start, @@ -185,9 +184,9 @@ func stripPrefix(key []byte, prefix []byte) []byte { return key[len(prefix):] } -// wrapping sdk.PrefixEndBytes +// wrapping types.PrefixEndBytes func cpIncr(bz []byte) []byte { - return sdk.PrefixEndBytes(bz) + return types.PrefixEndBytes(bz) } // copied from github.com/tendermint/tendermint/libs/db/util.go @@ -210,7 +209,7 @@ func cpDecr(bz []byte) (ret []byte) { return nil } -func skipOne(iter Iterator, skipKey []byte) { +func skipOne(iter types.Iterator, skipKey []byte) { if iter.Valid() { if bytes.Equal(iter.Key(), skipKey) { iter.Next() diff --git a/store/prefixstore_test.go b/store/prefix/store_test.go similarity index 79% rename from store/prefixstore_test.go rename to store/prefix/store_test.go index 4a4253af4..04019ead3 100644 --- a/store/prefixstore_test.go +++ b/store/prefix/store_test.go @@ -1,4 +1,4 @@ -package store +package prefix import ( "math/rand" @@ -6,12 +6,24 @@ import ( "github.com/stretchr/testify/require" - "github.com/tendermint/iavl" + tiavl "github.com/tendermint/iavl" dbm "github.com/tendermint/tendermint/libs/db" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/gaskv" + "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/types" ) +// copied from iavl/store_test.go +var ( + cacheSize = 100 + numRecent int64 = 5 + storeEvery int64 = 3 +) + +func bz(s string) []byte { return []byte(s) } + type kvpair struct { key []byte value []byte @@ -30,7 +42,7 @@ func genRandomKVPairs(t *testing.T) []kvpair { return kvps } -func setRandomKVPairs(t *testing.T, store KVStore) []kvpair { +func setRandomKVPairs(t *testing.T, store types.KVStore) []kvpair { kvps := genRandomKVPairs(t) for _, kvp := range kvps { store.Set(kvp.key, kvp.value) @@ -38,9 +50,9 @@ func setRandomKVPairs(t *testing.T, store KVStore) []kvpair { return kvps } -func testPrefixStore(t *testing.T, baseStore KVStore, prefix []byte) { - prefixStore := baseStore.Prefix(prefix) - prefixPrefixStore := prefixStore.Prefix([]byte("prefix")) +func testPrefixStore(t *testing.T, baseStore types.KVStore, prefix []byte) { + prefixStore := NewStore(baseStore, prefix) + prefixPrefixStore := NewStore(prefixStore, []byte("prefix")) require.Panics(t, func() { prefixStore.Get(nil) }) require.Panics(t, func() { prefixStore.Set(nil, []byte{}) }) @@ -75,43 +87,29 @@ func testPrefixStore(t *testing.T, baseStore KVStore, prefix []byte) { func TestIAVLStorePrefix(t *testing.T) { db := dbm.NewMemDB() - tree := iavl.NewMutableTree(db, cacheSize) - iavlStore := newIAVLStore(tree, numRecent, storeEvery) + tree := tiavl.NewMutableTree(db, cacheSize) + iavlStore := iavl.UnsafeNewStore(tree, numRecent, storeEvery) testPrefixStore(t, iavlStore, []byte("test")) } -func TestCacheKVStorePrefix(t *testing.T) { - cacheStore := newCacheKVStore() - - testPrefixStore(t, cacheStore, []byte("test")) -} - -func TestGasKVStorePrefix(t *testing.T) { - meter := sdk.NewGasMeter(100000000) - mem := dbStoreAdapter{dbm.NewMemDB()} - gasStore := NewGasKVStore(meter, sdk.KVGasConfig(), mem) - - testPrefixStore(t, gasStore, []byte("test")) -} - func TestPrefixKVStoreNoNilSet(t *testing.T) { - meter := sdk.NewGasMeter(100000000) - mem := dbStoreAdapter{dbm.NewMemDB()} - gasStore := NewGasKVStore(meter, sdk.KVGasConfig(), mem) + meter := types.NewGasMeter(100000000) + mem := dbadapter.Store{dbm.NewMemDB()} + gasStore := gaskv.NewStore(mem, meter, types.KVGasConfig()) require.Panics(t, func() { gasStore.Set([]byte("key"), nil) }, "setting a nil value should panic") } func TestPrefixStoreIterate(t *testing.T) { db := dbm.NewMemDB() - baseStore := dbStoreAdapter{db} + baseStore := dbadapter.Store{db} prefix := []byte("test") - prefixStore := baseStore.Prefix(prefix) + prefixStore := NewStore(baseStore, prefix) setRandomKVPairs(t, prefixStore) - bIter := sdk.KVStorePrefixIterator(baseStore, prefix) - pIter := sdk.KVStorePrefixIterator(prefixStore, nil) + bIter := types.KVStorePrefixIterator(baseStore, prefix) + pIter := types.KVStorePrefixIterator(prefixStore, nil) for bIter.Valid() && pIter.Valid() { require.Equal(t, bIter.Key(), append(prefix, pIter.Key()...)) @@ -150,11 +148,11 @@ func TestCloneAppend(t *testing.T) { func TestPrefixStoreIteratorEdgeCase(t *testing.T) { db := dbm.NewMemDB() - baseStore := dbStoreAdapter{db} + baseStore := dbadapter.Store{db} // overflow in cpIncr prefix := []byte{0xAA, 0xFF, 0xFF} - prefixStore := baseStore.Prefix(prefix) + prefixStore := NewStore(baseStore, prefix) // ascending order baseStore.Set([]byte{0xAA, 0xFF, 0xFE}, []byte{}) @@ -180,11 +178,11 @@ func TestPrefixStoreIteratorEdgeCase(t *testing.T) { func TestPrefixStoreReverseIteratorEdgeCase(t *testing.T) { db := dbm.NewMemDB() - baseStore := dbStoreAdapter{db} + baseStore := dbadapter.Store{db} // overflow in cpIncr prefix := []byte{0xAA, 0xFF, 0xFF} - prefixStore := baseStore.Prefix(prefix) + prefixStore := NewStore(baseStore, prefix) // descending order baseStore.Set([]byte{0xAB, 0x00, 0x00}, []byte{}) @@ -208,11 +206,11 @@ func TestPrefixStoreReverseIteratorEdgeCase(t *testing.T) { iter.Close() db = dbm.NewMemDB() - baseStore = dbStoreAdapter{db} + baseStore = dbadapter.Store{db} // underflow in cpDecr prefix = []byte{0xAA, 0x00, 0x00} - prefixStore = baseStore.Prefix(prefix) + prefixStore = NewStore(baseStore, prefix) baseStore.Set([]byte{0xAB, 0x00, 0x01, 0x00, 0x00}, []byte{}) baseStore.Set([]byte{0xAB, 0x00, 0x01, 0x00}, []byte{}) @@ -237,9 +235,9 @@ func TestPrefixStoreReverseIteratorEdgeCase(t *testing.T) { // Tests below are ported from https://github.com/tendermint/tendermint/blob/master/libs/db/prefix_db_test.go -func mockStoreWithStuff() sdk.KVStore { +func mockStoreWithStuff() types.KVStore { db := dbm.NewMemDB() - store := dbStoreAdapter{db} + store := dbadapter.Store{db} // Under "key" prefix store.Set(bz("key"), bz("value")) store.Set(bz("key1"), bz("value1")) @@ -253,55 +251,55 @@ func mockStoreWithStuff() sdk.KVStore { return store } -func checkValue(t *testing.T, store sdk.KVStore, key []byte, expected []byte) { +func checkValue(t *testing.T, store types.KVStore, key []byte, expected []byte) { bz := store.Get(key) require.Equal(t, expected, bz) } -func checkValid(t *testing.T, itr sdk.Iterator, expected bool) { +func checkValid(t *testing.T, itr types.Iterator, expected bool) { valid := itr.Valid() require.Equal(t, expected, valid) } -func checkNext(t *testing.T, itr sdk.Iterator, expected bool) { +func checkNext(t *testing.T, itr types.Iterator, expected bool) { itr.Next() valid := itr.Valid() require.Equal(t, expected, valid) } -func checkDomain(t *testing.T, itr sdk.Iterator, start, end []byte) { +func checkDomain(t *testing.T, itr types.Iterator, start, end []byte) { ds, de := itr.Domain() require.Equal(t, start, ds) require.Equal(t, end, de) } -func checkItem(t *testing.T, itr sdk.Iterator, key, value []byte) { +func checkItem(t *testing.T, itr types.Iterator, key, value []byte) { require.Exactly(t, key, itr.Key()) require.Exactly(t, value, itr.Value()) } -func checkInvalid(t *testing.T, itr sdk.Iterator) { +func checkInvalid(t *testing.T, itr types.Iterator) { checkValid(t, itr, false) checkKeyPanics(t, itr) checkValuePanics(t, itr) checkNextPanics(t, itr) } -func checkKeyPanics(t *testing.T, itr sdk.Iterator) { +func checkKeyPanics(t *testing.T, itr types.Iterator) { require.Panics(t, func() { itr.Key() }) } -func checkValuePanics(t *testing.T, itr sdk.Iterator) { +func checkValuePanics(t *testing.T, itr types.Iterator) { require.Panics(t, func() { itr.Value() }) } -func checkNextPanics(t *testing.T, itr sdk.Iterator) { +func checkNextPanics(t *testing.T, itr types.Iterator) { require.Panics(t, func() { itr.Next() }) } func TestPrefixDBSimple(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) checkValue(t, pstore, bz("key"), nil) checkValue(t, pstore, bz(""), bz("value")) @@ -319,7 +317,7 @@ func TestPrefixDBSimple(t *testing.T) { func TestPrefixDBIterator1(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.Iterator(nil, nil) checkDomain(t, itr, nil, nil) @@ -337,7 +335,7 @@ func TestPrefixDBIterator1(t *testing.T) { func TestPrefixDBIterator2(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.Iterator(nil, bz("")) checkDomain(t, itr, nil, bz("")) @@ -347,7 +345,7 @@ func TestPrefixDBIterator2(t *testing.T) { func TestPrefixDBIterator3(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.Iterator(bz(""), nil) checkDomain(t, itr, bz(""), nil) @@ -365,7 +363,7 @@ func TestPrefixDBIterator3(t *testing.T) { func TestPrefixDBIterator4(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.Iterator(bz(""), bz("")) checkDomain(t, itr, bz(""), bz("")) @@ -375,7 +373,7 @@ func TestPrefixDBIterator4(t *testing.T) { func TestPrefixDBReverseIterator1(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.ReverseIterator(nil, nil) checkDomain(t, itr, nil, nil) @@ -393,7 +391,7 @@ func TestPrefixDBReverseIterator1(t *testing.T) { func TestPrefixDBReverseIterator2(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.ReverseIterator(bz(""), nil) checkDomain(t, itr, bz(""), nil) @@ -411,7 +409,7 @@ func TestPrefixDBReverseIterator2(t *testing.T) { func TestPrefixDBReverseIterator3(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.ReverseIterator(nil, bz("")) checkDomain(t, itr, nil, bz("")) @@ -421,7 +419,7 @@ func TestPrefixDBReverseIterator3(t *testing.T) { func TestPrefixDBReverseIterator4(t *testing.T) { store := mockStoreWithStuff() - pstore := store.Prefix(bz("key")) + pstore := NewStore(store, bz("key")) itr := pstore.ReverseIterator(bz(""), bz("")) checkInvalid(t, itr) diff --git a/store/pruning.go b/store/pruning.go deleted file mode 100644 index 9a7aeb4d2..000000000 --- a/store/pruning.go +++ /dev/null @@ -1,29 +0,0 @@ -package store - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// default pruning strategies -var ( - // PruneEverything means all saved states will be deleted, storing only the current state - PruneEverything = sdk.NewPruningOptions(0, 0) - // PruneNothing means all historic states will be saved, nothing will be deleted - PruneNothing = sdk.NewPruningOptions(0, 1) - // PruneSyncable means only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th) - PruneSyncable = sdk.NewPruningOptions(100, 10000) -) - -func NewPruningOptions(strategy string) (opt PruningOptions) { - switch strategy { - case "nothing": - opt = PruneNothing - case "everything": - opt = PruneEverything - case "syncable": - opt = PruneSyncable - default: - opt = PruneSyncable - } - return -} diff --git a/store/queue.go b/store/queue/queue.go similarity index 95% rename from store/queue.go rename to store/queue/queue.go index f41ecb7d7..0f65d0375 100644 --- a/store/queue.go +++ b/store/queue/queue.go @@ -1,8 +1,12 @@ package store +// TODO: make it independent from list +/* import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/store/list" ) // Key for the top element position in the queue @@ -13,7 +17,7 @@ func TopKey() []byte { // Queue is a List wrapper that provides queue-like functions // It panics when the element type cannot be (un/)marshalled by the codec type Queue struct { - List List + List list.List } // NewQueue constructs new Queue @@ -86,3 +90,4 @@ func (m Queue) Flush(ptr interface{}, fn func() bool) { } m.setTop(i) } +*/ diff --git a/store/queue_test.go b/store/queue_test.go deleted file mode 100644 index 58e96f56a..000000000 --- a/store/queue_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package store - -import ( - "testing" - - "github.com/stretchr/testify/require" - - dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/log" - - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" -) - -type S struct { - I uint64 - B bool -} - -func defaultComponents(key sdk.StoreKey) (sdk.Context, *codec.Codec) { - db := dbm.NewMemDB() - cms := NewCommitMultiStore(db) - cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) - cms.LoadLatestVersion() - ctx := sdk.NewContext(cms, abci.Header{}, false, log.NewNopLogger()) - cdc := codec.New() - return ctx, cdc -} - -func TestQueue(t *testing.T) { - key := sdk.NewKVStoreKey("test") - ctx, cdc := defaultComponents(key) - store := ctx.KVStore(key) - - qm := NewQueue(cdc, store) - - val := S{1, true} - var res S - - qm.Push(val) - qm.Peek(&res) - require.Equal(t, val, res) - - qm.Pop() - empty := qm.IsEmpty() - - require.True(t, empty) - require.NotNil(t, qm.Peek(&res)) - - qm.Push(S{1, true}) - qm.Push(S{2, true}) - qm.Push(S{3, true}) - qm.Flush(&res, func() (brk bool) { - if res.I == 3 { - brk = true - } - return - }) - - require.False(t, qm.IsEmpty()) - - qm.Pop() - require.True(t, qm.IsEmpty()) -} - -func TestKeys(t *testing.T) { - key := sdk.NewKVStoreKey("test") - ctx, cdc := defaultComponents(key) - store := ctx.KVStore(key) - queue := NewQueue(cdc, store) - - for i := 0; i < 10; i++ { - queue.Push(i) - } - - var len uint64 - var top uint64 - var expected int - var actual int - - // Checking keys.LengthKey - err := cdc.UnmarshalBinaryLengthPrefixed(store.Get(LengthKey()), &len) - require.Nil(t, err) - require.Equal(t, len, queue.List.Len()) - - // Checking keys.ElemKey - for i := 0; i < 10; i++ { - queue.List.Get(uint64(i), &expected) - bz := store.Get(ElemKey(uint64(i))) - err = cdc.UnmarshalBinaryLengthPrefixed(bz, &actual) - require.Nil(t, err) - require.Equal(t, expected, actual) - } - - queue.Pop() - - err = cdc.UnmarshalBinaryLengthPrefixed(store.Get(TopKey()), &top) - require.Nil(t, err) - require.Equal(t, top, queue.getTop()) -} diff --git a/store/codec.go b/store/reexport.go similarity index 72% rename from store/codec.go rename to store/reexport.go index 181f12e51..913a8c55a 100644 --- a/store/codec.go +++ b/store/reexport.go @@ -1,7 +1,8 @@ package store import ( - "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/types" + stypes "github.com/cosmos/cosmos-sdk/store/types" ) // Import cosmos-sdk/types/store.go for convenience. @@ -26,7 +27,14 @@ type ( StoreType = types.StoreType Queryable = types.Queryable TraceContext = types.TraceContext - Gas = types.Gas + Gas = stypes.Gas GasMeter = types.GasMeter - GasConfig = types.GasConfig + GasConfig = stypes.GasConfig +) + +// nolint - reexport +var ( + PruneNothing = types.PruneNothing + PruneEverything = types.PruneEverything + PruneSyncable = types.PruneSyncable ) diff --git a/store/rootmulti/dbadapter.go b/store/rootmulti/dbadapter.go new file mode 100644 index 000000000..cda6e62ba --- /dev/null +++ b/store/rootmulti/dbadapter.go @@ -0,0 +1,33 @@ +package rootmulti + +import ( + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/types" +) + +var commithash = []byte("FAKE_HASH") + +//---------------------------------------- +// commitDBStoreWrapper should only be used for simulation/debugging, +// as it doesn't compute any commit hash, and it cannot load older state. + +// Wrapper type for dbm.Db with implementation of KVStore +type commitDBStoreAdapter struct { + dbadapter.Store +} + +func (cdsa commitDBStoreAdapter) Commit() types.CommitID { + return types.CommitID{ + Version: -1, + Hash: commithash, + } +} + +func (cdsa commitDBStoreAdapter) LastCommitID() types.CommitID { + return types.CommitID{ + Version: -1, + Hash: commithash, + } +} + +func (cdsa commitDBStoreAdapter) SetPruning(_ types.PruningOptions) {} diff --git a/store/multistoreproof.go b/store/rootmulti/proof.go similarity index 99% rename from store/multistoreproof.go rename to store/rootmulti/proof.go index 96f0a4837..6657a6007 100644 --- a/store/multistoreproof.go +++ b/store/rootmulti/proof.go @@ -1,4 +1,4 @@ -package store +package rootmulti import ( "bytes" diff --git a/store/multistoreproof_test.go b/store/rootmulti/proof_test.go similarity index 84% rename from store/multistoreproof_test.go rename to store/rootmulti/proof_test.go index 3d70451e7..885d0ddb4 100644 --- a/store/multistoreproof_test.go +++ b/store/rootmulti/proof_test.go @@ -1,20 +1,22 @@ -package store +package rootmulti import ( "testing" + "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/iavl" + stypes "github.com/cosmos/cosmos-sdk/store/types" ) func TestVerifyIAVLStoreQueryProof(t *testing.T) { // Create main tree for testing. db := dbm.NewMemDB() - iStore, err := LoadIAVLStore(db, CommitID{}, PruneNothing) - store := iStore.(*iavlStore) + iStore, err := iavl.LoadStore(db, types.CommitID{}, stypes.PruneNothing) + store := iStore.(*iavl.Store) require.Nil(t, err) store.Set([]byte("MYKEY"), []byte("MYVALUE")) cid := store.Commit() @@ -56,13 +58,13 @@ func TestVerifyIAVLStoreQueryProof(t *testing.T) { func TestVerifyMultiStoreQueryProof(t *testing.T) { // Create main tree for testing. db := dbm.NewMemDB() - store := NewCommitMultiStore(db) - iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") + store := NewStore(db) + iavlStoreKey := types.NewKVStoreKey("iavlStoreKey") - store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.MountStoreWithDB(iavlStoreKey, types.StoreTypeIAVL, nil) store.LoadVersion(0) - iavlStore := store.GetCommitStore(iavlStoreKey).(*iavlStore) + iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) cid := store.Commit() @@ -111,10 +113,10 @@ func TestVerifyMultiStoreQueryProof(t *testing.T) { func TestVerifyMultiStoreQueryProofEmptyStore(t *testing.T) { // Create main tree for testing. db := dbm.NewMemDB() - store := NewCommitMultiStore(db) - iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") + store := NewStore(db) + iavlStoreKey := types.NewKVStoreKey("iavlStoreKey") - store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.MountStoreWithDB(iavlStoreKey, types.StoreTypeIAVL, nil) store.LoadVersion(0) cid := store.Commit() // Commit with empty iavl store. @@ -140,13 +142,13 @@ func TestVerifyMultiStoreQueryProofEmptyStore(t *testing.T) { func TestVerifyMultiStoreQueryProofAbsence(t *testing.T) { // Create main tree for testing. db := dbm.NewMemDB() - store := NewCommitMultiStore(db) - iavlStoreKey := sdk.NewKVStoreKey("iavlStoreKey") + store := NewStore(db) + iavlStoreKey := types.NewKVStoreKey("iavlStoreKey") - store.MountStoreWithDB(iavlStoreKey, sdk.StoreTypeIAVL, nil) + store.MountStoreWithDB(iavlStoreKey, types.StoreTypeIAVL, nil) store.LoadVersion(0) - iavlStore := store.GetCommitStore(iavlStoreKey).(*iavlStore) + iavlStore := store.GetCommitStore(iavlStoreKey).(*iavl.Store) iavlStore.Set([]byte("MYKEY"), []byte("MYVALUE")) cid := store.Commit() // Commit with empty iavl store. diff --git a/store/rootmultistore.go b/store/rootmulti/store.go similarity index 67% rename from store/rootmultistore.go rename to store/rootmulti/store.go index fa576d3dc..8be98a1d8 100644 --- a/store/rootmultistore.go +++ b/store/rootmulti/store.go @@ -1,4 +1,4 @@ -package store +package rootmulti import ( "fmt" @@ -10,7 +10,12 @@ import ( "github.com/tendermint/tendermint/crypto/tmhash" dbm "github.com/tendermint/tendermint/libs/db" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/cachemulti" + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/iavl" + "github.com/cosmos/cosmos-sdk/store/tracekv" + "github.com/cosmos/cosmos-sdk/store/transient" + "github.com/cosmos/cosmos-sdk/types" ) const ( @@ -18,36 +23,36 @@ const ( commitInfoKeyFmt = "s/%d" // s/ ) -// rootMultiStore is composed of many CommitStores. Name contrasts with +// Store is composed of many CommitStores. Name contrasts with // cacheMultiStore which is for cache-wrapping other MultiStores. It implements // the CommitMultiStore interface. -type rootMultiStore struct { +type Store struct { db dbm.DB - lastCommitID CommitID - pruningOpts sdk.PruningOptions - storesParams map[StoreKey]storeParams - stores map[StoreKey]CommitStore - keysByName map[string]StoreKey + lastCommitID types.CommitID + pruningOpts types.PruningOptions + storesParams map[types.StoreKey]storeParams + stores map[types.StoreKey]types.CommitStore + keysByName map[string]types.StoreKey traceWriter io.Writer - traceContext TraceContext + traceContext types.TraceContext } -var _ CommitMultiStore = (*rootMultiStore)(nil) -var _ Queryable = (*rootMultiStore)(nil) +var _ types.CommitMultiStore = (*Store)(nil) +var _ types.Queryable = (*Store)(nil) // nolint -func NewCommitMultiStore(db dbm.DB) *rootMultiStore { - return &rootMultiStore{ +func NewStore(db dbm.DB) *Store { + return &Store{ db: db, - storesParams: make(map[StoreKey]storeParams), - stores: make(map[StoreKey]CommitStore), - keysByName: make(map[string]StoreKey), + storesParams: make(map[types.StoreKey]storeParams), + stores: make(map[types.StoreKey]types.CommitStore), + keysByName: make(map[string]types.StoreKey), } } // Implements CommitMultiStore -func (rs *rootMultiStore) SetPruning(pruningOpts sdk.PruningOptions) { +func (rs *Store) SetPruning(pruningOpts types.PruningOptions) { rs.pruningOpts = pruningOpts for _, substore := range rs.stores { substore.SetPruning(pruningOpts) @@ -55,20 +60,20 @@ func (rs *rootMultiStore) SetPruning(pruningOpts sdk.PruningOptions) { } // Implements Store. -func (rs *rootMultiStore) GetStoreType() StoreType { - return sdk.StoreTypeMulti +func (rs *Store) GetStoreType() types.StoreType { + return types.StoreTypeMulti } // Implements CommitMultiStore. -func (rs *rootMultiStore) MountStoreWithDB(key StoreKey, typ StoreType, db dbm.DB) { +func (rs *Store) MountStoreWithDB(key types.StoreKey, typ types.StoreType, db dbm.DB) { if key == nil { panic("MountIAVLStore() key cannot be nil") } if _, ok := rs.storesParams[key]; ok { - panic(fmt.Sprintf("rootMultiStore duplicate store key %v", key)) + panic(fmt.Sprintf("Store duplicate store key %v", key)) } if _, ok := rs.keysByName[key.Name()]; ok { - panic(fmt.Sprintf("rootMultiStore duplicate store key name %v", key)) + panic(fmt.Sprintf("Store duplicate store key name %v", key)) } rs.storesParams[key] = storeParams{ key: key, @@ -79,36 +84,36 @@ func (rs *rootMultiStore) MountStoreWithDB(key StoreKey, typ StoreType, db dbm.D } // Implements CommitMultiStore. -func (rs *rootMultiStore) GetCommitStore(key StoreKey) CommitStore { +func (rs *Store) GetCommitStore(key types.StoreKey) types.CommitStore { return rs.stores[key] } // Implements CommitMultiStore. -func (rs *rootMultiStore) GetCommitKVStore(key StoreKey) CommitKVStore { - return rs.stores[key].(CommitKVStore) +func (rs *Store) GetCommitKVStore(key types.StoreKey) types.CommitKVStore { + return rs.stores[key].(types.CommitKVStore) } // Implements CommitMultiStore. -func (rs *rootMultiStore) LoadLatestVersion() error { +func (rs *Store) LoadLatestVersion() error { ver := getLatestVersion(rs.db) return rs.LoadVersion(ver) } // Implements CommitMultiStore. -func (rs *rootMultiStore) LoadVersion(ver int64) error { +func (rs *Store) LoadVersion(ver int64) error { // Special logic for version 0 if ver == 0 { for key, storeParams := range rs.storesParams { - id := CommitID{} + id := types.CommitID{} store, err := rs.loadCommitStoreFromParams(key, id, storeParams) if err != nil { - return fmt.Errorf("failed to load rootMultiStore: %v", err) + return fmt.Errorf("failed to load Store: %v", err) } rs.stores[key] = store } - rs.lastCommitID = CommitID{} + rs.lastCommitID = types.CommitID{} return nil } // Otherwise, version is 1 or greater @@ -120,15 +125,15 @@ func (rs *rootMultiStore) LoadVersion(ver int64) error { } // Convert StoreInfos slice to map - infos := make(map[StoreKey]storeInfo) + infos := make(map[types.StoreKey]storeInfo) for _, storeInfo := range cInfo.StoreInfos { infos[rs.nameToKey(storeInfo.Name)] = storeInfo } // Load each Store - var newStores = make(map[StoreKey]CommitStore) + var newStores = make(map[types.StoreKey]types.CommitStore) for key, storeParams := range rs.storesParams { - var id CommitID + var id types.CommitID info, ok := infos[key] if ok { id = info.Core.CommitID @@ -136,7 +141,7 @@ func (rs *rootMultiStore) LoadVersion(ver int64) error { store, err := rs.loadCommitStoreFromParams(key, id, storeParams) if err != nil { - return fmt.Errorf("failed to load rootMultiStore: %v", err) + return fmt.Errorf("failed to load Store: %v", err) } newStores[key] = store } @@ -147,18 +152,18 @@ func (rs *rootMultiStore) LoadVersion(ver int64) error { return nil } -// WithTracer sets the tracer for the MultiStore that the underlying +// SetTracer sets the tracer for the MultiStore that the underlying // stores will utilize to trace operations. A MultiStore is returned. -func (rs *rootMultiStore) WithTracer(w io.Writer) MultiStore { +func (rs *Store) SetTracer(w io.Writer) types.MultiStore { rs.traceWriter = w return rs } -// WithTracingContext updates the tracing context for the MultiStore by merging +// SetTracingContext updates the tracing context for the MultiStore by merging // the given context with the existing context by key. Any existing keys will // be overwritten. It is implied that the caller should update the context when // necessary between tracing operations. It returns a modified MultiStore. -func (rs *rootMultiStore) WithTracingContext(tc TraceContext) MultiStore { +func (rs *Store) SetTracingContext(tc types.TraceContext) types.MultiStore { if rs.traceContext != nil { for k, v := range tc { rs.traceContext[k] = v @@ -171,26 +176,20 @@ func (rs *rootMultiStore) WithTracingContext(tc TraceContext) MultiStore { } // TracingEnabled returns if tracing is enabled for the MultiStore. -func (rs *rootMultiStore) TracingEnabled() bool { +func (rs *Store) TracingEnabled() bool { return rs.traceWriter != nil } -// ResetTraceContext resets the current tracing context. -func (rs *rootMultiStore) ResetTraceContext() MultiStore { - rs.traceContext = nil - return rs -} - //---------------------------------------- // +CommitStore // Implements Committer/CommitStore. -func (rs *rootMultiStore) LastCommitID() CommitID { +func (rs *Store) LastCommitID() types.CommitID { return rs.lastCommitID } // Implements Committer/CommitStore. -func (rs *rootMultiStore) Commit() CommitID { +func (rs *Store) Commit() types.CommitID { // Commit stores. version := rs.lastCommitID.Version + 1 @@ -203,7 +202,7 @@ func (rs *rootMultiStore) Commit() CommitID { batch.Write() // Prepare for next version. - commitID := CommitID{ + commitID := types.CommitID{ Version: version, Hash: commitInfo.Hash(), } @@ -212,12 +211,12 @@ func (rs *rootMultiStore) Commit() CommitID { } // Implements CacheWrapper/Store/CommitStore. -func (rs *rootMultiStore) CacheWrap() CacheWrap { - return rs.CacheMultiStore().(CacheWrap) +func (rs *Store) CacheWrap() types.CacheWrap { + return rs.CacheMultiStore().(types.CacheWrap) } // CacheWrapWithTrace implements the CacheWrapper interface. -func (rs *rootMultiStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap { +func (rs *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { return rs.CacheWrap() } @@ -225,13 +224,17 @@ func (rs *rootMultiStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheW // +MultiStore // Implements MultiStore. -func (rs *rootMultiStore) CacheMultiStore() CacheMultiStore { - return newCacheMultiStoreFromRMS(rs) +func (rs *Store) CacheMultiStore() types.CacheMultiStore { + stores := make(map[types.StoreKey]types.CacheWrapper) + for k, v := range rs.stores { + stores[k] = v + } + return cachemulti.NewStore(rs.db, stores, rs.keysByName, rs.traceWriter, rs.traceContext) } // Implements MultiStore. // If the store does not exist, panics. -func (rs *rootMultiStore) GetStore(key StoreKey) Store { +func (rs *Store) GetStore(key types.StoreKey) types.Store { store := rs.stores[key] if store == nil { panic("Could not load store " + key.String()) @@ -240,14 +243,14 @@ func (rs *rootMultiStore) GetStore(key StoreKey) Store { } // GetKVStore implements the MultiStore interface. If tracing is enabled on the -// rootMultiStore, a wrapped TraceKVStore will be returned with the given +// Store, a wrapped TraceKVStore will be returned with the given // tracer, otherwise, the original KVStore will be returned. // If the store does not exist, panics. -func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore { - store := rs.stores[key].(KVStore) +func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore { + store := rs.stores[key].(types.KVStore) if rs.TracingEnabled() { - store = NewTraceKVStore(store, rs.traceWriter, rs.traceContext) + store = tracekv.NewStore(store, rs.traceWriter, rs.traceContext) } return store @@ -260,7 +263,7 @@ func (rs *rootMultiStore) GetKVStore(key StoreKey) KVStore { // This is not exposed to the extensions (which will need the // StoreKey), but is useful in main, and particularly app.Query, // in order to convert human strings into CommitStores. -func (rs *rootMultiStore) getStoreByName(name string) Store { +func (rs *Store) getStoreByName(name string) types.Store { key := rs.keysByName[name] if key == nil { return nil @@ -274,7 +277,7 @@ func (rs *rootMultiStore) getStoreByName(name string) Store { // modified to remove the substore prefix. // Ie. `req.Path` here is `//`, and trimmed to `/` for the substore. // TODO: add proof for `multistore -> substore`. -func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { +func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery { // Query just routes this to a substore. path := req.Path storeName, subpath, err := parsePath(path) @@ -285,12 +288,12 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { store := rs.getStoreByName(storeName) if store == nil { msg := fmt.Sprintf("no such store: %s", storeName) - return sdk.ErrUnknownRequest(msg).QueryResult() + return types.ErrUnknownRequest(msg).QueryResult() } - queryable, ok := store.(Queryable) + queryable, ok := store.(types.Queryable) if !ok { msg := fmt.Sprintf("store %s doesn't support queries", storeName) - return sdk.ErrUnknownRequest(msg).QueryResult() + return types.ErrUnknownRequest(msg).QueryResult() } // trim the path and make the query @@ -302,12 +305,12 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { } if res.Proof == nil || len(res.Proof.Ops) == 0 { - return sdk.ErrInternal("substore proof was nil/empty when it should never be").QueryResult() + return types.ErrInternal("substore proof was nil/empty when it should never be").QueryResult() } commitInfo, errMsg := getCommitInfo(rs.db, res.Height) if errMsg != nil { - return sdk.ErrInternal(errMsg.Error()).QueryResult() + return types.ErrInternal(errMsg.Error()).QueryResult() } // Restore origin path and append proof op. @@ -324,9 +327,9 @@ func (rs *rootMultiStore) Query(req abci.RequestQuery) abci.ResponseQuery { // parsePath expects a format like /[/] // Must start with /, subpath may be empty // Returns error if it doesn't start with / -func parsePath(path string) (storeName string, subpath string, err sdk.Error) { +func parsePath(path string) (storeName string, subpath string, err types.Error) { if !strings.HasPrefix(path, "/") { - err = sdk.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path)) + err = types.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path)) return } @@ -342,7 +345,7 @@ func parsePath(path string) (storeName string, subpath string, err sdk.Error) { //---------------------------------------- -func (rs *rootMultiStore) loadCommitStoreFromParams(key sdk.StoreKey, id CommitID, params storeParams) (store CommitStore, err error) { +func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID, params storeParams) (store types.CommitStore, err error) { var db dbm.DB if params.db != nil { db = dbm.NewPrefixDB(params.db, []byte("s/_/")) @@ -350,30 +353,30 @@ func (rs *rootMultiStore) loadCommitStoreFromParams(key sdk.StoreKey, id CommitI db = dbm.NewPrefixDB(rs.db, []byte("s/k:"+params.key.Name()+"/")) } switch params.typ { - case sdk.StoreTypeMulti: + case types.StoreTypeMulti: panic("recursive MultiStores not yet supported") // TODO: id? // return NewCommitMultiStore(db, id) - case sdk.StoreTypeIAVL: - store, err = LoadIAVLStore(db, id, rs.pruningOpts) + case types.StoreTypeIAVL: + store, err = iavl.LoadStore(db, id, rs.pruningOpts) return - case sdk.StoreTypeDB: - store = commitDBStoreAdapter{dbStoreAdapter{db}} + case types.StoreTypeDB: + store = commitDBStoreAdapter{dbadapter.Store{db}} return - case sdk.StoreTypeTransient: - _, ok := key.(*sdk.TransientStoreKey) + case types.StoreTypeTransient: + _, ok := key.(*types.TransientStoreKey) if !ok { err = fmt.Errorf("invalid StoreKey for StoreTypeTransient: %s", key.String()) return } - store = newTransientStore() + store = transient.NewStore() return default: panic(fmt.Sprintf("unrecognized store type %v", params.typ)) } } -func (rs *rootMultiStore) nameToKey(name string) StoreKey { +func (rs *Store) nameToKey(name string) types.StoreKey { for key := range rs.storesParams { if key.Name() == name { return key @@ -386,9 +389,9 @@ func (rs *rootMultiStore) nameToKey(name string) StoreKey { // storeParams type storeParams struct { - key StoreKey + key types.StoreKey db dbm.DB - typ StoreType + typ types.StoreType } //---------------------------------------- @@ -415,8 +418,8 @@ func (ci commitInfo) Hash() []byte { return merkle.SimpleHashFromMap(m) } -func (ci commitInfo) CommitID() CommitID { - return CommitID{ +func (ci commitInfo) CommitID() types.CommitID { + return types.CommitID{ Version: ci.Version, Hash: ci.Hash(), } @@ -426,7 +429,7 @@ func (ci commitInfo) CommitID() CommitID { // storeInfo // storeInfo contains the name and core reference for an -// underlying store. It is the leaf of the rootMultiStores top +// underlying store. It is the leaf of the Stores top // level simple merkle tree. type storeInfo struct { Name string @@ -435,7 +438,7 @@ type storeInfo struct { type storeCore struct { // StoreType StoreType - CommitID CommitID + CommitID types.CommitID // ... maybe add more state } @@ -480,14 +483,14 @@ func setLatestVersion(batch dbm.Batch, version int64) { } // Commits each store and returns a new commitInfo. -func commitStores(version int64, storeMap map[StoreKey]CommitStore) commitInfo { +func commitStores(version int64, storeMap map[types.StoreKey]types.CommitStore) commitInfo { storeInfos := make([]storeInfo, 0, len(storeMap)) for key, store := range storeMap { // Commit commitID := store.Commit() - if store.GetStoreType() == sdk.StoreTypeTransient { + if store.GetStoreType() == types.StoreTypeTransient { continue } @@ -513,14 +516,14 @@ func getCommitInfo(db dbm.DB, ver int64) (commitInfo, error) { cInfoKey := fmt.Sprintf(commitInfoKeyFmt, ver) cInfoBytes := db.Get([]byte(cInfoKey)) if cInfoBytes == nil { - return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: no data") + return commitInfo{}, fmt.Errorf("failed to get Store: no data") } var cInfo commitInfo err := cdc.UnmarshalBinaryLengthPrefixed(cInfoBytes, &cInfo) if err != nil { - return commitInfo{}, fmt.Errorf("failed to get rootMultiStore: %v", err) + return commitInfo{}, fmt.Errorf("failed to get Store: %v", err) } return cInfo, nil diff --git a/store/rootmultistore_test.go b/store/rootmulti/store_test.go similarity index 71% rename from store/rootmultistore_test.go rename to store/rootmulti/store_test.go index 10f095656..75aed91b6 100644 --- a/store/rootmultistore_test.go +++ b/store/rootmulti/store_test.go @@ -1,4 +1,4 @@ -package store +package rootmulti import ( "testing" @@ -8,32 +8,33 @@ import ( "github.com/tendermint/tendermint/crypto/merkle" dbm "github.com/tendermint/tendermint/libs/db" - sdk "github.com/cosmos/cosmos-sdk/types" + stypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/types" ) const useDebugDB = false func TestStoreType(t *testing.T) { db := dbm.NewMemDB() - store := NewCommitMultiStore(db) + store := NewStore(db) store.MountStoreWithDB( - sdk.NewKVStoreKey("store1"), sdk.StoreTypeIAVL, db) + types.NewKVStoreKey("store1"), types.StoreTypeIAVL, db) } func TestStoreMount(t *testing.T) { db := dbm.NewMemDB() - store := NewCommitMultiStore(db) + store := NewStore(db) - key1 := sdk.NewKVStoreKey("store1") - key2 := sdk.NewKVStoreKey("store2") - dup1 := sdk.NewKVStoreKey("store1") + key1 := types.NewKVStoreKey("store1") + key2 := types.NewKVStoreKey("store2") + dup1 := types.NewKVStoreKey("store1") - require.NotPanics(t, func() { store.MountStoreWithDB(key1, sdk.StoreTypeIAVL, db) }) - require.NotPanics(t, func() { store.MountStoreWithDB(key2, sdk.StoreTypeIAVL, db) }) + require.NotPanics(t, func() { store.MountStoreWithDB(key1, types.StoreTypeIAVL, db) }) + require.NotPanics(t, func() { store.MountStoreWithDB(key2, types.StoreTypeIAVL, db) }) - require.Panics(t, func() { store.MountStoreWithDB(key1, sdk.StoreTypeIAVL, db) }) - require.Panics(t, func() { store.MountStoreWithDB(dup1, sdk.StoreTypeIAVL, db) }) + require.Panics(t, func() { store.MountStoreWithDB(key1, types.StoreTypeIAVL, db) }) + require.Panics(t, func() { store.MountStoreWithDB(dup1, types.StoreTypeIAVL, db) }) } func TestMultistoreCommitLoad(t *testing.T) { @@ -46,7 +47,7 @@ func TestMultistoreCommitLoad(t *testing.T) { require.Nil(t, err) // New store has empty last commit. - commitID := CommitID{} + commitID := types.CommitID{} checkStore(t, store, commitID, commitID) // Make sure we can get stores by name. @@ -137,11 +138,11 @@ func TestMultiStoreQuery(t *testing.T) { require.Nil(t, garbage) // Set and commit data in one store. - store1 := multi.getStoreByName("store1").(KVStore) + store1 := multi.getStoreByName("store1").(types.KVStore) store1.Set(k, v) // ... and another. - store2 := multi.getStoreByName("store2").(KVStore) + store2 := multi.getStoreByName("store2").(types.KVStore) store2.Set(k2, v2) // Commit the multistore. @@ -156,69 +157,69 @@ func TestMultiStoreQuery(t *testing.T) { // Test bad path. query := abci.RequestQuery{Path: "/key", Data: k, Height: ver} qres := multi.Query(query) - require.EqualValues(t, sdk.CodeUnknownRequest, qres.Code) - require.EqualValues(t, sdk.CodespaceRoot, qres.Codespace) + require.EqualValues(t, types.CodeUnknownRequest, qres.Code) + require.EqualValues(t, types.CodespaceRoot, qres.Codespace) query.Path = "h897fy32890rf63296r92" qres = multi.Query(query) - require.EqualValues(t, sdk.CodeUnknownRequest, qres.Code) - require.EqualValues(t, sdk.CodespaceRoot, qres.Codespace) + require.EqualValues(t, types.CodeUnknownRequest, qres.Code) + require.EqualValues(t, types.CodespaceRoot, qres.Codespace) // Test invalid store name. query.Path = "/garbage/key" qres = multi.Query(query) - require.EqualValues(t, sdk.CodeUnknownRequest, qres.Code) - require.EqualValues(t, sdk.CodespaceRoot, qres.Codespace) + require.EqualValues(t, types.CodeUnknownRequest, qres.Code) + require.EqualValues(t, types.CodespaceRoot, qres.Codespace) // Test valid query with data. query.Path = "/store1/key" qres = multi.Query(query) - require.EqualValues(t, sdk.CodeOK, qres.Code) + require.EqualValues(t, types.CodeOK, qres.Code) require.Equal(t, v, qres.Value) // Test valid but empty query. query.Path = "/store2/key" query.Prove = true qres = multi.Query(query) - require.EqualValues(t, sdk.CodeOK, qres.Code) + require.EqualValues(t, types.CodeOK, qres.Code) require.Nil(t, qres.Value) // Test store2 data. query.Data = k2 qres = multi.Query(query) - require.EqualValues(t, sdk.CodeOK, qres.Code) + require.EqualValues(t, types.CodeOK, qres.Code) require.Equal(t, v2, qres.Value) } //----------------------------------------------------------------------- // utils -func newMultiStoreWithMounts(db dbm.DB) *rootMultiStore { - store := NewCommitMultiStore(db) - store.pruningOpts = PruneSyncable +func newMultiStoreWithMounts(db dbm.DB) *Store { + store := NewStore(db) + store.pruningOpts = stypes.PruneSyncable store.MountStoreWithDB( - sdk.NewKVStoreKey("store1"), sdk.StoreTypeIAVL, nil) + types.NewKVStoreKey("store1"), types.StoreTypeIAVL, nil) store.MountStoreWithDB( - sdk.NewKVStoreKey("store2"), sdk.StoreTypeIAVL, nil) + types.NewKVStoreKey("store2"), types.StoreTypeIAVL, nil) store.MountStoreWithDB( - sdk.NewKVStoreKey("store3"), sdk.StoreTypeIAVL, nil) + types.NewKVStoreKey("store3"), types.StoreTypeIAVL, nil) return store } -func checkStore(t *testing.T, store *rootMultiStore, expect, got CommitID) { +func checkStore(t *testing.T, store *Store, expect, got types.CommitID) { require.Equal(t, expect, got) require.Equal(t, expect, store.LastCommitID()) } -func getExpectedCommitID(store *rootMultiStore, ver int64) CommitID { - return CommitID{ +func getExpectedCommitID(store *Store, ver int64) types.CommitID { + return types.CommitID{ Version: ver, Hash: hashStores(store.stores), } } -func hashStores(stores map[StoreKey]CommitStore) []byte { +func hashStores(stores map[types.StoreKey]types.CommitStore) []byte { m := make(map[string][]byte, len(stores)) for key, store := range stores { name := key.Name() diff --git a/store/rootmulti/wire.go b/store/rootmulti/wire.go new file mode 100644 index 000000000..8d6d93616 --- /dev/null +++ b/store/rootmulti/wire.go @@ -0,0 +1,7 @@ +package rootmulti + +import ( + "github.com/cosmos/cosmos-sdk/codec" +) + +var cdc = codec.New() diff --git a/store/store.go b/store/store.go new file mode 100644 index 000000000..cb7a0ada8 --- /dev/null +++ b/store/store.go @@ -0,0 +1,26 @@ +package store + +import ( + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/rootmulti" + "github.com/cosmos/cosmos-sdk/store/types" +) + +func NewCommitMultiStore(db dbm.DB) types.CommitMultiStore { + return rootmulti.NewStore(db) +} + +func NewPruningOptionsFromString(strategy string) (opt PruningOptions) { + switch strategy { + case "nothing": + opt = PruneNothing + case "everything": + opt = PruneEverything + case "syncable": + opt = PruneSyncable + default: + opt = PruneSyncable + } + return +} diff --git a/store/tracekvstore.go b/store/tracekv/store.go similarity index 68% rename from store/tracekvstore.go rename to store/tracekv/store.go index d8c34c354..ee9558ecb 100644 --- a/store/tracekvstore.go +++ b/store/tracekv/store.go @@ -1,4 +1,4 @@ -package store +package tracekv import ( "encoding/base64" @@ -6,7 +6,7 @@ import ( "fmt" "io" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/types" ) const ( @@ -18,16 +18,16 @@ const ( ) type ( - // TraceKVStore implements the KVStore interface with tracing enabled. + // Store implements the KVStore interface with tracing enabled. // Operations are traced on each core KVStore call and written to the // underlying io.writer. // // TODO: Should we use a buffered writer and implement Commit on - // TraceKVStore? - TraceKVStore struct { - parent sdk.KVStore + // Store? + Store struct { + parent types.KVStore writer io.Writer - context TraceContext + context types.TraceContext } // operation represents an IO operation @@ -42,15 +42,15 @@ type ( } ) -// NewTraceKVStore returns a reference to a new traceKVStore given a parent +// NewStore returns a reference to a new traceKVStore given a parent // KVStore implementation and a buffered writer. -func NewTraceKVStore(parent sdk.KVStore, writer io.Writer, tc TraceContext) *TraceKVStore { - return &TraceKVStore{parent: parent, writer: writer, context: tc} +func NewStore(parent types.KVStore, writer io.Writer, tc types.TraceContext) *Store { + return &Store{parent: parent, writer: writer, context: tc} } // Get implements the KVStore interface. It traces a read operation and // delegates a Get call to the parent KVStore. -func (tkv *TraceKVStore) Get(key []byte) []byte { +func (tkv *Store) Get(key []byte) []byte { value := tkv.parent.Get(key) writeOperation(tkv.writer, readOp, tkv.context, key, value) @@ -59,50 +59,40 @@ func (tkv *TraceKVStore) Get(key []byte) []byte { // Set implements the KVStore interface. It traces a write operation and // delegates the Set call to the parent KVStore. -func (tkv *TraceKVStore) Set(key []byte, value []byte) { +func (tkv *Store) Set(key []byte, value []byte) { writeOperation(tkv.writer, writeOp, tkv.context, key, value) tkv.parent.Set(key, value) } // Delete implements the KVStore interface. It traces a write operation and // delegates the Delete call to the parent KVStore. -func (tkv *TraceKVStore) Delete(key []byte) { +func (tkv *Store) Delete(key []byte) { writeOperation(tkv.writer, deleteOp, tkv.context, key, nil) tkv.parent.Delete(key) } // Has implements the KVStore interface. It delegates the Has call to the // parent KVStore. -func (tkv *TraceKVStore) Has(key []byte) bool { +func (tkv *Store) Has(key []byte) bool { return tkv.parent.Has(key) } -// Prefix implements the KVStore interface. -func (tkv *TraceKVStore) Prefix(prefix []byte) KVStore { - return prefixStore{tkv, prefix} -} - -// Gas implements the KVStore interface. -func (tkv *TraceKVStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, tkv.parent) -} - // Iterator implements the KVStore interface. It delegates the Iterator call // the to the parent KVStore. -func (tkv *TraceKVStore) Iterator(start, end []byte) sdk.Iterator { +func (tkv *Store) Iterator(start, end []byte) types.Iterator { return tkv.iterator(start, end, true) } // ReverseIterator implements the KVStore interface. It delegates the // ReverseIterator call the to the parent KVStore. -func (tkv *TraceKVStore) ReverseIterator(start, end []byte) sdk.Iterator { +func (tkv *Store) ReverseIterator(start, end []byte) types.Iterator { return tkv.iterator(start, end, false) } // iterator facilitates iteration over a KVStore. It delegates the necessary // calls to it's parent KVStore. -func (tkv *TraceKVStore) iterator(start, end []byte, ascending bool) sdk.Iterator { - var parent sdk.Iterator +func (tkv *Store) iterator(start, end []byte, ascending bool) types.Iterator { + var parent types.Iterator if ascending { parent = tkv.parent.Iterator(start, end) @@ -114,12 +104,12 @@ func (tkv *TraceKVStore) iterator(start, end []byte, ascending bool) sdk.Iterato } type traceIterator struct { - parent sdk.Iterator + parent types.Iterator writer io.Writer - context TraceContext + context types.TraceContext } -func newTraceIterator(w io.Writer, parent sdk.Iterator, tc TraceContext) sdk.Iterator { +func newTraceIterator(w io.Writer, parent types.Iterator, tc types.TraceContext) types.Iterator { return &traceIterator{writer: w, parent: parent, context: tc} } @@ -161,26 +151,26 @@ func (ti *traceIterator) Close() { // GetStoreType implements the KVStore interface. It returns the underlying // KVStore type. -func (tkv *TraceKVStore) GetStoreType() sdk.StoreType { +func (tkv *Store) GetStoreType() types.StoreType { return tkv.parent.GetStoreType() } -// CacheWrap implements the KVStore interface. It panics as a TraceKVStore +// CacheWrap implements the KVStore interface. It panics as a Store // cannot be cache wrapped. -func (tkv *TraceKVStore) CacheWrap() sdk.CacheWrap { - panic("cannot CacheWrap a TraceKVStore") +func (tkv *Store) CacheWrap() types.CacheWrap { + panic("cannot CacheWrap a Store") } // CacheWrapWithTrace implements the KVStore interface. It panics as a -// TraceKVStore cannot be cache wrapped. -func (tkv *TraceKVStore) CacheWrapWithTrace(_ io.Writer, _ TraceContext) CacheWrap { - panic("cannot CacheWrapWithTrace a TraceKVStore") +// Store cannot be cache wrapped. +func (tkv *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { + panic("cannot CacheWrapWithTrace a Store") } // writeOperation writes a KVStore operation to the underlying io.Writer as // JSON-encoded data where the key/value pair is base64 encoded. // nolint: errcheck -func writeOperation(w io.Writer, op operation, tc TraceContext, key, value []byte) { +func writeOperation(w io.Writer, op operation, tc types.TraceContext, key, value []byte) { traceOp := traceOperation{ Operation: op, Key: base64.StdEncoding.EncodeToString(key), diff --git a/store/tracekvstore_test.go b/store/tracekv/store_test.go similarity index 89% rename from store/tracekvstore_test.go rename to store/tracekv/store_test.go index 887fcf96e..36d101583 100644 --- a/store/tracekvstore_test.go +++ b/store/tracekv/store_test.go @@ -1,22 +1,33 @@ -package store +package tracekv_test import ( "bytes" + "fmt" "io" "testing" "github.com/stretchr/testify/require" dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/prefix" + "github.com/cosmos/cosmos-sdk/store/tracekv" + "github.com/cosmos/cosmos-sdk/store/types" ) -var kvPairs = []KVPair{ +func bz(s string) []byte { return []byte(s) } + +func keyFmt(i int) []byte { return bz(fmt.Sprintf("key%0.8d", i)) } +func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) } + +var kvPairs = []types.KVPair{ {Key: keyFmt(1), Value: valFmt(1)}, {Key: keyFmt(2), Value: valFmt(2)}, {Key: keyFmt(3), Value: valFmt(3)}, } -func newTraceKVStore(w io.Writer) *TraceKVStore { +func newTraceKVStore(w io.Writer) *tracekv.Store { store := newEmptyTraceKVStore(w) for _, kvPair := range kvPairs { @@ -26,11 +37,11 @@ func newTraceKVStore(w io.Writer) *TraceKVStore { return store } -func newEmptyTraceKVStore(w io.Writer) *TraceKVStore { - memDB := dbStoreAdapter{dbm.NewMemDB()} - tc := TraceContext(map[string]interface{}{"blockHeight": 64}) +func newEmptyTraceKVStore(w io.Writer) *tracekv.Store { + memDB := dbadapter.Store{dbm.NewMemDB()} + tc := types.TraceContext(map[string]interface{}{"blockHeight": 64}) - return NewTraceKVStore(memDB, w, tc) + return tracekv.NewStore(memDB, w, tc) } func TestTraceKVStoreGet(t *testing.T) { @@ -263,12 +274,12 @@ func TestTestTraceKVStoreReverseIterator(t *testing.T) { func TestTraceKVStorePrefix(t *testing.T) { store := newEmptyTraceKVStore(nil) - pStore := store.Prefix([]byte("trace_prefix")) - require.IsType(t, prefixStore{}, pStore) + pStore := prefix.NewStore(store, []byte("trace_prefix")) + require.IsType(t, prefix.Store{}, pStore) } func TestTraceKVStoreGetStoreType(t *testing.T) { - memDB := dbStoreAdapter{dbm.NewMemDB()} + memDB := dbadapter.Store{dbm.NewMemDB()} store := newEmptyTraceKVStore(nil) require.Equal(t, memDB.GetStoreType(), store.GetStoreType()) } diff --git a/store/transient/store.go b/store/transient/store.go new file mode 100644 index 000000000..0a7e945e3 --- /dev/null +++ b/store/transient/store.go @@ -0,0 +1,42 @@ +package transient + +import ( + "github.com/cosmos/cosmos-sdk/store/types" + dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/dbadapter" +) + +var _ types.Committer = (*Store)(nil) +var _ types.KVStore = (*Store)(nil) + +// Store is a wrapper for a MemDB with Commiter implementation +type Store struct { + dbadapter.Store +} + +// Constructs new MemDB adapter +func NewStore() *Store { + return &Store{dbadapter.Store{dbm.NewMemDB()}} +} + +// Implements CommitStore +// Commit cleans up Store. +func (ts *Store) Commit() (id types.CommitID) { + ts.Store = dbadapter.Store{dbm.NewMemDB()} + return +} + +// Implements CommitStore +func (ts *Store) SetPruning(pruning types.PruningOptions) { +} + +// Implements CommitStore +func (ts *Store) LastCommitID() (id types.CommitID) { + return +} + +// Implements Store. +func (ts *Store) GetStoreType() types.StoreType { + return types.StoreTypeTransient +} diff --git a/store/transientstore_test.go b/store/transient/store_test.go similarity index 86% rename from store/transientstore_test.go rename to store/transient/store_test.go index 1c9e98cfa..846c8a3a4 100644 --- a/store/transientstore_test.go +++ b/store/transient/store_test.go @@ -1,4 +1,4 @@ -package store +package transient import ( "testing" @@ -9,7 +9,7 @@ import ( var k, v = []byte("hello"), []byte("world") func TestTransientStore(t *testing.T) { - tstore := newTransientStore() + tstore := NewStore() require.Nil(t, tstore.Get(k)) diff --git a/store/transientstore.go b/store/transientstore.go deleted file mode 100644 index 2de1197b9..000000000 --- a/store/transientstore.go +++ /dev/null @@ -1,50 +0,0 @@ -package store - -import ( - dbm "github.com/tendermint/tendermint/libs/db" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -var _ KVStore = (*transientStore)(nil) - -// transientStore is a wrapper for a MemDB with Commiter implementation -type transientStore struct { - dbStoreAdapter -} - -// Constructs new MemDB adapter -func newTransientStore() *transientStore { - return &transientStore{dbStoreAdapter{dbm.NewMemDB()}} -} - -// Implements CommitStore -// Commit cleans up transientStore. -func (ts *transientStore) Commit() (id CommitID) { - ts.dbStoreAdapter = dbStoreAdapter{dbm.NewMemDB()} - return -} - -// Implements CommitStore -func (ts *transientStore) SetPruning(opts PruningOptions) { -} - -// Implements CommitStore -func (ts *transientStore) LastCommitID() (id CommitID) { - return -} - -// Implements KVStore -func (ts *transientStore) Prefix(prefix []byte) KVStore { - return prefixStore{ts, prefix} -} - -// Implements KVStore -func (ts *transientStore) Gas(meter GasMeter, config GasConfig) KVStore { - return NewGasKVStore(meter, config, ts) -} - -// Implements Store. -func (ts *transientStore) GetStoreType() StoreType { - return sdk.StoreTypeTransient -} diff --git a/types/gas.go b/store/types/gas.go similarity index 89% rename from types/gas.go rename to store/types/gas.go index 2daecdeb1..0acbcd7e2 100644 --- a/types/gas.go +++ b/store/types/gas.go @@ -1,5 +1,7 @@ package types +import "math" + // Gas consumption descriptors. const ( GasIterNextCostFlatDesc = "IterNextFlat" @@ -69,11 +71,20 @@ func (g *basicGasMeter) GasConsumedToLimit() Gas { return g.consumed } +// addUint64Overflow performs the addition operation on two uint64 integers and +// returns a boolean on whether or not the result overflows. +func addUint64Overflow(a, b uint64) (uint64, bool) { + if math.MaxUint64-a < b { + return 0, true + } + + return a + b, false +} + func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { var overflow bool - // TODO: Should we set the consumed field after overflow checking? - g.consumed, overflow = AddUint64Overflow(g.consumed, amount) + g.consumed, overflow = addUint64Overflow(g.consumed, amount) if overflow { panic(ErrorGasOverflow{descriptor}) } @@ -81,6 +92,7 @@ func (g *basicGasMeter) ConsumeGas(amount Gas, descriptor string) { if g.consumed > g.limit { panic(ErrorOutOfGas{descriptor}) } + } func (g *basicGasMeter) IsPastLimit() bool { @@ -116,9 +128,8 @@ func (g *infiniteGasMeter) Limit() Gas { func (g *infiniteGasMeter) ConsumeGas(amount Gas, descriptor string) { var overflow bool - // TODO: Should we set the consumed field after overflow checking? - g.consumed, overflow = AddUint64Overflow(g.consumed, amount) + g.consumed, overflow = addUint64Overflow(g.consumed, amount) if overflow { panic(ErrorGasOverflow{descriptor}) } diff --git a/types/gas_test.go b/store/types/gas_test.go similarity index 70% rename from types/gas_test.go rename to store/types/gas_test.go index 5f862dccd..81bb0c901 100644 --- a/types/gas_test.go +++ b/store/types/gas_test.go @@ -1,6 +1,7 @@ package types import ( + "math" "testing" "github.com/stretchr/testify/require" @@ -43,3 +44,28 @@ func TestGasMeter(t *testing.T) { } } + +func TestAddUint64Overflow(t *testing.T) { + testCases := []struct { + a, b uint64 + result uint64 + overflow bool + }{ + {0, 0, 0, false}, + {100, 100, 200, false}, + {math.MaxUint64 / 2, math.MaxUint64/2 + 1, math.MaxUint64, false}, + {math.MaxUint64 / 2, math.MaxUint64/2 + 2, 0, true}, + } + + for i, tc := range testCases { + res, overflow := addUint64Overflow(tc.a, tc.b) + require.Equal( + t, tc.overflow, overflow, + "invalid overflow result; tc: #%d, a: %d, b: %d", i, tc.a, tc.b, + ) + require.Equal( + t, tc.result, res, + "invalid uint64 result; tc: #%d, a: %d, b: %d", i, tc.a, tc.b, + ) + } +} diff --git a/store/types/pruning.go b/store/types/pruning.go new file mode 100644 index 000000000..f82023a2c --- /dev/null +++ b/store/types/pruning.go @@ -0,0 +1,35 @@ +package types + +// PruningStrategy specifies how old states will be deleted over time where +// keepRecent can be used with keepEvery to create a pruning "strategy". +type PruningOptions struct { + keepRecent int64 + keepEvery int64 +} + +func NewPruningOptions(keepRecent, keepEvery int64) PruningOptions { + return PruningOptions{ + keepRecent: keepRecent, + keepEvery: keepEvery, + } +} + +// How much recent state will be kept. Older state will be deleted. +func (po PruningOptions) KeepRecent() int64 { + return po.keepRecent +} + +// Keeps every N stated, deleting others. +func (po PruningOptions) KeepEvery() int64 { + return po.keepEvery +} + +// default pruning strategies +var ( + // PruneEverything means all saved states will be deleted, storing only the current state + PruneEverything = NewPruningOptions(0, 0) + // PruneNothing means all historic states will be saved, nothing will be deleted + PruneNothing = NewPruningOptions(0, 1) + // PruneSyncable means only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th) + PruneSyncable = NewPruningOptions(100, 10000) +) diff --git a/store/types/store.go b/store/types/store.go new file mode 100644 index 000000000..db696f688 --- /dev/null +++ b/store/types/store.go @@ -0,0 +1,274 @@ +package types + +import ( + "fmt" + "io" + + abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + dbm "github.com/tendermint/tendermint/libs/db" +) + +type Store interface { //nolint + GetStoreType() StoreType + CacheWrapper +} + +// something that can persist to disk +type Committer interface { + Commit() CommitID + LastCommitID() CommitID + SetPruning(PruningOptions) +} + +// Stores of MultiStore must implement CommitStore. +type CommitStore interface { + Committer + Store +} + +// Queryable allows a Store to expose internal state to the abci.Query +// interface. Multistore can route requests to the proper Store. +// +// This is an optional, but useful extension to any CommitStore +type Queryable interface { + Query(abci.RequestQuery) abci.ResponseQuery +} + +//---------------------------------------- +// MultiStore + +type MultiStore interface { //nolint + Store + + // Cache wrap MultiStore. + // NOTE: Caller should probably not call .Write() on each, but + // call CacheMultiStore.Write(). + CacheMultiStore() CacheMultiStore + + // Convenience for fetching substores. + // If the store does not exist, panics. + GetStore(StoreKey) Store + GetKVStore(StoreKey) KVStore + + // TracingEnabled returns if tracing is enabled for the MultiStore. + TracingEnabled() bool + + // SetTracer sets the tracer for the MultiStore that the underlying + // stores will utilize to trace operations. The modified MultiStore is + // returned. + SetTracer(w io.Writer) MultiStore + + // SetTracingContext sets the tracing context for a MultiStore. It is + // implied that the caller should update the context when necessary between + // tracing operations. The modified MultiStore is returned. + SetTracingContext(TraceContext) MultiStore +} + +// From MultiStore.CacheMultiStore().... +type CacheMultiStore interface { + MultiStore + Write() // Writes operations to underlying KVStore +} + +// A non-cache MultiStore. +type CommitMultiStore interface { + Committer + MultiStore + + // Mount a store of type using the given db. + // If db == nil, the new store will use the CommitMultiStore db. + MountStoreWithDB(key StoreKey, typ StoreType, db dbm.DB) + + // Panics on a nil key. + GetCommitStore(key StoreKey) CommitStore + + // Panics on a nil key. + GetCommitKVStore(key StoreKey) CommitKVStore + + // Load the latest persisted version. Called once after all + // calls to Mount*Store() are complete. + LoadLatestVersion() error + + // Load a specific persisted version. When you load an old + // version, or when the last commit attempt didn't complete, + // the next commit after loading must be idempotent (return the + // same commit id). Otherwise the behavior is undefined. + LoadVersion(ver int64) error +} + +//---------subsp------------------------------- +// KVStore + +// KVStore is a simple interface to get/set data +type KVStore interface { + Store + + // Get returns nil iff key doesn't exist. Panics on nil key. + Get(key []byte) []byte + + // Has checks if a key exists. Panics on nil key. + Has(key []byte) bool + + // Set sets the key. Panics on nil key or value. + Set(key, value []byte) + + // Delete deletes the key. Panics on nil key. + Delete(key []byte) + + // Iterator over a domain of keys in ascending order. End is exclusive. + // Start must be less than end, or the Iterator is invalid. + // Iterator must be closed by caller. + // To iterate over entire domain, use store.Iterator(nil, nil) + // CONTRACT: No writes may happen within a domain while an iterator exists over it. + // Exceptionally allowed for cachekv.Store, safe to write in the modules. + Iterator(start, end []byte) Iterator + + // Iterator over a domain of keys in descending order. End is exclusive. + // Start must be less than end, or the Iterator is invalid. + // Iterator must be closed by caller. + // CONTRACT: No writes may happen within a domain while an iterator exists over it. + // Exceptionally allowed for cachekv.Store, safe to write in the modules. + ReverseIterator(start, end []byte) Iterator +} + +// Alias iterator to db's Iterator for convenience. +type Iterator = dbm.Iterator + +// CacheKVStore cache-wraps a KVStore. After calling .Write() on +// the CacheKVStore, all previously created CacheKVStores on the +// object expire. +type CacheKVStore interface { + KVStore + + // Writes operations to underlying KVStore + Write() +} + +// Stores of MultiStore must implement CommitStore. +type CommitKVStore interface { + Committer + KVStore +} + +//---------------------------------------- +// CacheWrap + +// CacheWrap makes the most appropriate cache-wrap. For example, +// IAVLStore.CacheWrap() returns a CacheKVStore. CacheWrap should not return +// a Committer, since Commit cache-wraps make no sense. It can return KVStore, +// HeapStore, SpaceStore, etc. +type CacheWrap interface { + // Write syncs with the underlying store. + Write() + + // CacheWrap recursively wraps again. + CacheWrap() CacheWrap + + // CacheWrapWithTrace recursively wraps again with tracing enabled. + CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap +} + +type CacheWrapper interface { //nolint + // CacheWrap cache wraps. + CacheWrap() CacheWrap + + // CacheWrapWithTrace cache wraps with tracing enabled. + CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap +} + +//---------------------------------------- +// CommitID + +// CommitID contains the tree version number and its merkle root. +type CommitID struct { + Version int64 + Hash []byte +} + +func (cid CommitID) IsZero() bool { //nolint + return cid.Version == 0 && len(cid.Hash) == 0 +} + +func (cid CommitID) String() string { + return fmt.Sprintf("CommitID{%v:%X}", cid.Hash, cid.Version) +} + +//---------------------------------------- +// Store types + +// kind of store +type StoreType int + +const ( + //nolint + StoreTypeMulti StoreType = iota + StoreTypeDB + StoreTypeIAVL + StoreTypeTransient +) + +//---------------------------------------- +// Keys for accessing substores + +// StoreKey is a key used to index stores in a MultiStore. +type StoreKey interface { + Name() string + String() string +} + +// KVStoreKey is used for accessing substores. +// Only the pointer value should ever be used - it functions as a capabilities key. +type KVStoreKey struct { + name string +} + +// NewKVStoreKey returns a new pointer to a KVStoreKey. +// Use a pointer so keys don't collide. +func NewKVStoreKey(name string) *KVStoreKey { + return &KVStoreKey{ + name: name, + } +} + +func (key *KVStoreKey) Name() string { + return key.name +} + +func (key *KVStoreKey) String() string { + return fmt.Sprintf("KVStoreKey{%p, %s}", key, key.name) +} + +// TransientStoreKey is used for indexing transient stores in a MultiStore +type TransientStoreKey struct { + name string +} + +// Constructs new TransientStoreKey +// Must return a pointer according to the ocap principle +func NewTransientStoreKey(name string) *TransientStoreKey { + return &TransientStoreKey{ + name: name, + } +} + +// Implements StoreKey +func (key *TransientStoreKey) Name() string { + return key.name +} + +// Implements StoreKey +func (key *TransientStoreKey) String() string { + return fmt.Sprintf("TransientStoreKey{%p, %s}", key, key.name) +} + +//---------------------------------------- + +// key-value result for iterator queries +type KVPair cmn.KVPair + +//---------------------------------------- + +// TraceContext contains TraceKVStore context data. It will be written with +// every trace operation. +type TraceContext map[string]interface{} diff --git a/store/types/utils.go b/store/types/utils.go new file mode 100644 index 000000000..a86efdb8c --- /dev/null +++ b/store/types/utils.go @@ -0,0 +1,98 @@ +package types + +import ( + "bytes" + + cmn "github.com/tendermint/tendermint/libs/common" +) + +// Iterator over all the keys with a certain prefix in ascending order +func KVStorePrefixIterator(kvs KVStore, prefix []byte) Iterator { + return kvs.Iterator(prefix, PrefixEndBytes(prefix)) +} + +// Iterator over all the keys with a certain prefix in descending order. +func KVStoreReversePrefixIterator(kvs KVStore, prefix []byte) Iterator { + return kvs.ReverseIterator(prefix, PrefixEndBytes(prefix)) +} + +// Compare two KVstores, return either the first key/value pair +// at which they differ and whether or not they are equal, skipping +// value comparison for a set of provided prefixes +func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvA cmn.KVPair, kvB cmn.KVPair, count int64, equal bool) { + iterA := a.Iterator(nil, nil) + iterB := b.Iterator(nil, nil) + count = int64(0) + for { + if !iterA.Valid() && !iterB.Valid() { + break + } + var kvA, kvB cmn.KVPair + if iterA.Valid() { + kvA = cmn.KVPair{Key: iterA.Key(), Value: iterA.Value()} + iterA.Next() + } + if iterB.Valid() { + kvB = cmn.KVPair{Key: iterB.Key(), Value: iterB.Value()} + iterB.Next() + } + if !bytes.Equal(kvA.Key, kvB.Key) { + return kvA, kvB, count, false + } + compareValue := true + for _, prefix := range prefixesToSkip { + // Skip value comparison if we matched a prefix + if bytes.Equal(kvA.Key[:len(prefix)], prefix) { + compareValue = false + } + } + if compareValue && !bytes.Equal(kvA.Value, kvB.Value) { + return kvA, kvB, count, false + } + count++ + } + return cmn.KVPair{}, cmn.KVPair{}, count, true +} + +// PrefixEndBytes returns the []byte that would end a +// range query for all []byte with a certain prefix +// Deals with last byte of prefix being FF without overflowing +func PrefixEndBytes(prefix []byte) []byte { + if prefix == nil { + return nil + } + + end := make([]byte, len(prefix)) + copy(end, prefix) + + for { + if end[len(end)-1] != byte(255) { + end[len(end)-1]++ + break + } else { + end = end[:len(end)-1] + if len(end) == 0 { + end = nil + break + } + } + } + return end +} + +// InclusiveEndBytes returns the []byte that would end a +// range query such that the input would be included +func InclusiveEndBytes(inclusiveBytes []byte) (exclusiveBytes []byte) { + exclusiveBytes = append(inclusiveBytes, byte(0x00)) + return exclusiveBytes +} + +//---------------------------------------- +func Cp(bz []byte) (ret []byte) { + if bz == nil { + return nil + } + ret = make([]byte, len(bz)) + copy(ret, bz) + return ret +} diff --git a/store/types/validity.go b/store/types/validity.go new file mode 100644 index 000000000..59dbd0c03 --- /dev/null +++ b/store/types/validity.go @@ -0,0 +1,15 @@ +package types + +// Check if the key is valid(key is not nil) +func AssertValidKey(key []byte) { + if key == nil { + panic("key is nil") + } +} + +// Check if the value is valid(value is not nil) +func AssertValidValue(value []byte) { + if value == nil { + panic("value is nil") + } +} diff --git a/store/validity.go b/store/validity.go deleted file mode 100644 index 5a20f1fb3..000000000 --- a/store/validity.go +++ /dev/null @@ -1,13 +0,0 @@ -package store - -func assertValidKey(key []byte) { - if key == nil { - panic("key is nil") - } -} - -func assertValidValue(value []byte) { - if value == nil { - panic("value is nil") - } -} diff --git a/types/context.go b/types/context.go index 3b815ee20..1663a94e6 100644 --- a/types/context.go +++ b/types/context.go @@ -10,6 +10,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/libs/log" + + "github.com/cosmos/cosmos-sdk/store/gaskv" + stypes "github.com/cosmos/cosmos-sdk/store/types" ) /* @@ -46,7 +49,7 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, logger log.Lo c = c.WithTxBytes(nil) c = c.WithLogger(logger) c = c.WithVoteInfos(nil) - c = c.WithGasMeter(NewInfiniteGasMeter()) + c = c.WithGasMeter(stypes.NewInfiniteGasMeter()) c = c.WithMinGasPrices(DecCoins{}) c = c.WithConsensusParams(nil) return c @@ -74,12 +77,12 @@ func (c Context) Value(key interface{}) interface{} { // KVStore fetches a KVStore from the MultiStore. func (c Context) KVStore(key StoreKey) KVStore { - return c.MultiStore().GetKVStore(key).Gas(c.GasMeter(), cachedKVGasConfig) + return gaskv.NewStore(c.MultiStore().GetKVStore(key), c.GasMeter(), stypes.KVGasConfig()) } // TransientStore fetches a TransientStore from the MultiStore. func (c Context) TransientStore(key StoreKey) KVStore { - return c.MultiStore().GetKVStore(key).Gas(c.GasMeter(), cachedTransientGasConfig) + return gaskv.NewStore(c.MultiStore().GetKVStore(key), c.GasMeter(), stypes.TransientGasConfig()) } //---------------------------------------- diff --git a/types/int.go b/types/int.go index dec7e81c8..5c4ff4245 100644 --- a/types/int.go +++ b/types/int.go @@ -2,7 +2,6 @@ package types import ( "encoding/json" - "math" "testing" "math/big" @@ -575,16 +574,6 @@ func UintOverflow(x Uint) bool { return x.i.Sign() == -1 || x.i.Sign() == 1 && x.i.BitLen() > 256 } -// AddUint64Overflow performs the addition operation on two uint64 integers and -// returns a boolean on whether or not the result overflows. -func AddUint64Overflow(a, b uint64) (uint64, bool) { - if math.MaxUint64-a < b { - return 0, true - } - - return a + b, false -} - // intended to be used with require/assert: require.True(IntEq(...)) func IntEq(t *testing.T, exp, got Int) (*testing.T, bool, string, string, string) { return t, exp.Equal(got), "expected:\t%v\ngot:\t\t%v", exp.String(), got.String() diff --git a/types/int_test.go b/types/int_test.go index 18d968e90..a6c3c9002 100644 --- a/types/int_test.go +++ b/types/int_test.go @@ -631,28 +631,3 @@ func TestSafeSub(t *testing.T) { ) } } - -func TestAddUint64Overflow(t *testing.T) { - testCases := []struct { - a, b uint64 - result uint64 - overflow bool - }{ - {0, 0, 0, false}, - {100, 100, 200, false}, - {math.MaxUint64 / 2, math.MaxUint64/2 + 1, math.MaxUint64, false}, - {math.MaxUint64 / 2, math.MaxUint64/2 + 2, 0, true}, - } - - for i, tc := range testCases { - res, overflow := AddUint64Overflow(tc.a, tc.b) - require.Equal( - t, tc.overflow, overflow, - "invalid overflow result; tc: #%d, a: %d, b: %d", i, tc.a, tc.b, - ) - require.Equal( - t, tc.result, res, - "invalid uint64 result; tc: #%d, a: %d, b: %d", i, tc.a, tc.b, - ) - } -} diff --git a/types/store.go b/types/store.go index 3118fba8f..1b5b20213 100644 --- a/types/store.go +++ b/types/store.go @@ -1,398 +1,130 @@ package types import ( - "bytes" - "fmt" - "io" - - abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" - dbm "github.com/tendermint/tendermint/libs/db" + + "github.com/cosmos/cosmos-sdk/store/types" ) -// NOTE: These are implemented in cosmos-sdk/store. +// nolint - reexport +type ( + PruningOptions = types.PruningOptions +) -// PruningStrategy specifies how old states will be deleted over time where -// keepRecent can be used with keepEvery to create a pruning "strategy". -type PruningOptions struct { - keepRecent int64 - keepEvery int64 -} - -func NewPruningOptions(keepRecent, keepEvery int64) PruningOptions { - return PruningOptions{ - keepRecent: keepRecent, - keepEvery: keepEvery, - } -} - -// How much recent state will be kept. Older state will be deleted. -func (po PruningOptions) KeepRecent() int64 { - return po.keepRecent -} - -// Keeps every N stated, deleting others. -func (po PruningOptions) KeepEvery() int64 { - return po.keepEvery -} - -type Store interface { //nolint - GetStoreType() StoreType - CacheWrapper -} - -// something that can persist to disk -type Committer interface { - Commit() CommitID - LastCommitID() CommitID - SetPruning(PruningOptions) -} - -// Stores of MultiStore must implement CommitStore. -type CommitStore interface { - Committer - Store -} - -// Queryable allows a Store to expose internal state to the abci.Query -// interface. Multistore can route requests to the proper Store. -// -// This is an optional, but useful extension to any CommitStore -type Queryable interface { - Query(abci.RequestQuery) abci.ResponseQuery -} - -//---------------------------------------- -// MultiStore - -type MultiStore interface { //nolint - Store - - // Cache wrap MultiStore. - // NOTE: Caller should probably not call .Write() on each, but - // call CacheMultiStore.Write(). - CacheMultiStore() CacheMultiStore - - // Convenience for fetching substores. - // If the store does not exist, panics. - GetStore(StoreKey) Store - GetKVStore(StoreKey) KVStore - - // TracingEnabled returns if tracing is enabled for the MultiStore. - TracingEnabled() bool - - // WithTracer sets the tracer for the MultiStore that the underlying - // stores will utilize to trace operations. A MultiStore is returned. - WithTracer(w io.Writer) MultiStore - - // WithTracingContext sets the tracing context for a MultiStore. It is - // implied that the caller should update the context when necessary between - // tracing operations. A MultiStore is returned. - WithTracingContext(TraceContext) MultiStore - - // ResetTraceContext resets the current tracing context. - ResetTraceContext() MultiStore -} - -// From MultiStore.CacheMultiStore().... -type CacheMultiStore interface { - MultiStore - Write() // Writes operations to underlying KVStore -} - -// A non-cache MultiStore. -type CommitMultiStore interface { - Committer - MultiStore - - // Mount a store of type using the given db. - // If db == nil, the new store will use the CommitMultiStore db. - MountStoreWithDB(key StoreKey, typ StoreType, db dbm.DB) - - // Panics on a nil key. - GetCommitStore(key StoreKey) CommitStore - - // Panics on a nil key. - GetCommitKVStore(key StoreKey) CommitKVStore - - // Load the latest persisted version. Called once after all - // calls to Mount*Store() are complete. - LoadLatestVersion() error - - // Load a specific persisted version. When you load an old - // version, or when the last commit attempt didn't complete, - // the next commit after loading must be idempotent (return the - // same commit id). Otherwise the behavior is undefined. - LoadVersion(ver int64) error -} - -//---------subsp------------------------------- -// KVStore - -// KVStore is a simple interface to get/set data -type KVStore interface { - Store - - // Get returns nil iff key doesn't exist. Panics on nil key. - Get(key []byte) []byte - - // Has checks if a key exists. Panics on nil key. - Has(key []byte) bool - - // Set sets the key. Panics on nil key or value. - Set(key, value []byte) - - // Delete deletes the key. Panics on nil key. - Delete(key []byte) - - // Iterator over a domain of keys in ascending order. End is exclusive. - // Start must be less than end, or the Iterator is invalid. - // Iterator must be closed by caller. - // To iterate over entire domain, use store.Iterator(nil, nil) - // CONTRACT: No writes may happen within a domain while an iterator exists over it. - Iterator(start, end []byte) Iterator - - // Iterator over a domain of keys in descending order. End is exclusive. - // Start must be less than end, or the Iterator is invalid. - // Iterator must be closed by caller. - // CONTRACT: No writes may happen within a domain while an iterator exists over it. - ReverseIterator(start, end []byte) Iterator - - // TODO Not yet implemented. - // CreateSubKVStore(key *storeKey) (KVStore, error) - - // TODO Not yet implemented. - // GetSubKVStore(key *storeKey) KVStore - - // Prefix applied keys with the argument - // CONTRACT: when Prefix is called on a KVStore more than once, - // the concatanation of the prefixes is applied - Prefix(prefix []byte) KVStore - - // Gas consuming store - // CONTRACT: when Gas is called on a KVStore more than once, - // the concatanation of the meters/configs is applied - Gas(GasMeter, GasConfig) KVStore -} - -// Alias iterator to db's Iterator for convenience. -type Iterator = dbm.Iterator +// nolint - reexport +type ( + Store = types.Store + Committer = types.Committer + CommitStore = types.CommitStore + Queryable = types.Queryable + MultiStore = types.MultiStore + CacheMultiStore = types.CacheMultiStore + CommitMultiStore = types.CommitMultiStore + KVStore = types.KVStore + Iterator = types.Iterator +) // Iterator over all the keys with a certain prefix in ascending order func KVStorePrefixIterator(kvs KVStore, prefix []byte) Iterator { - return kvs.Iterator(prefix, PrefixEndBytes(prefix)) + return types.KVStorePrefixIterator(kvs, prefix) } // Iterator over all the keys with a certain prefix in descending order. func KVStoreReversePrefixIterator(kvs KVStore, prefix []byte) Iterator { - return kvs.ReverseIterator(prefix, PrefixEndBytes(prefix)) + return types.KVStoreReversePrefixIterator(kvs, prefix) } // Compare two KVstores, return either the first key/value pair // at which they differ and whether or not they are equal, skipping // value comparison for a set of provided prefixes func DiffKVStores(a KVStore, b KVStore, prefixesToSkip [][]byte) (kvA cmn.KVPair, kvB cmn.KVPair, count int64, equal bool) { - iterA := a.Iterator(nil, nil) - iterB := b.Iterator(nil, nil) - count = int64(0) - for { - if !iterA.Valid() && !iterB.Valid() { - break - } - var kvA, kvB cmn.KVPair - if iterA.Valid() { - kvA = cmn.KVPair{Key: iterA.Key(), Value: iterA.Value()} - iterA.Next() - } - if iterB.Valid() { - kvB = cmn.KVPair{Key: iterB.Key(), Value: iterB.Value()} - iterB.Next() - } - if !bytes.Equal(kvA.Key, kvB.Key) { - return kvA, kvB, count, false - } - compareValue := true - for _, prefix := range prefixesToSkip { - // Skip value comparison if we matched a prefix - if bytes.Equal(kvA.Key[:len(prefix)], prefix) { - compareValue = false - } - } - if compareValue && !bytes.Equal(kvA.Value, kvB.Value) { - return kvA, kvB, count, false - } - count++ - } - return cmn.KVPair{}, cmn.KVPair{}, count, true + return types.DiffKVStores(a, b, prefixesToSkip) } -// CacheKVStore cache-wraps a KVStore. After calling .Write() on -// the CacheKVStore, all previously created CacheKVStores on the -// object expire. -type CacheKVStore interface { - KVStore - - // Writes operations to underlying KVStore - Write() -} - -// Stores of MultiStore must implement CommitStore. -type CommitKVStore interface { - Committer - KVStore -} - -//---------------------------------------- -// CacheWrap - -// CacheWrap makes the most appropriate cache-wrap. For example, -// IAVLStore.CacheWrap() returns a CacheKVStore. CacheWrap should not return -// a Committer, since Commit cache-wraps make no sense. It can return KVStore, -// HeapStore, SpaceStore, etc. -type CacheWrap interface { - // Write syncs with the underlying store. - Write() - - // CacheWrap recursively wraps again. - CacheWrap() CacheWrap - - // CacheWrapWithTrace recursively wraps again with tracing enabled. - CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap -} - -type CacheWrapper interface { //nolint - // CacheWrap cache wraps. - CacheWrap() CacheWrap - - // CacheWrapWithTrace cache wraps with tracing enabled. - CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap -} - -//---------------------------------------- -// CommitID - -// CommitID contains the tree version number and its merkle root. -type CommitID struct { - Version int64 - Hash []byte -} - -func (cid CommitID) IsZero() bool { //nolint - return cid.Version == 0 && len(cid.Hash) == 0 -} - -func (cid CommitID) String() string { - return fmt.Sprintf("CommitID{%v:%X}", cid.Hash, cid.Version) -} - -//---------------------------------------- -// Store types - -// kind of store -type StoreType int - -const ( - //nolint - StoreTypeMulti StoreType = iota - StoreTypeDB - StoreTypeIAVL - StoreTypeTransient +// nolint - reexport +type ( + CacheKVStore = types.CacheKVStore + CommitKVStore = types.CommitKVStore + CacheWrap = types.CacheWrap + CacheWrapper = types.CacheWrapper + CommitID = types.CommitID ) -//---------------------------------------- -// Keys for accessing substores +// nolint - reexport +type StoreType = types.StoreType -// StoreKey is a key used to index stores in a MultiStore. -type StoreKey interface { - Name() string - String() string -} +// nolint - reexport +const ( + StoreTypeMulti = types.StoreTypeMulti + StoreTypeDB = types.StoreTypeDB + StoreTypeIAVL = types.StoreTypeIAVL + StoreTypeTransient = types.StoreTypeTransient +) -// KVStoreKey is used for accessing substores. -// Only the pointer value should ever be used - it functions as a capabilities key. -type KVStoreKey struct { - name string -} +// nolint - reexport +type ( + StoreKey = types.StoreKey + KVStoreKey = types.KVStoreKey + TransientStoreKey = types.TransientStoreKey +) // NewKVStoreKey returns a new pointer to a KVStoreKey. // Use a pointer so keys don't collide. func NewKVStoreKey(name string) *KVStoreKey { - return &KVStoreKey{ - name: name, - } + return types.NewKVStoreKey(name) } -func (key *KVStoreKey) Name() string { - return key.name -} - -func (key *KVStoreKey) String() string { - return fmt.Sprintf("KVStoreKey{%p, %s}", key, key.name) +// Constructs new TransientStoreKey +// Must return a pointer according to the ocap principle +func NewTransientStoreKey(name string) *TransientStoreKey { + return types.NewTransientStoreKey(name) } // PrefixEndBytes returns the []byte that would end a // range query for all []byte with a certain prefix // Deals with last byte of prefix being FF without overflowing func PrefixEndBytes(prefix []byte) []byte { - if prefix == nil { - return nil - } - - end := make([]byte, len(prefix)) - copy(end, prefix) - - for { - if end[len(end)-1] != byte(255) { - end[len(end)-1]++ - break - } else { - end = end[:len(end)-1] - if len(end) == 0 { - end = nil - break - } - } - } - return end + return types.PrefixEndBytes(prefix) } // InclusiveEndBytes returns the []byte that would end a // range query such that the input would be included func InclusiveEndBytes(inclusiveBytes []byte) (exclusiveBytes []byte) { - exclusiveBytes = append(inclusiveBytes, byte(0x00)) - return exclusiveBytes -} - -// TransientStoreKey is used for indexing transient stores in a MultiStore -type TransientStoreKey struct { - name string -} - -// Constructs new TransientStoreKey -// Must return a pointer according to the ocap principle -func NewTransientStoreKey(name string) *TransientStoreKey { - return &TransientStoreKey{ - name: name, - } -} - -// Implements StoreKey -func (key *TransientStoreKey) Name() string { - return key.name -} - -// Implements StoreKey -func (key *TransientStoreKey) String() string { - return fmt.Sprintf("TransientStoreKey{%p, %s}", key, key.name) + return types.InclusiveEndBytes(inclusiveBytes) } //---------------------------------------- // key-value result for iterator queries -type KVPair cmn.KVPair +type KVPair = types.KVPair //---------------------------------------- // TraceContext contains TraceKVStore context data. It will be written with // every trace operation. -type TraceContext map[string]interface{} +type TraceContext = types.TraceContext + +// -------------------------------------- + +// nolint - reexport +type ( + Gas = types.Gas + GasMeter = types.GasMeter + GasConfig = types.GasConfig +) + +// nolint - reexport +func NewGasMeter(limit Gas) GasMeter { + return types.NewGasMeter(limit) +} + +// nolint - reexport +type ( + ErrorOutOfGas = types.ErrorOutOfGas + ErrorGasOverflow = types.ErrorGasOverflow +) + +// nolint - reexport +func NewInfiniteGasMeter() GasMeter { + return types.NewInfiniteGasMeter() +} diff --git a/x/params/keeper_test.go b/x/params/keeper_test.go index 585db3c41..cda57de8e 100644 --- a/x/params/keeper_test.go +++ b/x/params/keeper_test.go @@ -12,6 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store" + "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -71,7 +72,7 @@ func TestKeeper(t *testing.T) { ctx := defaultContext(skey, tkey) keeper := NewKeeper(cdc, skey, tkey) space := keeper.Subspace("test").WithTypeTable(table) - store := ctx.KVStore(skey).Prefix([]byte("test/")) + store := prefix.NewStore(ctx.KVStore(skey), []byte("test/")) // Set params for i, kv := range kvs { @@ -177,7 +178,7 @@ func TestSubspace(t *testing.T) { []byte("struct"), s{}, ) - store := ctx.KVStore(key).Prefix([]byte("test/")) + store := prefix.NewStore(ctx.KVStore(key), []byte("test/")) space := keeper.Subspace("test").WithTypeTable(table) // Test space.Set, space.Modified diff --git a/x/params/subspace/subspace.go b/x/params/subspace/subspace.go index 2ace19232..cf8e345dd 100644 --- a/x/params/subspace/subspace.go +++ b/x/params/subspace/subspace.go @@ -5,6 +5,8 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/cosmos/cosmos-sdk/store/prefix" ) const ( @@ -69,14 +71,14 @@ func (s Subspace) WithTypeTable(table TypeTable) Subspace { func (s Subspace) kvStore(ctx sdk.Context) sdk.KVStore { // append here is safe, appends within a function won't cause // weird side effects when its singlethreaded - return ctx.KVStore(s.key).Prefix(append(s.name, '/')) + return prefix.NewStore(ctx.KVStore(s.key), append(s.name, '/')) } // Returns a KVStore identical with ctx.TransientStore(s.tkey).Prefix() func (s Subspace) transientStore(ctx sdk.Context) sdk.KVStore { // append here is safe, appends within a function won't cause // weird side effects when its singlethreaded - return ctx.TransientStore(s.tkey).Prefix(append(s.name, '/')) + return prefix.NewStore(ctx.TransientStore(s.tkey), append(s.name, '/')) } // Get parameter from store diff --git a/x/params/subspace/test_common.go b/x/params/subspace/test_common.go index e39b9c1b2..f6bf9c73f 100644 --- a/x/params/subspace/test_common.go +++ b/x/params/subspace/test_common.go @@ -27,8 +27,8 @@ func DefaultTestComponents(t *testing.T, table TypeTable) (sdk.Context, Subspace tkey := sdk.NewTransientStoreKey(TStoreKey) db := dbm.NewMemDB() ms := store.NewCommitMultiStore(db) - ms.WithTracer(os.Stdout) - ms.WithTracingContext(sdk.TraceContext{}) + ms.SetTracer(os.Stdout) + ms.SetTracingContext(sdk.TraceContext{}) ms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db) ms.MountStoreWithDB(tkey, sdk.StoreTypeTransient, db) err := ms.LoadLatestVersion() From 884ce49f7ff35bc8460e4e2f6acf27eb78656faa Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 1 Feb 2019 17:03:57 -0800 Subject: [PATCH 35/99] allow generation of gentxs with empty memo field (#3478) --- PENDING.md | 1 + cmd/gaia/init/gentx.go | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/PENDING.md b/PENDING.md index 75c3c799b..3f62c3b34 100644 --- a/PENDING.md +++ b/PENDING.md @@ -51,6 +51,7 @@ IMPROVEMENTS * [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account genesis validation checks to `GaiaValidateGenesisState`. * [\#3420](https://github.com/cosmos/cosmos-sdk/issues/3420) Added maximum length to governance proposal descriptions and titles + * [\#3424](https://github.com/cosmos/cosmos-sdk/issues/3424) Allow generation of gentxs with empty memo field. * SDK * [\#2986](https://github.com/cosmos/cosmos-sdk/pull/2986) Store Refactor diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 142a9801e..ec3ae3573 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -61,9 +61,11 @@ following delegation and commission default parameters: if err != nil { return err } + ip, err := server.ExternalIP() if err != nil { - return err + fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP, "+ + "consequently the tx's memo field will be unset: %s", err) } genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile()) From 097a640412ce4b9e5c7d9d77b33090dd6531c1f5 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Fri, 1 Feb 2019 20:04:13 -0500 Subject: [PATCH 36/99] Allow Simulation and Gen Only in REST Client (#3467) --- PENDING.md | 2 ++ client/errors.go | 9 +++++++++ client/utils/rest.go | 24 ++++++++++++++++++---- client/utils/utils.go | 27 +++++++++++-------------- docs/gaia/gaiacli.md | 9 --------- x/auth/client/txbuilder/txbuilder.go | 30 +++++++++------------------- x/bank/client/rest/sendtx.go | 2 +- x/gov/client/rest/rest.go | 6 +++--- x/ibc/client/rest/transfer.go | 2 +- x/slashing/client/rest/tx.go | 2 +- x/staking/client/rest/tx.go | 6 +++--- 11 files changed, 61 insertions(+), 58 deletions(-) create mode 100644 client/errors.go diff --git a/PENDING.md b/PENDING.md index 3f62c3b34..b8bcf9564 100644 --- a/PENDING.md +++ b/PENDING.md @@ -44,6 +44,8 @@ IMPROVEMENTS * Automatic account number and sequence population when fields are omitted * Generate only functionality no longer requires access to a local Keybase * `from` field in the `base_req` body can be a Keybase name or account address + * [\#3423](https://github.com/cosmos/cosmos-sdk/issues/3423) Allow simulation + (auto gas) to work with generate only. * Gaia CLI (`gaiacli`) diff --git a/client/errors.go b/client/errors.go new file mode 100644 index 000000000..d01d57429 --- /dev/null +++ b/client/errors.go @@ -0,0 +1,9 @@ +package client + +import "errors" + +// common errors for CLI and REST clients +var ( + ErrInvalidGasAdjustment = errors.New("invalid gas adjustment") + ErrInvalidSigner = errors.New("tx intended signer does not match the given signer") +) diff --git a/client/utils/rest.go b/client/utils/rest.go index 44fc746ad..2926a5551 100644 --- a/client/utils/rest.go +++ b/client/utils/rest.go @@ -246,11 +246,11 @@ func CompleteAndBroadcastTxREST( if baseReq.Simulate || simAndExec { if gasAdj < 0 { - WriteErrorResponse(w, http.StatusBadRequest, "gas adjustment must be a positive float") + WriteErrorResponse(w, http.StatusBadRequest, client.ErrInvalidGasAdjustment.Error()) return } - txBldr, err = EnrichWithGas(txBldr, cliCtx, cliCtx.GetFromName(), msgs) + txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs) if err != nil { WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return @@ -308,13 +308,16 @@ func PostProcessResponse(w http.ResponseWriter, cdc *codec.Codec, response inter } // WriteGenerateStdTxResponse writes response for the generate only mode. -func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec, br BaseReq, msgs []sdk.Msg) { +func WriteGenerateStdTxResponse( + w http.ResponseWriter, cdc *codec.Codec, cliCtx context.CLIContext, br BaseReq, msgs []sdk.Msg, +) { + gasAdj, ok := ParseFloat64OrReturnBadRequest(w, br.GasAdjustment, client.DefaultGasAdjustment) if !ok { return } - _, gas, err := client.ParseGas(br.Gas) + simAndExec, gas, err := client.ParseGas(br.Gas) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -325,6 +328,19 @@ func WriteGenerateStdTxResponse(w http.ResponseWriter, cdc *codec.Codec, br Base br.Simulate, br.ChainID, br.Memo, br.Fees, br.GasPrices, ) + if simAndExec { + if gasAdj < 0 { + WriteErrorResponse(w, http.StatusBadRequest, client.ErrInvalidGasAdjustment.Error()) + return + } + + txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs) + if err != nil { + WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + } + stdMsg, err := txBldr.Build(msgs) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, err.Error()) diff --git a/client/utils/utils.go b/client/utils/utils.go index a30a54cd1..03463a208 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -6,6 +6,7 @@ import ( "io" "os" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" "github.com/tendermint/go-amino" @@ -31,10 +32,10 @@ func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIConte return err } - name := cliCtx.GetFromName() + fromName := cliCtx.GetFromName() if txBldr.GetSimulateAndExecute() || cliCtx.Simulate { - txBldr, err = EnrichWithGas(txBldr, cliCtx, name, msgs) + txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs) if err != nil { return err } @@ -44,13 +45,13 @@ func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIConte return nil } - passphrase, err := keys.GetPassphrase(name) + passphrase, err := keys.GetPassphrase(fromName) if err != nil { return err } // build and sign the transaction - txBytes, err := txBldr.BuildAndSign(name, passphrase, msgs) + txBytes, err := txBldr.BuildAndSign(fromName, passphrase, msgs) if err != nil { return err } @@ -62,8 +63,8 @@ func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIConte // EnrichWithGas calculates the gas estimate that would be consumed by the // transaction and set the transaction's respective value accordingly. -func EnrichWithGas(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (authtxb.TxBuilder, error) { - _, adjusted, err := simulateMsgs(txBldr, cliCtx, name, msgs) +func EnrichWithGas(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (authtxb.TxBuilder, error) { + _, adjusted, err := simulateMsgs(txBldr, cliCtx, msgs) if err != nil { return txBldr, err } @@ -126,8 +127,7 @@ func SignStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, // check whether the address is a signer if !isTxSigner(sdk.AccAddress(addr), stdTx.GetSigners()) { - return signedStdTx, fmt.Errorf( - "The generated transaction's intended signer does not match the given signer: %q", name) + return signedStdTx, fmt.Errorf("%s: %s", client.ErrInvalidSigner, name) } if !offline { @@ -155,8 +155,7 @@ func SignStdTxWithSignerAddress(txBldr authtxb.TxBuilder, cliCtx context.CLICont // check whether the address is a signer if !isTxSigner(addr, stdTx.GetSigners()) { - return signedStdTx, fmt.Errorf( - "The generated transaction's intended signer does not match the given signer: %q", name) + return signedStdTx, fmt.Errorf("%s: %s", client.ErrInvalidSigner, name) } if !offline { @@ -207,8 +206,8 @@ func GetTxEncoder(cdc *codec.Codec) (encoder sdk.TxEncoder) { // nolint // SimulateMsgs simulates the transaction and returns the gas estimate and the adjusted value. -func simulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (estimated, adjusted uint64, err error) { - txBytes, err := txBldr.BuildWithPubKey(name, msgs) +func simulateMsgs(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (estimated, adjusted uint64, err error) { + txBytes, err := txBldr.BuildTxForSim(msgs) if err != nil { return } @@ -269,9 +268,7 @@ func buildUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msg func buildUnsignedStdTxOffline(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx auth.StdTx, err error) { if txBldr.GetSimulateAndExecute() { - name := cliCtx.GetFromName() - - txBldr, err = EnrichWithGas(txBldr, cliCtx, name, msgs) + txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs) if err != nil { return } diff --git a/docs/gaia/gaiacli.md b/docs/gaia/gaiacli.md index 4886167e2..a3ad6dc36 100644 --- a/docs/gaia/gaiacli.md +++ b/docs/gaia/gaiacli.md @@ -233,15 +233,6 @@ gaiacli tx send \ --generate-only > unsignedSendTx.json ``` -::: tip Note -Simulation cannot be used in conjunction with tx generation only functionality -due to the fact that simulation requires a public key and generation only does -not utilize a Keybase. - -You can now sign the transaction file generated through the `--generate-only` -flag by providing your key to the following command: -::: - ```bash gaiacli tx sign \ --chain-id= \ diff --git a/x/auth/client/txbuilder/txbuilder.go b/x/auth/client/txbuilder/txbuilder.go index ed5f4339d..331d811ba 100644 --- a/x/auth/client/txbuilder/txbuilder.go +++ b/x/auth/client/txbuilder/txbuilder.go @@ -156,6 +156,8 @@ func (bldr TxBuilder) WithAccountNumber(accnum uint64) TxBuilder { // Build builds a single message to be signed from a TxBuilder given a set of // messages. It returns an error if a fee is supplied but cannot be parsed. +// +// TODO: Should consider renaming to BuildSignMsg. func (bldr TxBuilder) Build(msgs []sdk.Msg) (StdSignMsg, error) { chainID := bldr.chainID if chainID == "" { @@ -212,31 +214,17 @@ func (bldr TxBuilder) BuildAndSign(name, passphrase string, msgs []sdk.Msg) ([]b return bldr.Sign(name, passphrase, msg) } -// BuildWithPubKey builds a single message to be signed from a TxBuilder given a set of -// messages and attach the public key associated to the given name. -// It returns an error if a fee is supplied but cannot be parsed or the key cannot be -// retrieved. -func (bldr TxBuilder) BuildWithPubKey(name string, msgs []sdk.Msg) ([]byte, error) { - msg, err := bldr.Build(msgs) +// BuildTxForSim creates a StdSignMsg and encodes a transaction with the +// StdSignMsg with a single empty StdSignature for tx simulation. +func (bldr TxBuilder) BuildTxForSim(msgs []sdk.Msg) ([]byte, error) { + signMsg, err := bldr.Build(msgs) if err != nil { return nil, err } - keybase, err := keys.GetKeyBase() - if err != nil { - return nil, err - } - - info, err := keybase.Get(name) - if err != nil { - return nil, err - } - - sigs := []auth.StdSignature{{ - PubKey: info.GetPubKey(), - }} - - return bldr.txEncoder(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo)) + // the ante handler will populate with a sentinel pubkey + sigs := []auth.StdSignature{{}} + return bldr.txEncoder(auth.NewStdTx(signMsg.Msgs, signMsg.Fee, sigs, signMsg.Memo)) } // SignStdTx appends a signature to a StdTx and returns a copy of a it. If append diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index d087e9316..b4ef7d7a2 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -64,7 +64,7 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC } msg := bankclient.CreateMsg(fromAddr, toAddr, req.Amount) - utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index b39d2f700..f00c187a1 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -102,7 +102,7 @@ func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } @@ -146,7 +146,7 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } @@ -196,7 +196,7 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index a92f9ea19..e732145e2 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -66,7 +66,7 @@ func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. msg := ibc.IBCTransferMsg{IBCPacket: packet} if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 1c4266aa1..672928f3f 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -57,7 +57,7 @@ func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CL } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } diff --git a/x/staking/client/rest/tx.go b/x/staking/client/rest/tx.go index 26b821f1d..1710d340a 100644 --- a/x/staking/client/rest/tx.go +++ b/x/staking/client/rest/tx.go @@ -76,7 +76,7 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } @@ -121,7 +121,7 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } @@ -166,7 +166,7 @@ func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, req.BaseReq, []sdk.Msg{msg}) + utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } From 857a65dc610cd736a47980b5d4778e5123206a3d Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 1 Feb 2019 19:06:54 -0800 Subject: [PATCH 37/99] gaiacli switches back to insecure mode by default (#3470) --- PENDING.md | 3 +++ client/flags.go | 4 ++-- client/lcd/root.go | 6 +++--- docs/clients/lite/getting_started.md | 9 +++++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/PENDING.md b/PENDING.md index b8bcf9564..042b9dd11 100644 --- a/PENDING.md +++ b/PENDING.md @@ -9,6 +9,9 @@ BREAKING CHANGES * Gaia CLI (`gaiacli`) - [#3399](https://github.com/cosmos/cosmos-sdk/pull/3399) Add `gaiad validate-genesis` command to facilitate checking of genesis files - [\#1894](https://github.com/cosmos/cosmos-sdk/issues/1894) `version` prints out short info by default. Add `--long` flag. Proper handling of `--format` flag introduced. + - [\#3465](https://github.com/cosmos/cosmos-sdk/issues/3465) `gaiacli rest-server` switched back to insecure mode by default: + - `--insecure` flag is removed. + - `--tls` is now used to enable secure layer. * Gaia diff --git a/client/flags.go b/client/flags.go index 0c5d3423a..c76bbb3c7 100644 --- a/client/flags.go +++ b/client/flags.go @@ -40,7 +40,7 @@ const ( FlagListenAddr = "laddr" FlagCORS = "cors" FlagMaxOpenConnections = "max-open" - FlagInsecure = "insecure" + FlagTLS = "tls" FlagSSLHosts = "ssl-hosts" FlagSSLCertFile = "ssl-certfile" FlagSSLKeyFile = "ssl-keyfile" @@ -105,7 +105,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { func RegisterRestServerFlags(cmd *cobra.Command) *cobra.Command { cmd = GetCommands(cmd)[0] cmd.Flags().String(FlagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") - cmd.Flags().Bool(FlagInsecure, false, "Do not set up SSL/TLS layer") + cmd.Flags().Bool(FlagTLS, true, "Enable SSL/TLS layer") cmd.Flags().String(FlagSSLHosts, "", "Comma-separated hostnames and IPs to generate a certificate for") cmd.Flags().String(FlagSSLCertFile, "", "Path to a SSL certificate file. If not supplied, a self-signed certificate will be generated.") cmd.Flags().String(FlagSSLKeyFile, "", "Path to a key file; ignored if a certificate file is not supplied.") diff --git a/client/lcd/root.go b/client/lcd/root.go index 8014fdd92..7021149f2 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -70,7 +70,7 @@ func (rs *RestServer) setKeybase(kb keybase.Keybase) { // Start starts the rest server func (rs *RestServer) Start(listenAddr string, sslHosts string, - certFile string, keyFile string, maxOpen int, insecure bool) (err error) { + certFile string, keyFile string, maxOpen int, secure bool) (err error) { server.TrapSignal(func() { err := rs.listener.Close() @@ -88,7 +88,7 @@ func (rs *RestServer) Start(listenAddr string, sslHosts string, viper.GetString(client.FlagChainID))) // launch rest-server in insecure mode - if insecure { + if !secure { return rpcserver.StartHTTPServer(rs.listener, rs.Mux, rs.log) } @@ -146,7 +146,7 @@ func ServeCommand(cdc *codec.Codec, registerRoutesFn func(*RestServer)) *cobra.C viper.GetString(client.FlagSSLCertFile), viper.GetString(client.FlagSSLKeyFile), viper.GetInt(client.FlagMaxOpenConnections), - viper.GetBool(client.FlagInsecure)) + viper.GetBool(client.FlagTLS)) return err }, diff --git a/docs/clients/lite/getting_started.md b/docs/clients/lite/getting_started.md index faa3ea91b..47ee16f9e 100644 --- a/docs/clients/lite/getting_started.md +++ b/docs/clients/lite/getting_started.md @@ -19,17 +19,18 @@ gaiacli rest-server --chain-id=test \ --trust-node=false ``` -The server listens on HTTPS by default. You can set the SSL certificate to be used by the server with these additional flags: +The server listens on HTTP by default. You can enable the secure layer by adding the `--tls` flag. +By default a self-signed certificate will be generated and its fingerprint printed out. You can +configure the server to use a SSL certificate by passing the certificate and key files via the +`--ssl-certfile` and `--ssl-keyfile` flags: ```bash gaiacli rest-server --chain-id=test \ --laddr=tcp://localhost:1317 \ --node tcp://localhost:26657 \ --trust-node=false \ + --tls \ --ssl-certfile=mycert.pem --ssl-keyfile=mykey.key ``` -If no certificate/keyfile pair is supplied, a self-signed certificate will be generated and its fingerprint printed out. -Append `--insecure` to the command line if you want to disable the secure layer and listen on an insecure HTTP port. - For more information about the Gaia-Lite RPC, see the [swagger documentation](https://cosmos.network/rpc/) From 15225e933324c2c1213edee6451a1cb0f0486900 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Sun, 3 Feb 2019 16:34:05 -0800 Subject: [PATCH 38/99] Migration from circleci.io to build.snapcraft.io --- .circleci/config.yml | 54 ------------------------ snapcraft-full.yaml.in => snapcraft.yaml | 20 +++------ 2 files changed, 7 insertions(+), 67 deletions(-) rename snapcraft-full.yaml.in => snapcraft.yaml (58%) diff --git a/.circleci/config.yml b/.circleci/config.yml index c48ee899d..37ad00181 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -264,52 +264,6 @@ jobs: make localnet-start ./scripts/localnet-blocks-test.sh 40 5 10 localhost - snap-edge: - working_directory: /home/circleci/.snap_workspace/src/github.com/cosmos/cosmos-sdk - machine: true - environment: - LC_ALL: C.UTF-8 - LANG: C.UTF-8 - GOPATH: /tmp/workspace - GOBIN: /tmp/workspace/bin - parallelism: 1 - steps: - - checkout - - attach_workspace: - at: /tmp/workspace - - run: - name: Install prerequitistes - command: | - sudo sh -c "echo '#!/bin/sh' > /usr/sbin/update-initramfs" - sudo sh -c "echo 'exit 0' >> /usr/sbin/update-initramfs" - sudo apt update -y &>/dev/null - sudo apt install -y build-essential snapd - sudo snap install snapcraft --edge --classic - - run: - name: prepping snap build - command: | - /snap/bin/snapcraft version - make snapcraft.yaml - - run: - name: build edge snap - command: /snap/bin/snapcraft - - run: - name: Create credentials file - command: | - echo '[login.ubuntu.com]' > credentials - echo "macaroon = $SNAPCRAFT_CREDENTIALS_MACAROON" >> credentials - echo "unbound_discharge = $SNAPCRAFT_CREDENTIALS_UNBOUND_DISCHARGE" >> credentials - echo "email = $SNAPCRAFT_CREDENTIALS_EMAIL" >> credentials - - run: - name: Authenticate snapcraft - command: /snap/bin/snapcraft login --with credentials - - run: - name: Push/release snap - command: /snap/bin/snapcraft push *.snap --release edge - - run: - name: Clean up credentials file - command: rm -vf credentials - deploy_docs: <<: *docs_deploy steps: @@ -430,14 +384,6 @@ workflows: - localnet: requires: - setup_dependencies - - snap-edge: - requires: - - setup_dependencies - filters: - branches: - only: - - master - - develop - upload_coverage: requires: - test_cover diff --git a/snapcraft-full.yaml.in b/snapcraft.yaml similarity index 58% rename from snapcraft-full.yaml.in rename to snapcraft.yaml index 2b2b43feb..0c24dafe0 100644 --- a/snapcraft-full.yaml.in +++ b/snapcraft.yaml @@ -1,12 +1,11 @@ -name: gaia # you probably want to 'snapcraft register ' -# base: core18 # the base snap is the execution environment for this snap -version: '@VERSION@' # just for humans, typically '1.2+git' or '1.3.2' +name: gaia +version: git summary: Gaia Daemon # 79 char long summary description: | This snap provides the Gaia daemon gaiad and the command line tool gaiacli. grade: devel # must be 'stable' to release into candidate/stable channels -confinement: strict # use 'strict' once you have the right plugs and slots +confinement: strict apps: gaiad: @@ -24,17 +23,12 @@ parts: rootdir=$(pwd) gitroot=$(git rev-parse --show-toplevel) cd ${gitroot} && git archive \ - -o ${rootdir}/gaia-@VERSION@.tar.gz \ - --format tar.gz -9 --prefix gaia-@VERSION@/ HEAD + -o ${rootdir}/gaia-git.tar.gz \ + --format tar.gz -9 --prefix gaia-git/ HEAD cd ${rootdir} - tar xf gaia-@VERSION@.tar.gz ; rm -f gaia-@VERSION@.tar.gz + tar xf gaia-git.tar.gz ; rm -f gaia-git.tar.gz mkdir -p go/src/github.com/cosmos bin - mv gaia-@VERSION@/ go/src/github.com/cosmos/cosmos-sdk/ - - # Use the following instructions to build a package from a release. - # wget https://github.com/cosmos/cosmos-sdk/archive/v@VERSION@.tar.gz - # tar xvf v@VERSION@.tar.gz - # rm v@VERSION@.tar.gz + mv gaia-git/ go/src/github.com/cosmos/cosmos-sdk/ build-snaps: [go] override-build: | From 30fb0d79f443270ba16c268f85b0511431dd425a Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Sun, 3 Feb 2019 16:34:49 -0800 Subject: [PATCH 39/99] Add snapcraft build status badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b69baae05..d23a1edc8 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![version](https://img.shields.io/github/tag/cosmos/cosmos-sdk.svg)](https://github.com/cosmos/cosmos-sdk/releases/latest) [![CircleCI](https://circleci.com/gh/cosmos/cosmos-sdk/tree/master.svg?style=shield)](https://circleci.com/gh/cosmos/cosmos-sdk/tree/master) +[![Snap Status](https://build.snapcraft.io/badge/cosmos/cosmos-sdk.svg)](https://build.snapcraft.io/user/cosmos/cosmos-sdk) [![codecov](https://codecov.io/gh/cosmos/cosmos-sdk/branch/master/graph/badge.svg)](https://codecov.io/gh/cosmos/cosmos-sdk) [![Go Report Card](https://goreportcard.com/badge/github.com/cosmos/cosmos-sdk)](https://goreportcard.com/report/github.com/cosmos/cosmos-sdk) [![license](https://img.shields.io/github/license/cosmos/cosmos-sdk.svg)](https://github.com/cosmos/cosmos-sdk/blob/master/LICENSE) From 59727516eea6294b2e5ea0b00388dccc256c8af9 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Sun, 3 Feb 2019 16:35:13 -0800 Subject: [PATCH 40/99] Remove build-snap-edge, keep snapcraft-local.yaml for local builds --- Makefile | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 423179a0a..d583e0c76 100644 --- a/Makefile +++ b/Makefile @@ -123,7 +123,7 @@ draw_deps: tools @goviz -i github.com/cosmos/cosmos-sdk/cmd/gaia/cmd/gaiad -d 2 | dot -Tpng -o dependency-graph.png clean: - rm -f devtools-stamp vendor-deps snapcraft.yaml + rm -f devtools-stamp vendor-deps snapcraft-local.yaml distclean: clean rm -rf vendor/ @@ -256,14 +256,9 @@ localnet-stop: ######################################## ### Packaging -snapcraft.yaml: snapcraft.yaml.in +snapcraft-local.yaml: snapcraft-local.yaml.in sed "s/@VERSION@/${VERSION}/g" < $< > $@ -build-snap-edge: snapcraft.yaml - snapcraft clean - snapcraft - - # To avoid unintended conflicts with file names, always add to .PHONY # unless there is a reason not to. # https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html @@ -274,4 +269,4 @@ build-linux build-docker-gaiadnode localnet-start localnet-stop \ format check-ledger test_sim_gaia_nondeterminism test_sim_modules test_sim_gaia_fast \ test_sim_gaia_custom_genesis_fast test_sim_gaia_custom_genesis_multi_seed \ test_sim_gaia_multi_seed test_sim_gaia_import_export update_tools update_dev_tools \ -build-snap-edge devtools-clean +devtools-clean From 2e4fa268643f10a3c5d8027cab55ae4c35d3a80c Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Sun, 3 Feb 2019 20:52:08 -0800 Subject: [PATCH 41/99] Merge PR #3485: Implement new ErrorResponse --- PENDING.md | 1 + client/utils/rest.go | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/PENDING.md b/PENDING.md index 042b9dd11..bb5e7a4c6 100644 --- a/PENDING.md +++ b/PENDING.md @@ -5,6 +5,7 @@ BREAKING CHANGES * Gaia REST API (`gaiacli advanced rest-server`) * [\#3284](https://github.com/cosmos/cosmos-sdk/issues/3284) Rename the `name` field to `from` in the `base_req` body. + * [\#3485](https://github.com/cosmos/cosmos-sdk/pull/3485) Error responses are now JSON objects. * Gaia CLI (`gaiacli`) - [#3399](https://github.com/cosmos/cosmos-sdk/pull/3399) Add `gaiad validate-genesis` command to facilitate checking of genesis files diff --git a/client/utils/rest.go b/client/utils/rest.go index 2926a5551..098a21298 100644 --- a/client/utils/rest.go +++ b/client/utils/rest.go @@ -24,11 +24,23 @@ type GasEstimateResponse struct { //----------------------------------------------------------------------------- // Basic HTTP utilities +// ErrorResponse defines the attributes of a JSON error response. +type ErrorResponse struct { + Code int `json:"code,omitempty"` + Message string `json:"message"` +} + +// NewErrorResponse creates a new ErrorResponse instance. +func NewErrorResponse(code int, msg string) ErrorResponse { + return ErrorResponse{Code: code, Message: msg} +} + // WriteErrorResponse prepares and writes a HTTP error // given a status code and an error message. func WriteErrorResponse(w http.ResponseWriter, status int, err string) { + w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) - w.Write([]byte(err)) + w.Write(codec.Cdc.MustMarshalJSON(NewErrorResponse(0, err))) } // WriteSimulationResponse prepares and writes an HTTP @@ -353,6 +365,7 @@ func WriteGenerateStdTxResponse( return } + w.Header().Set("Content-Type", "application/json") w.Write(output) return } From c04c69648e83a80821f705e049c63ed5748b7fe2 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 07:48:26 -0800 Subject: [PATCH 42/99] client/utils/ reorganization (#3487) --- PENDING.md | 1 + client/lcd/lcd_test.go | 12 +-- client/lcd/test_helpers.go | 40 ++++---- client/{utils => rest}/rest.go | 130 ++------------------------ client/rest/types.go | 126 +++++++++++++++++++++++++ client/rpc/block.go | 8 +- client/rpc/root.go | 5 +- client/rpc/status.go | 5 +- client/rpc/validators.go | 7 +- client/tx/broadcast.go | 13 +-- client/tx/query.go | 7 +- client/tx/search.go | 13 +-- client/utils/utils.go | 7 +- x/auth/client/rest/query.go | 18 ++-- x/auth/client/rest/sign.go | 23 ++--- x/bank/client/rest/broadcast.go | 12 +-- x/bank/client/rest/sendtx.go | 19 ++-- x/gov/client/rest/rest.go | 157 ++++++++++++++++---------------- x/ibc/client/rest/transfer.go | 17 ++-- x/slashing/client/rest/query.go | 15 +-- x/slashing/client/rest/tx.go | 19 ++-- x/staking/client/rest/query.go | 37 ++++---- x/staking/client/rest/tx.go | 51 ++++++----- x/staking/client/rest/utils.go | 39 ++++---- 24 files changed, 404 insertions(+), 377 deletions(-) rename client/{utils => rest}/rest.go (63%) create mode 100644 client/rest/types.go diff --git a/PENDING.md b/PENDING.md index bb5e7a4c6..3aef0023f 100644 --- a/PENDING.md +++ b/PENDING.md @@ -17,6 +17,7 @@ BREAKING CHANGES * Gaia * SDK + * [\#3487](https://github.com/cosmos/cosmos-sdk/pull/3487) Move HTTP/REST utilities out of client/utils into a new dedicated client/rest package. * Tendermint diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 5e13cf474..c531a0040 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -14,13 +14,13 @@ import ( ctypes "github.com/tendermint/tendermint/rpc/core/types" - client "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/rest" "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" - tests "github.com/cosmos/cosmos-sdk/tests" + "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/version" "github.com/cosmos/cosmos-sdk/x/auth" @@ -212,7 +212,7 @@ func TestCoinSend(t *testing.T) { res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, "10000", 1.0, true, false, fees) require.Equal(t, http.StatusOK, res.StatusCode, body) - var gasEstResp utils.GasEstimateResponse + var gasEstResp rest.GasEstimateResponse require.Nil(t, cdc.UnmarshalJSON([]byte(body), &gasEstResp)) require.NotZero(t, gasEstResp.GasEstimate) @@ -288,7 +288,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { res, body, _ := doTransferWithGas(t, port, seed, name1, memo, "", addr, client.GasFlagAuto, 1, true, false, fees) require.Equal(t, http.StatusOK, res.StatusCode, body) - var gasEstResp utils.GasEstimateResponse + var gasEstResp rest.GasEstimateResponse require.Nil(t, cdc.UnmarshalJSON([]byte(body), &gasEstResp)) require.NotZero(t, gasEstResp.GasEstimate) @@ -315,7 +315,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { payload := authrest.SignBody{ Tx: msg, - BaseReq: utils.NewBaseReq( + BaseReq: rest.NewBaseReq( name1, pw, "", viper.GetString(client.FlagChainID), "", "", accnum, sequence, nil, nil, false, false, ), diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 71c2a38d6..b420215eb 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -26,9 +26,9 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" + "github.com/cosmos/cosmos-sdk/client/rest" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/client/utils" gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" @@ -646,7 +646,7 @@ func doSign(t *testing.T, port, name, password, chainID string, accnum, sequence var signedMsg auth.StdTx payload := authrest.SignBody{ Tx: msg, - BaseReq: utils.NewBaseReq( + BaseReq: rest.NewBaseReq( name, password, "", chainID, "", "", accnum, sequence, nil, nil, false, false, ), } @@ -712,7 +712,7 @@ func doTransferWithGas( from = addr.String() } - baseReq := utils.NewBaseReq( + baseReq := rest.NewBaseReq( from, password, memo, chainID, gas, fmt.Sprintf("%f", gasAdjustment), accnum, sequence, fees, nil, generateOnly, simulate, @@ -746,7 +746,7 @@ func doTransferWithGasAccAuto( receiveAddr = sdk.AccAddress(receiveInfo.GetPubKey().Address()) chainID := viper.GetString(client.FlagChainID) - baseReq := utils.NewBaseReq( + baseReq := rest.NewBaseReq( from, password, memo, chainID, gas, fmt.Sprintf("%f", gasAdjustment), 0, 0, fees, nil, generateOnly, simulate, ) @@ -764,8 +764,8 @@ func doTransferWithGasAccAuto( } type sendReq struct { - Amount sdk.Coins `json:"amount"` - BaseReq utils.BaseReq `json:"base_req"` + Amount sdk.Coins `json:"amount"` + BaseReq rest.BaseReq `json:"base_req"` } // ---------------------------------------------------------------------- @@ -779,7 +779,7 @@ func doDelegate(t *testing.T, port, name, password string, accnum := acc.GetAccountNumber() sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) - baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) + baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) msg := msgDelegationsInput{ BaseReq: baseReq, DelegatorAddr: delAddr, @@ -799,7 +799,7 @@ func doDelegate(t *testing.T, port, name, password string, } type msgDelegationsInput struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32 Delegation sdk.Coin `json:"delegation"` @@ -813,7 +813,7 @@ func doUndelegate(t *testing.T, port, name, password string, accnum := acc.GetAccountNumber() sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) - baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) + baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) msg := msgUndelegateInput{ BaseReq: baseReq, DelegatorAddr: delAddr, @@ -834,7 +834,7 @@ func doUndelegate(t *testing.T, port, name, password string, } type msgUndelegateInput struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32 SharesAmount sdk.Dec `json:"shares"` @@ -849,7 +849,7 @@ func doBeginRedelegation(t *testing.T, port, name, password string, sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) - baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) + baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) msg := msgBeginRedelegateInput{ BaseReq: baseReq, @@ -872,7 +872,7 @@ func doBeginRedelegation(t *testing.T, port, name, password string, } type msgBeginRedelegateInput struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 ValidatorSrcAddr sdk.ValAddress `json:"validator_src_addr"` // in bech32 ValidatorDstAddr sdk.ValAddress `json:"validator_dst_addr"` // in bech32 @@ -1080,7 +1080,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA accnum := acc.GetAccountNumber() sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) - baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) + baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) pr := postProposalReq{ Title: "Test", @@ -1106,7 +1106,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA } type postProposalReq struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` Title string `json:"title"` // Title of the proposal Description string `json:"description"` // Description of the proposal ProposalType string `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} @@ -1176,7 +1176,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk accnum := acc.GetAccountNumber() sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) - baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) + baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) dr := depositReq{ Depositor: proposerAddr, @@ -1198,7 +1198,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk } type depositReq struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` Depositor sdk.AccAddress `json:"depositor"` // Address of the depositor Amount sdk.Coins `json:"amount"` // Coins to add to the proposal's deposit } @@ -1230,7 +1230,7 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ac accnum := acc.GetAccountNumber() sequence := acc.GetSequence() chainID := viper.GetString(client.FlagChainID) - baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) + baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) vr := voteReq{ Voter: proposerAddr, @@ -1252,7 +1252,7 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ac } type voteReq struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` Voter sdk.AccAddress `json:"voter"` // address of the voter Option string `json:"option"` // option from OptionSet chosen by the voter } @@ -1362,7 +1362,7 @@ func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing. func doUnjail(t *testing.T, port, seed, name, password string, valAddr sdk.ValAddress, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) { chainID := viper.GetString(client.FlagChainID) - baseReq := utils.NewBaseReq(name, password, "", chainID, "", "", 1, 1, fees, nil, false, false) + baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", 1, 1, fees, nil, false, false) ur := unjailReq{ BaseReq: baseReq, @@ -1380,5 +1380,5 @@ func doUnjail(t *testing.T, port, seed, name, password string, } type unjailReq struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` } diff --git a/client/utils/rest.go b/client/rest/rest.go similarity index 63% rename from client/utils/rest.go rename to client/rest/rest.go index 098a21298..0b87e084d 100644 --- a/client/utils/rest.go +++ b/client/rest/rest.go @@ -1,14 +1,13 @@ -package utils +package rest import ( "fmt" - "io/ioutil" "net/http" "strconv" - "strings" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" sdk "github.com/cosmos/cosmos-sdk/types" @@ -16,11 +15,6 @@ import ( authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" ) -// GasEstimateResponse defines a response definition for tx gas estimation. -type GasEstimateResponse struct { - GasEstimate uint64 `json:"gas_estimate"` -} - //----------------------------------------------------------------------------- // Basic HTTP utilities @@ -105,116 +99,6 @@ func ParseFloat64OrReturnBadRequest(w http.ResponseWriter, s string, defaultIfEm //----------------------------------------------------------------------------- // Building / Sending utilities -// BaseReq defines a structure that can be embedded in other request structures -// that all share common "base" fields. -type BaseReq struct { - From string `json:"from"` - Password string `json:"password"` - Memo string `json:"memo"` - ChainID string `json:"chain_id"` - AccountNumber uint64 `json:"account_number"` - Sequence uint64 `json:"sequence"` - Fees sdk.Coins `json:"fees"` - GasPrices sdk.DecCoins `json:"gas_prices"` - Gas string `json:"gas"` - GasAdjustment string `json:"gas_adjustment"` - GenerateOnly bool `json:"generate_only"` - Simulate bool `json:"simulate"` -} - -// NewBaseReq creates a new basic request instance and sanitizes its values -func NewBaseReq( - from, password, memo, chainID string, gas, gasAdjustment string, - accNumber, seq uint64, fees sdk.Coins, gasPrices sdk.DecCoins, genOnly, simulate bool, -) BaseReq { - - return BaseReq{ - From: strings.TrimSpace(from), - Password: password, - Memo: strings.TrimSpace(memo), - ChainID: strings.TrimSpace(chainID), - Fees: fees, - GasPrices: gasPrices, - Gas: strings.TrimSpace(gas), - GasAdjustment: strings.TrimSpace(gasAdjustment), - AccountNumber: accNumber, - Sequence: seq, - GenerateOnly: genOnly, - Simulate: simulate, - } -} - -// Sanitize performs basic sanitization on a BaseReq object. -func (br BaseReq) Sanitize() BaseReq { - return NewBaseReq( - br.From, br.Password, br.Memo, br.ChainID, br.Gas, br.GasAdjustment, - br.AccountNumber, br.Sequence, br.Fees, br.GasPrices, br.GenerateOnly, br.Simulate, - ) -} - -// ValidateBasic performs basic validation of a BaseReq. If custom validation -// logic is needed, the implementing request handler should perform those -// checks manually. -func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { - if !br.GenerateOnly && !br.Simulate { - switch { - case len(br.Password) == 0: - WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified") - return false - - case len(br.ChainID) == 0: - WriteErrorResponse(w, http.StatusUnauthorized, "chain-id required but not specified") - return false - - case !br.Fees.IsZero() && !br.GasPrices.IsZero(): - // both fees and gas prices were provided - WriteErrorResponse(w, http.StatusBadRequest, "cannot provide both fees and gas prices") - return false - - case !br.Fees.IsValid() && !br.GasPrices.IsValid(): - // neither fees or gas prices were provided - WriteErrorResponse(w, http.StatusPaymentRequired, "invalid fees or gas prices provided") - return false - } - } - - if len(br.From) == 0 { - WriteErrorResponse(w, http.StatusUnauthorized, "name or address required but not specified") - return false - } - - return true -} - -/* -ReadRESTReq is a simple convenience wrapper that reads the body and -unmarshals to the req interface. - - Usage: - type SomeReq struct { - BaseReq `json:"base_req"` - CustomField string `json:"custom_field"` - } - - req := new(SomeReq) - err := ReadRESTReq(w, r, cdc, req) -*/ -func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) error { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return err - } - - err = cdc.UnmarshalJSON(body, req) - if err != nil { - WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to decode JSON payload: %s", err)) - return err - } - - return nil -} - // CompleteAndBroadcastTxREST implements a utility function that facilitates // sending a series of messages in a signed tx. In addition, it will handle // tx gas simulation and estimation. @@ -245,12 +129,12 @@ func CompleteAndBroadcastTxREST( cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) txBldr := authtxb.NewTxBuilder( - GetTxEncoder(cdc), baseReq.AccountNumber, + utils.GetTxEncoder(cdc), baseReq.AccountNumber, baseReq.Sequence, gas, gasAdj, baseReq.Simulate, baseReq.ChainID, baseReq.Memo, baseReq.Fees, baseReq.GasPrices, ) - txBldr, err = prepareTxBuilder(txBldr, cliCtx) + txBldr, err = utils.PrepareTxBuilder(txBldr, cliCtx) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return @@ -262,7 +146,7 @@ func CompleteAndBroadcastTxREST( return } - txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs) + txBldr, err = utils.EnrichWithGas(txBldr, cliCtx, msgs) if err != nil { WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return @@ -336,7 +220,7 @@ func WriteGenerateStdTxResponse( } txBldr := authtxb.NewTxBuilder( - GetTxEncoder(cdc), br.AccountNumber, br.Sequence, gas, gasAdj, + utils.GetTxEncoder(cdc), br.AccountNumber, br.Sequence, gas, gasAdj, br.Simulate, br.ChainID, br.Memo, br.Fees, br.GasPrices, ) @@ -346,7 +230,7 @@ func WriteGenerateStdTxResponse( return } - txBldr, err = EnrichWithGas(txBldr, cliCtx, msgs) + txBldr, err = utils.EnrichWithGas(txBldr, cliCtx, msgs) if err != nil { WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return diff --git a/client/rest/types.go b/client/rest/types.go new file mode 100644 index 000000000..113dc0650 --- /dev/null +++ b/client/rest/types.go @@ -0,0 +1,126 @@ +package rest + +import ( + "fmt" + "io/ioutil" + "net/http" + "strings" + + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// GasEstimateResponse defines a response definition for tx gas estimation. +type GasEstimateResponse struct { + GasEstimate uint64 `json:"gas_estimate"` +} + +// BaseReq defines a structure that can be embedded in other request structures +// that all share common "base" fields. +type BaseReq struct { + From string `json:"from"` + Password string `json:"password"` + Memo string `json:"memo"` + ChainID string `json:"chain_id"` + AccountNumber uint64 `json:"account_number"` + Sequence uint64 `json:"sequence"` + Fees sdk.Coins `json:"fees"` + GasPrices sdk.DecCoins `json:"gas_prices"` + Gas string `json:"gas"` + GasAdjustment string `json:"gas_adjustment"` + GenerateOnly bool `json:"generate_only"` + Simulate bool `json:"simulate"` +} + +// NewBaseReq creates a new basic request instance and sanitizes its values +func NewBaseReq( + from, password, memo, chainID string, gas, gasAdjustment string, + accNumber, seq uint64, fees sdk.Coins, gasPrices sdk.DecCoins, genOnly, simulate bool, +) BaseReq { + + return BaseReq{ + From: strings.TrimSpace(from), + Password: password, + Memo: strings.TrimSpace(memo), + ChainID: strings.TrimSpace(chainID), + Fees: fees, + GasPrices: gasPrices, + Gas: strings.TrimSpace(gas), + GasAdjustment: strings.TrimSpace(gasAdjustment), + AccountNumber: accNumber, + Sequence: seq, + GenerateOnly: genOnly, + Simulate: simulate, + } +} + +// Sanitize performs basic sanitization on a BaseReq object. +func (br BaseReq) Sanitize() BaseReq { + return NewBaseReq( + br.From, br.Password, br.Memo, br.ChainID, br.Gas, br.GasAdjustment, + br.AccountNumber, br.Sequence, br.Fees, br.GasPrices, br.GenerateOnly, br.Simulate, + ) +} + +// ValidateBasic performs basic validation of a BaseReq. If custom validation +// logic is needed, the implementing request handler should perform those +// checks manually. +func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { + if !br.GenerateOnly && !br.Simulate { + switch { + case len(br.Password) == 0: + WriteErrorResponse(w, http.StatusUnauthorized, "password required but not specified") + return false + + case len(br.ChainID) == 0: + WriteErrorResponse(w, http.StatusUnauthorized, "chain-id required but not specified") + return false + + case !br.Fees.IsZero() && !br.GasPrices.IsZero(): + // both fees and gas prices were provided + WriteErrorResponse(w, http.StatusBadRequest, "cannot provide both fees and gas prices") + return false + + case !br.Fees.IsValid() && !br.GasPrices.IsValid(): + // neither fees or gas prices were provided + WriteErrorResponse(w, http.StatusPaymentRequired, "invalid fees or gas prices provided") + return false + } + } + + if len(br.From) == 0 { + WriteErrorResponse(w, http.StatusUnauthorized, "name or address required but not specified") + return false + } + + return true +} + +/* +ReadRESTReq is a simple convenience wrapper that reads the body and +unmarshals to the req interface. + + Usage: + type SomeReq struct { + BaseReq `json:"base_req"` + CustomField string `json:"custom_field"` + } + + req := new(SomeReq) + err := ReadRESTReq(w, r, cdc, req) +*/ +func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) error { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return err + } + + err = cdc.UnmarshalJSON(body, req) + if err != nil { + WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to decode JSON payload: %s", err)) + return err + } + + return nil +} diff --git a/client/rpc/block.go b/client/rpc/block.go index 95f5acb99..d5aaecc1f 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -5,6 +5,8 @@ import ( "net/http" "strconv" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" @@ -12,8 +14,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" - - "github.com/cosmos/cosmos-sdk/client/utils" ) //BlockCommand returns the verified block data for a given heights @@ -131,7 +131,7 @@ func BlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { w.Write([]byte(err.Error())) return } - utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) } } @@ -150,6 +150,6 @@ func LatestBlockRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { w.Write([]byte(err.Error())) return } - utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) } } diff --git a/client/rpc/root.go b/client/rpc/root.go index 83ee51786..6ea831cac 100644 --- a/client/rpc/root.go +++ b/client/rpc/root.go @@ -4,10 +4,11 @@ import ( "fmt" "net/http" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/gorilla/mux" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/version" ) @@ -34,7 +35,7 @@ func NodeVersionRequestHandler(cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { version, err := cliCtx.Query("/app/version", nil) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } diff --git a/client/rpc/status.go b/client/rpc/status.go index 86bb1ef31..438346f16 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -5,6 +5,8 @@ import ( "net/http" "strconv" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/spf13/cobra" "github.com/spf13/viper" @@ -12,7 +14,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" ) // StatusCommand returns the status of the network @@ -77,7 +78,7 @@ func NodeInfoRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { } nodeInfo := status.NodeInfo - utils.PostProcessResponse(w, cdc, nodeInfo, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, nodeInfo, cliCtx.Indent) } } diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 0992f8504..379b3bc6f 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -6,6 +6,8 @@ import ( "net/http" "strconv" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -14,7 +16,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -157,7 +158,7 @@ func ValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerFunc { w.Write([]byte(err.Error())) return } - utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) } } @@ -177,6 +178,6 @@ func LatestValidatorSetRequestHandlerFn(cliCtx context.CLIContext) http.HandlerF w.Write([]byte(err.Error())) return } - utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) } } diff --git a/client/tx/broadcast.go b/client/tx/broadcast.go index 4080b68ec..9dd7b3abd 100644 --- a/client/tx/broadcast.go +++ b/client/tx/broadcast.go @@ -3,10 +3,11 @@ package tx import ( "net/http" + "github.com/cosmos/cosmos-sdk/client/rest" + "io/ioutil" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" ) @@ -32,12 +33,12 @@ func BroadcastTxRequest(cliCtx context.CLIContext, cdc *codec.Codec) http.Handle var m BroadcastBody body, err := ioutil.ReadAll(r.Body) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } err = cdc.UnmarshalJSON(body, &m) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } var res interface{} @@ -49,13 +50,13 @@ func BroadcastTxRequest(cliCtx context.CLIContext, cdc *codec.Codec) http.Handle case flagAsync: res, err = cliCtx.BroadcastTxAsync(m.TxBytes) default: - utils.WriteErrorResponse(w, http.StatusInternalServerError, "unsupported return type. supported types: block, sync, async") + rest.WriteErrorResponse(w, http.StatusInternalServerError, "unsupported return type. supported types: block, sync, async") return } if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } diff --git a/client/tx/query.go b/client/tx/query.go index e169286e3..754b5ba96 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -5,6 +5,8 @@ import ( "fmt" "net/http" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/tendermint/tendermint/libs/common" "github.com/gorilla/mux" @@ -16,7 +18,6 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -142,9 +143,9 @@ func QueryTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.H output, err := queryTx(cdc, cliCtx, hashHexStr) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, output, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, output, cliCtx.Indent) } } diff --git a/client/tx/search.go b/client/tx/search.go index 29537e00b..97beee075 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -8,9 +8,10 @@ import ( "strconv" "strings" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -175,28 +176,28 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http. var txs []Info err := r.ParseForm() if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("could not parse query parameters", err.Error())) + rest.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("could not parse query parameters", err.Error())) return } if len(r.Form) == 0 { - utils.PostProcessResponse(w, cdc, txs, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent) return } tags, page, limit, err = parseHTTPArgs(r) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } txs, err = SearchTxs(cliCtx, cdc, tags, page, limit) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, txs, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, txs, cliCtx.Indent) } } diff --git a/client/utils/utils.go b/client/utils/utils.go index 03463a208..daad43156 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -27,7 +27,7 @@ import ( // // NOTE: Also see CompleteAndBroadcastTxREST. func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) error { - txBldr, err := prepareTxBuilder(txBldr, cliCtx) + txBldr, err := PrepareTxBuilder(txBldr, cliCtx) if err != nil { return err } @@ -227,7 +227,8 @@ func parseQueryResponse(cdc *amino.Codec, rawRes []byte) (uint64, error) { return simulationResult.GasUsed, nil } -func prepareTxBuilder(txBldr authtxb.TxBuilder, cliCtx context.CLIContext) (authtxb.TxBuilder, error) { +// PrepareTxBuilder populates a TxBuilder in preparation for the build of a Tx. +func PrepareTxBuilder(txBldr authtxb.TxBuilder, cliCtx context.CLIContext) (authtxb.TxBuilder, error) { if err := cliCtx.EnsureAccountExists(); err != nil { return txBldr, err } @@ -259,7 +260,7 @@ func prepareTxBuilder(txBldr authtxb.TxBuilder, cliCtx context.CLIContext) (auth // buildUnsignedStdTx builds a StdTx as per the parameters passed in the // contexts. Gas is automatically estimated if gas wanted is set to 0. func buildUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) (stdTx auth.StdTx, err error) { - txBldr, err = prepareTxBuilder(txBldr, cliCtx) + txBldr, err = PrepareTxBuilder(txBldr, cliCtx) if err != nil { return } diff --git a/x/auth/client/rest/query.go b/x/auth/client/rest/query.go index e60e0fdc1..50ae9e234 100644 --- a/x/auth/client/rest/query.go +++ b/x/auth/client/rest/query.go @@ -4,7 +4,7 @@ import ( "net/http" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" + "github.com/cosmos/cosmos-sdk/client/rest" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -39,13 +39,13 @@ func QueryAccountRequestHandlerFn( addr, err := sdk.AccAddressFromBech32(bech32addr) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } res, err := cliCtx.QueryStore(auth.AddressStoreKey(addr), storeName) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } @@ -58,11 +58,11 @@ func QueryAccountRequestHandlerFn( // decode the value account, err := decoder(res) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, account, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, account, cliCtx.Indent) } } @@ -78,13 +78,13 @@ func QueryBalancesRequestHandlerFn( addr, err := sdk.AccAddressFromBech32(bech32addr) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } res, err := cliCtx.QueryStore(auth.AddressStoreKey(addr), storeName) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } @@ -97,10 +97,10 @@ func QueryBalancesRequestHandlerFn( // decode the value account, err := decoder(res) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, account.GetCoins(), cliCtx.Indent) + rest.PostProcessResponse(w, cdc, account.GetCoins(), cliCtx.Indent) } } diff --git a/x/auth/client/rest/sign.go b/x/auth/client/rest/sign.go index 11b8c083b..50ec66055 100644 --- a/x/auth/client/rest/sign.go +++ b/x/auth/client/rest/sign.go @@ -4,6 +4,7 @@ import ( "net/http" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/rest" "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" @@ -14,9 +15,9 @@ import ( // SignBody defines the properties of a sign request's body. type SignBody struct { - Tx auth.StdTx `json:"tx"` - AppendSig bool `json:"append_sig"` - BaseReq utils.BaseReq `json:"base_req"` + Tx auth.StdTx `json:"tx"` + AppendSig bool `json:"append_sig"` + BaseReq rest.BaseReq `json:"base_req"` } // nolint: unparam @@ -25,8 +26,8 @@ func SignTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha return func(w http.ResponseWriter, r *http.Request) { var m SignBody - if err := utils.ReadRESTReq(w, r, cdc, &m); err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if err := rest.ReadRESTReq(w, r, cdc, &m); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -37,14 +38,14 @@ func SignTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha // validate tx // discard error if it's CodeNoSignatures as the tx comes with no signatures if err := m.Tx.ValidateBasic(); err != nil && err.Code() != sdk.CodeNoSignatures { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } // derive the from account address and name from the Keybase fromAddress, fromName, err := context.GetFromFields(m.BaseReq.From) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -65,16 +66,16 @@ func SignTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha signedTx, err := txBldr.SignStdTx(cliCtx.GetFromName(), m.BaseReq.Password, m.Tx, m.AppendSig) if keyerror.IsErrKeyNotFound(err) { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } else if keyerror.IsErrWrongPassword(err) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) + rest.WriteErrorResponse(w, http.StatusUnauthorized, err.Error()) return } else if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, signedTx, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, signedTx, cliCtx.Indent) } } diff --git a/x/bank/client/rest/broadcast.go b/x/bank/client/rest/broadcast.go index 71696f12b..1d40c2820 100644 --- a/x/bank/client/rest/broadcast.go +++ b/x/bank/client/rest/broadcast.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" + "github.com/cosmos/cosmos-sdk/client/rest" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/x/auth" ) @@ -24,29 +24,29 @@ func BroadcastTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) ht txBytes, err := cliCtx.Codec.MarshalBinaryLengthPrefixed(m.Tx) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } res, err := cliCtx.BroadcastTx(txBytes) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } func unmarshalBodyOrReturnBadRequest(cliCtx context.CLIContext, w http.ResponseWriter, r *http.Request, m *broadcastBody) bool { body, err := ioutil.ReadAll(r.Body) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return false } err = cliCtx.Codec.UnmarshalJSON(body, m) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return false } return true diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index b4ef7d7a2..70aae05fc 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -3,8 +3,9 @@ package rest import ( "net/http" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" @@ -21,8 +22,8 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, } type sendReq struct { - BaseReq utils.BaseReq `json:"base_req"` - Amount sdk.Coins `json:"amount"` + BaseReq rest.BaseReq `json:"base_req"` + Amount sdk.Coins `json:"amount"` } var msgCdc = codec.New() @@ -39,12 +40,12 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC toAddr, err := sdk.AccAddressFromBech32(bech32Addr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } var req sendReq - err = utils.ReadRESTReq(w, r, cdc, &req) + err = rest.ReadRESTReq(w, r, cdc, &req) if err != nil { return } @@ -59,25 +60,25 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC // address. fromAddr, err := sdk.AccAddressFromBech32(req.BaseReq.From) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } msg := bankclient.CreateMsg(fromAddr, toAddr, req.Amount) - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } // derive the from account address and name from the Keybase fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) msg := bankclient.CreateMsg(cliCtx.GetFromAddress(), toAddr, req.Amount) - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index f00c187a1..e37f7870e 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -4,8 +4,9 @@ import ( "fmt" "net/http" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" @@ -53,7 +54,7 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec) } type postProposalReq struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` Title string `json:"title"` // Title of the proposal Description string `json:"description"` // Description of the proposal ProposalType string `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} @@ -62,13 +63,13 @@ type postProposalReq struct { } type depositReq struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` Depositor sdk.AccAddress `json:"depositor"` // Address of the depositor Amount sdk.Coins `json:"amount"` // Coins to add to the proposal's deposit } type voteReq struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` Voter sdk.AccAddress `json:"voter"` // address of the voter Option string `json:"option"` // option from OptionSet chosen by the voter } @@ -76,9 +77,9 @@ type voteReq struct { func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req postProposalReq - err := utils.ReadRESTReq(w, r, cdc, &req) + err := rest.ReadRESTReq(w, r, cdc, &req) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -89,7 +90,7 @@ func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han proposalType, err := gov.ProposalTypeFromString(govClientUtils.NormalizeProposalType(req.ProposalType)) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -97,16 +98,16 @@ func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han msg := gov.NewMsgSubmitProposal(req.Title, req.Description, proposalType, req.Proposer, req.InitialDeposit) err = msg.ValidateBasic() if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } @@ -117,17 +118,17 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF if len(strProposalID) == 0 { err := errors.New("proposalId required but not specified") - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) + proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } var req depositReq - err := utils.ReadRESTReq(w, r, cdc, &req) + err := rest.ReadRESTReq(w, r, cdc, &req) if err != nil { return } @@ -141,16 +142,16 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF msg := gov.NewMsgDeposit(req.Depositor, proposalID, req.Amount) err = msg.ValidateBasic() if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } @@ -161,17 +162,17 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc if len(strProposalID) == 0 { err := errors.New("proposalId required but not specified") - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) + proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } var req voteReq - err := utils.ReadRESTReq(w, r, cdc, &req) + err := rest.ReadRESTReq(w, r, cdc, &req) if err != nil { return } @@ -183,7 +184,7 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc voteOption, err := gov.VoteOptionFromString(govClientUtils.NormalizeVoteOption(req.Option)) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -191,16 +192,16 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc msg := gov.NewMsgVote(req.Voter, proposalID, voteOption) err = msg.ValidateBasic() if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } @@ -211,11 +212,11 @@ func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Hand res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/gov/%s/%s", gov.QueryParams, paramType), nil) if err != nil { - utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -226,11 +227,11 @@ func queryProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha if len(strProposalID) == 0 { err := errors.New("proposalId required but not specified") - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) + proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } @@ -239,17 +240,17 @@ func queryProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -258,7 +259,7 @@ func queryDepositsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha vars := mux.Vars(r) strProposalID := vars[RestProposalID] - proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) + proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } @@ -267,19 +268,19 @@ func queryDepositsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } var proposal gov.Proposal if err := cdc.UnmarshalJSON(res, &proposal); err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } @@ -293,11 +294,11 @@ func queryDepositsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha } if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -306,18 +307,18 @@ func queryProposerHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha vars := mux.Vars(r) strProposalID := vars[RestProposalID] - proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) + proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } res, err := gcutils.QueryProposerByTxQuery(cdc, cliCtx, proposalID) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -329,24 +330,24 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han if len(strProposalID) == 0 { err := errors.New("proposalId required but not specified") - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) + proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } if len(bechDepositorAddr) == 0 { err := errors.New("depositor address required but not specified") - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -354,13 +355,13 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/deposit", bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } @@ -373,25 +374,25 @@ func queryDepositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han if deposit.Empty() { bz, err := cdc.MarshalJSON(gov.NewQueryProposalParams(proposalID)) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err = cliCtx.QueryWithData("custom/gov/proposal", bz) if err != nil || len(res) == 0 { err := fmt.Errorf("proposalID %d does not exist", proposalID) - utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } res, err = gcutils.QueryDepositByTxQuery(cdc, cliCtx, params) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -403,24 +404,24 @@ func queryVoteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Handle if len(strProposalID) == 0 { err := errors.New("proposalId required but not specified") - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) + proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } if len(bechVoterAddr) == 0 { err := errors.New("voter address required but not specified") - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -428,13 +429,13 @@ func queryVoteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Handle bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/vote", bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } @@ -447,25 +448,25 @@ func queryVoteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Handle if vote.Empty() { bz, err := cdc.MarshalJSON(gov.NewQueryProposalParams(proposalID)) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err = cliCtx.QueryWithData("custom/gov/proposal", bz) if err != nil || len(res) == 0 { err := fmt.Errorf("proposalID %d does not exist", proposalID) - utils.WriteErrorResponse(w, http.StatusNotFound, err.Error()) + rest.WriteErrorResponse(w, http.StatusNotFound, err.Error()) return } res, err = gcutils.QueryVoteByTxQuery(cdc, cliCtx, params) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -477,11 +478,11 @@ func queryVotesOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) if len(strProposalID) == 0 { err := errors.New("proposalId required but not specified") - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) + proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } @@ -490,19 +491,19 @@ func queryVotesOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/proposal", bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } var proposal gov.Proposal if err := cdc.UnmarshalJSON(res, &proposal); err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } @@ -516,11 +517,11 @@ func queryVotesOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) } if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -537,7 +538,7 @@ func queryProposalsWithParameterFn(cdc *codec.Codec, cliCtx context.CLIContext) if len(bechVoterAddr) != 0 { voterAddr, err := sdk.AccAddressFromBech32(bechVoterAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } params.Voter = voterAddr @@ -546,7 +547,7 @@ func queryProposalsWithParameterFn(cdc *codec.Codec, cliCtx context.CLIContext) if len(bechDepositorAddr) != 0 { depositorAddr, err := sdk.AccAddressFromBech32(bechDepositorAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } params.Depositor = depositorAddr @@ -555,13 +556,13 @@ func queryProposalsWithParameterFn(cdc *codec.Codec, cliCtx context.CLIContext) if len(strProposalStatus) != 0 { proposalStatus, err := gov.ProposalStatusFromString(govClientUtils.NormalizeProposalStatus(strProposalStatus)) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } params.ProposalStatus = proposalStatus } if len(strNumLimit) != 0 { - numLimit, ok := utils.ParseUint64OrReturnBadRequest(w, strNumLimit) + numLimit, ok := rest.ParseUint64OrReturnBadRequest(w, strNumLimit) if !ok { return } @@ -570,17 +571,17 @@ func queryProposalsWithParameterFn(cdc *codec.Codec, cliCtx context.CLIContext) bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/proposals", bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -592,11 +593,11 @@ func queryTallyOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) if len(strProposalID) == 0 { err := errors.New("proposalId required but not specified") - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } - proposalID, ok := utils.ParseUint64OrReturnBadRequest(w, strProposalID) + proposalID, ok := rest.ParseUint64OrReturnBadRequest(w, strProposalID) if !ok { return } @@ -605,16 +606,16 @@ func queryTallyOnProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/gov/tally", bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index e732145e2..fd35764b0 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -3,8 +3,9 @@ package rest import ( "net/http" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" @@ -19,8 +20,8 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, } type transferReq struct { - BaseReq utils.BaseReq `json:"base_req"` - Amount sdk.Coins `json:"amount"` + BaseReq rest.BaseReq `json:"base_req"` + Amount sdk.Coins `json:"amount"` } // TransferRequestHandler - http request handler to transfer coins to a address @@ -33,12 +34,12 @@ func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. to, err := sdk.AccAddressFromBech32(bech32Addr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } var req transferReq - err = utils.ReadRESTReq(w, r, cdc, &req) + err = rest.ReadRESTReq(w, r, cdc, &req) if err != nil { return } @@ -55,7 +56,7 @@ func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. // address. addr, err := sdk.AccAddressFromBech32(req.BaseReq.From) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -66,10 +67,10 @@ func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. msg := ibc.IBCTransferMsg{IBCPacket: packet} if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } diff --git a/x/slashing/client/rest/query.go b/x/slashing/client/rest/query.go index 59c6493c2..812e9527f 100644 --- a/x/slashing/client/rest/query.go +++ b/x/slashing/client/rest/query.go @@ -4,10 +4,11 @@ import ( "fmt" "net/http" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/gorilla/mux" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/slashing" @@ -33,7 +34,7 @@ func signingInfoHandlerFn(cliCtx context.CLIContext, storeName string, cdc *code pk, err := sdk.GetConsPubKeyBech32(vars["validatorPubKey"]) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -41,7 +42,7 @@ func signingInfoHandlerFn(cliCtx context.CLIContext, storeName string, cdc *code res, err := cliCtx.QueryStore(key, storeName) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } @@ -54,11 +55,11 @@ func signingInfoHandlerFn(cliCtx context.CLIContext, storeName string, cdc *code err = cdc.UnmarshalBinaryLengthPrefixed(res, &signingInfo) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, signingInfo, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, signingInfo, cliCtx.Indent) } } @@ -68,10 +69,10 @@ func queryParamsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Hand res, err := cliCtx.QueryWithData(route, nil) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 672928f3f..edbfaec92 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -4,8 +4,9 @@ import ( "bytes" "net/http" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" @@ -23,7 +24,7 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec // Unjail TX body type UnjailReq struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` } func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { @@ -33,7 +34,7 @@ func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CL bech32validator := vars["validatorAddr"] var req UnjailReq - err := utils.ReadRESTReq(w, r, cdc, &req) + err := rest.ReadRESTReq(w, r, cdc, &req) if err != nil { return } @@ -45,36 +46,36 @@ func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CL valAddr, err := sdk.ValAddressFromBech32(bech32validator) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } msg := slashing.NewMsgUnjail(valAddr) err = msg.ValidateBasic() if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } // derive the from account address and name from the Keybase fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) if !bytes.Equal(cliCtx.GetFromAddress(), valAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "must use own validator address") + rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own validator address") return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } diff --git a/x/staking/client/rest/query.go b/x/staking/client/rest/query.go index c51f9e7ad..2ffefbda1 100644 --- a/x/staking/client/rest/query.go +++ b/x/staking/client/rest/query.go @@ -4,9 +4,10 @@ import ( "net/http" "strings" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking" @@ -122,13 +123,13 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han _, err := sdk.AccAddressFromBech32(delegatorAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } node, err := cliCtx.GetNode() if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } @@ -170,17 +171,17 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han for _, action := range actions { foundTxs, errQuery := queryTxs(node, cliCtx, cdc, action, delegatorAddr) if errQuery != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) } txs = append(txs, foundTxs...) } res, err := cdc.MarshalJSON(txs) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -201,7 +202,7 @@ func redelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Ha if len(bechDelegatorAddr) != 0 { delegatorAddr, err := sdk.AccAddressFromBech32(bechDelegatorAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } params.DelegatorAddr = delegatorAddr @@ -210,7 +211,7 @@ func redelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Ha if len(bechSrcValidatorAddr) != 0 { srcValidatorAddr, err := sdk.ValAddressFromBech32(bechSrcValidatorAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } params.SrcValidatorAddr = srcValidatorAddr @@ -219,7 +220,7 @@ func redelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Ha if len(bechDstValidatorAddr) != 0 { dstValidatorAddr, err := sdk.ValAddressFromBech32(bechDstValidatorAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } params.DstValidatorAddr = dstValidatorAddr @@ -227,16 +228,16 @@ func redelegationsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Ha bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData("custom/staking/redelegations", bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -260,10 +261,10 @@ func validatorsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Handl return func(w http.ResponseWriter, r *http.Request) { res, err := cliCtx.QueryWithData("custom/staking/validators", nil) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -287,10 +288,10 @@ func poolHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc return func(w http.ResponseWriter, r *http.Request) { res, err := cliCtx.QueryWithData("custom/staking/pool", nil) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -299,9 +300,9 @@ func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFu return func(w http.ResponseWriter, r *http.Request) { res, err := cliCtx.QueryWithData("custom/staking/parameters", nil) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } diff --git a/x/staking/client/rest/tx.go b/x/staking/client/rest/tx.go index 1710d340a..397e61a73 100644 --- a/x/staking/client/rest/tx.go +++ b/x/staking/client/rest/tx.go @@ -4,8 +4,9 @@ import ( "bytes" "net/http" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" @@ -31,14 +32,14 @@ func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec type ( msgDelegationsInput struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32 Delegation sdk.Coin `json:"delegation"` } msgBeginRedelegateInput struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 ValidatorSrcAddr sdk.ValAddress `json:"validator_src_addr"` // in bech32 ValidatorDstAddr sdk.ValAddress `json:"validator_dst_addr"` // in bech32 @@ -46,7 +47,7 @@ type ( } msgUndelegateInput struct { - BaseReq utils.BaseReq `json:"base_req"` + BaseReq rest.BaseReq `json:"base_req"` DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 ValidatorAddr sdk.ValAddress `json:"validator_addr"` // in bech32 SharesAmount sdk.Dec `json:"shares"` @@ -57,9 +58,9 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. return func(w http.ResponseWriter, r *http.Request) { var req msgDelegationsInput - err := utils.ReadRESTReq(w, r, cdc, &req) + err := rest.ReadRESTReq(w, r, cdc, &req) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -71,30 +72,30 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. msg := staking.NewMsgDelegate(req.DelegatorAddr, req.ValidatorAddr, req.Delegation) err = msg.ValidateBasic() if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } // derive the from account address and name from the Keybase fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) if !bytes.Equal(cliCtx.GetFromAddress(), req.DelegatorAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } @@ -102,9 +103,9 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex return func(w http.ResponseWriter, r *http.Request) { var req msgBeginRedelegateInput - err := utils.ReadRESTReq(w, r, cdc, &req) + err := rest.ReadRESTReq(w, r, cdc, &req) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -116,30 +117,30 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex msg := staking.NewMsgBeginRedelegate(req.DelegatorAddr, req.ValidatorSrcAddr, req.ValidatorDstAddr, req.SharesAmount) err = msg.ValidateBasic() if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } // derive the from account address and name from the Keybase fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) if !bytes.Equal(cliCtx.GetFromAddress(), req.DelegatorAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } @@ -147,9 +148,9 @@ func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx return func(w http.ResponseWriter, r *http.Request) { var req msgUndelegateInput - err := utils.ReadRESTReq(w, r, cdc, &req) + err := rest.ReadRESTReq(w, r, cdc, &req) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -161,29 +162,29 @@ func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx msg := staking.NewMsgUndelegate(req.DelegatorAddr, req.ValidatorAddr, req.SharesAmount) err = msg.ValidateBasic() if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } if req.BaseReq.GenerateOnly { - utils.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } // derive the from account address and name from the Keybase fromAddress, fromName, err := context.GetFromFields(req.BaseReq.From) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) if !bytes.Equal(cliCtx.GetFromAddress(), req.DelegatorAddr) { - utils.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") + rest.WriteErrorResponse(w, http.StatusUnauthorized, "must use own delegator address") return } - utils.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } } diff --git a/x/staking/client/rest/utils.go b/x/staking/client/rest/utils.go index 2a95776e3..c9e2ed9d9 100644 --- a/x/staking/client/rest/utils.go +++ b/x/staking/client/rest/utils.go @@ -4,11 +4,12 @@ import ( "fmt" "net/http" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/gorilla/mux" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/tx" - "github.com/cosmos/cosmos-sdk/client/utils" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking" @@ -59,17 +60,17 @@ func queryRedelegations(cliCtx context.CLIContext, cdc *codec.Codec, endpoint st delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } srcValidatorAddr, err := sdk.ValAddressFromBech32(bech32srcValidator) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } dstValidatorAddr, err := sdk.ValAddressFromBech32(bech32dstValidator) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -81,16 +82,16 @@ func queryRedelegations(cliCtx context.CLIContext, cdc *codec.Codec, endpoint st bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData(endpoint, bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -103,7 +104,7 @@ func queryBonds(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string) ht delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) validatorAddr, err := sdk.ValAddressFromBech32(bech32validator) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -111,16 +112,16 @@ func queryBonds(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string) ht bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData(endpoint, bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -131,7 +132,7 @@ func queryDelegator(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string delegatorAddr, err := sdk.AccAddressFromBech32(bech32delegator) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -139,16 +140,16 @@ func queryDelegator(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData(endpoint, bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } @@ -159,7 +160,7 @@ func queryValidator(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string validatorAddr, err := sdk.ValAddressFromBech32(bech32validatorAddr) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -167,15 +168,15 @@ func queryValidator(cliCtx context.CLIContext, cdc *codec.Codec, endpoint string bz, err := cdc.MarshalJSON(params) if err != nil { - utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } res, err := cliCtx.QueryWithData(endpoint, bz) if err != nil { - utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return } - utils.PostProcessResponse(w, cdc, res, cliCtx.Indent) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) } } From c766993c8330d6996df8b2185584d8a1ded18fb6 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 4 Feb 2019 08:42:48 -0800 Subject: [PATCH 43/99] Merge PR #3454: Add --jail-whitelist to gaiad export --- PENDING.md | 3 ++- cmd/gaia/app/app_test.go | 2 +- cmd/gaia/app/export.go | 28 +++++++++++++++++++++++++--- cmd/gaia/app/sim_test.go | 4 ++-- cmd/gaia/cmd/gaiad/main.go | 6 +++--- server/constructors.go | 2 +- server/export.go | 5 ++++- 7 files changed, 38 insertions(+), 12 deletions(-) diff --git a/PENDING.md b/PENDING.md index 3aef0023f..73e2f4ce3 100644 --- a/PENDING.md +++ b/PENDING.md @@ -58,6 +58,7 @@ IMPROVEMENTS * [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account genesis validation checks to `GaiaValidateGenesisState`. * [\#3420](https://github.com/cosmos/cosmos-sdk/issues/3420) Added maximum length to governance proposal descriptions and titles + * [\#3454](https://github.com/cosmos/cosmos-sdk/pull/3454) Add `--jail-whitelist` to `gaiad export` to enable testing of complex exports * [\#3424](https://github.com/cosmos/cosmos-sdk/issues/3424) Allow generation of gentxs with empty memo field. * SDK @@ -79,7 +80,7 @@ BUG FIXES - [\#3419](https://github.com/cosmos/cosmos-sdk/pull/3419) Fix `q distr slashes` panic - [\#3453](https://github.com/cosmos/cosmos-sdk/pull/3453) The `rest-server` command didn't respect persistent flags such as `--chain-id` and `--trust-node` if they were passed on the command line. - + * Gaia * SDK diff --git a/cmd/gaia/app/app_test.go b/cmd/gaia/app/app_test.go index 81f57abd6..0ee1596ea 100644 --- a/cmd/gaia/app/app_test.go +++ b/cmd/gaia/app/app_test.go @@ -58,6 +58,6 @@ func TestGaiadExport(t *testing.T) { // Making a new app object with the db, so that initchain hasn't been called newGapp := NewGaiaApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true) - _, _, err := newGapp.ExportAppStateAndValidators(false) + _, _, err := newGapp.ExportAppStateAndValidators(false, []string{}) require.NoError(t, err, "ExportAppStateAndValidators should not have an error") } diff --git a/cmd/gaia/app/export.go b/cmd/gaia/app/export.go index edf319cfa..efc9c2051 100644 --- a/cmd/gaia/app/export.go +++ b/cmd/gaia/app/export.go @@ -2,6 +2,7 @@ package app import ( "encoding/json" + "log" abci "github.com/tendermint/tendermint/abci/types" tmtypes "github.com/tendermint/tendermint/types" @@ -18,14 +19,14 @@ import ( ) // export the state of gaia for a genesis file -func (app *GaiaApp) ExportAppStateAndValidators(forZeroHeight bool) ( +func (app *GaiaApp) ExportAppStateAndValidators(forZeroHeight bool, jailWhiteList []string) ( appState json.RawMessage, validators []tmtypes.GenesisValidator, err error) { // as if they could withdraw from the start of the next block ctx := app.NewContext(true, abci.Header{Height: app.LastBlockHeight()}) if forZeroHeight { - app.prepForZeroHeightGenesis(ctx) + app.prepForZeroHeightGenesis(ctx, jailWhiteList) } // iterate to get the accounts @@ -56,7 +57,23 @@ func (app *GaiaApp) ExportAppStateAndValidators(forZeroHeight bool) ( } // prepare for fresh start at zero height -func (app *GaiaApp) prepForZeroHeightGenesis(ctx sdk.Context) { +func (app *GaiaApp) prepForZeroHeightGenesis(ctx sdk.Context, jailWhiteList []string) { + applyWhiteList := false + + //Check if there is a whitelist + if len(jailWhiteList) > 0 { + applyWhiteList = true + } + + whiteListMap := make(map[string]bool) + + for _, addr := range jailWhiteList { + _, err := sdk.ValAddressFromBech32(addr) + if err != nil { + log.Fatal(err) + } + whiteListMap[addr] = true + } /* Just to be safe, assert the invariants on current state. */ app.assertRuntimeInvariantsOnContext(ctx) @@ -136,6 +153,9 @@ func (app *GaiaApp) prepForZeroHeightGenesis(ctx sdk.Context) { validator.BondHeight = 0 validator.UnbondingHeight = 0 valConsAddrs = append(valConsAddrs, validator.ConsAddress()) + if applyWhiteList && !whiteListMap[addr.String()] { + validator.Jailed = true + } app.stakingKeeper.SetValidator(ctx, validator) counter++ @@ -143,6 +163,8 @@ func (app *GaiaApp) prepForZeroHeightGenesis(ctx sdk.Context) { iter.Close() + _ = app.stakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + /* Handle slashing state. */ // reset start height on signing infos diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index bbb5a2b4e..dae525342 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -416,7 +416,7 @@ func TestGaiaImportExport(t *testing.T) { fmt.Printf("Exporting genesis...\n") - appState, _, err := app.ExportAppStateAndValidators(false) + appState, _, err := app.ExportAppStateAndValidators(false, []string{}) if err != nil { panic(err) } @@ -520,7 +520,7 @@ func TestGaiaSimulationAfterImport(t *testing.T) { fmt.Printf("Exporting genesis...\n") - appState, _, err := app.ExportAppStateAndValidators(true) + appState, _, err := app.ExportAppStateAndValidators(true, []string{}) if err != nil { panic(err) } diff --git a/cmd/gaia/cmd/gaiad/main.go b/cmd/gaia/cmd/gaiad/main.go index 00ebb9667..d6d88e0df 100644 --- a/cmd/gaia/cmd/gaiad/main.go +++ b/cmd/gaia/cmd/gaiad/main.go @@ -66,7 +66,7 @@ func newApp(logger log.Logger, db dbm.DB, traceStore io.Writer) abci.Application } func exportAppStateAndTMValidators( - logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, + logger log.Logger, db dbm.DB, traceStore io.Writer, height int64, forZeroHeight bool, jailWhiteList []string, ) (json.RawMessage, []tmtypes.GenesisValidator, error) { if height != -1 { gApp := app.NewGaiaApp(logger, db, traceStore, false) @@ -74,8 +74,8 @@ func exportAppStateAndTMValidators( if err != nil { return nil, nil, err } - return gApp.ExportAppStateAndValidators(forZeroHeight) + return gApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) } gApp := app.NewGaiaApp(logger, db, traceStore, true) - return gApp.ExportAppStateAndValidators(forZeroHeight) + return gApp.ExportAppStateAndValidators(forZeroHeight, jailWhiteList) } diff --git a/server/constructors.go b/server/constructors.go index 909bda8e2..5a51937a9 100644 --- a/server/constructors.go +++ b/server/constructors.go @@ -19,7 +19,7 @@ type ( // AppExporter is a function that dumps all app state to // JSON-serializable structure and returns the current validator set. - AppExporter func(log.Logger, dbm.DB, io.Writer, int64, bool) (json.RawMessage, []tmtypes.GenesisValidator, error) + AppExporter func(log.Logger, dbm.DB, io.Writer, int64, bool, []string) (json.RawMessage, []tmtypes.GenesisValidator, error) ) func openDB(rootDir string) (dbm.DB, error) { diff --git a/server/export.go b/server/export.go index 16187fa66..1637b1dd2 100644 --- a/server/export.go +++ b/server/export.go @@ -18,6 +18,7 @@ import ( const ( flagHeight = "height" flagForZeroHeight = "for-zero-height" + flagJailWhitelist = "jail-whitelist" ) // ExportCmd dumps app state to JSON. @@ -54,7 +55,8 @@ func ExportCmd(ctx *Context, cdc *codec.Codec, appExporter AppExporter) *cobra.C } height := viper.GetInt64(flagHeight) forZeroHeight := viper.GetBool(flagForZeroHeight) - appState, validators, err := appExporter(ctx.Logger, db, traceWriter, height, forZeroHeight) + jailWhiteList := viper.GetStringSlice(flagJailWhitelist) + appState, validators, err := appExporter(ctx.Logger, db, traceWriter, height, forZeroHeight, jailWhiteList) if err != nil { return errors.Errorf("error exporting state: %v\n", err) } @@ -78,6 +80,7 @@ func ExportCmd(ctx *Context, cdc *codec.Codec, appExporter AppExporter) *cobra.C } cmd.Flags().Int64(flagHeight, -1, "Export state from a particular height (-1 means latest height)") cmd.Flags().Bool(flagForZeroHeight, false, "Export state to start at height zero (perform preproccessing)") + cmd.Flags().StringSlice(flagJailWhitelist, []string{}, "List of validators to not jail state export") return cmd } From 9fb774832cfda6d8f70f222aef6f04b172694462 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 13:08:11 -0800 Subject: [PATCH 44/99] Merge PR #3492: disable TLS flag by default --- client/flags.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/flags.go b/client/flags.go index c76bbb3c7..31feacf6c 100644 --- a/client/flags.go +++ b/client/flags.go @@ -105,7 +105,7 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { func RegisterRestServerFlags(cmd *cobra.Command) *cobra.Command { cmd = GetCommands(cmd)[0] cmd.Flags().String(FlagListenAddr, "tcp://localhost:1317", "The address for the server to listen on") - cmd.Flags().Bool(FlagTLS, true, "Enable SSL/TLS layer") + cmd.Flags().Bool(FlagTLS, false, "Enable SSL/TLS layer") cmd.Flags().String(FlagSSLHosts, "", "Comma-separated hostnames and IPs to generate a certificate for") cmd.Flags().String(FlagSSLCertFile, "", "Path to a SSL certificate file. If not supplied, a self-signed certificate will be generated.") cmd.Flags().String(FlagSSLKeyFile, "", "Path to a key file; ignored if a certificate file is not supplied.") From 3780b8429081635f861d66a06debdd274adea33a Mon Sep 17 00:00:00 2001 From: Zaki Manian Date: Mon, 4 Feb 2019 13:10:06 -0800 Subject: [PATCH 45/99] Merge PR #3486: Checking Vesting Coins with AmountOf --- PENDING.md | 4 +- x/auth/account.go | 58 +++------------------------ x/auth/account_test.go | 91 ++++++++++++++++++++++-------------------- 3 files changed, 56 insertions(+), 97 deletions(-) diff --git a/PENDING.md b/PENDING.md index 73e2f4ce3..545d74380 100644 --- a/PENDING.md +++ b/PENDING.md @@ -80,8 +80,10 @@ BUG FIXES - [\#3419](https://github.com/cosmos/cosmos-sdk/pull/3419) Fix `q distr slashes` panic - [\#3453](https://github.com/cosmos/cosmos-sdk/pull/3453) The `rest-server` command didn't respect persistent flags such as `--chain-id` and `--trust-node` if they were passed on the command line. - + * Gaia + * [\#3486](https://github.com/cosmos/cosmos-sdk/pull/3486) Use AmountOf in + vesting accounts instead of zipping/aligning denominations. * SDK diff --git a/x/auth/account.go b/x/auth/account.go index 2bf2fce0c..bb4e98684 100644 --- a/x/auth/account.go +++ b/x/auth/account.go @@ -221,27 +221,11 @@ func (bva BaseVestingAccount) spendableCoins(vestingCoins sdk.Coins) sdk.Coins { var spendableCoins sdk.Coins bc := bva.GetCoins() - j, k := 0, 0 for _, coin := range bc { // zip/lineup all coins by their denomination to provide O(n) time - for j < len(vestingCoins) && vestingCoins[j].Denom != coin.Denom { - j++ - } - for k < len(bva.DelegatedVesting) && bva.DelegatedVesting[k].Denom != coin.Denom { - k++ - } - baseAmt := coin.Amount - - vestingAmt := sdk.ZeroInt() - if len(vestingCoins) > 0 { - vestingAmt = vestingCoins[j].Amount - } - - delVestingAmt := sdk.ZeroInt() - if len(bva.DelegatedVesting) > 0 { - delVestingAmt = bva.DelegatedVesting[k].Amount - } + vestingAmt := vestingCoins.AmountOf(coin.Denom) + delVestingAmt := bva.DelegatedVesting.AmountOf(coin.Denom) // compute min((BC + DV) - V, BC) per the specification min := sdk.MinInt(baseAmt.Add(delVestingAmt).Sub(vestingAmt), baseAmt) @@ -264,33 +248,12 @@ func (bva BaseVestingAccount) spendableCoins(vestingCoins sdk.Coins) sdk.Coins { func (bva *BaseVestingAccount) trackDelegation(vestingCoins, amount sdk.Coins) { bc := bva.GetCoins() - i, j, k := 0, 0, 0 for _, coin := range amount { // zip/lineup all coins by their denomination to provide O(n) time - for i < len(bc) && bc[i].Denom != coin.Denom { - i++ - } - for j < len(vestingCoins) && vestingCoins[j].Denom != coin.Denom { - j++ - } - for k < len(bva.DelegatedVesting) && bva.DelegatedVesting[k].Denom != coin.Denom { - k++ - } - baseAmt := sdk.ZeroInt() - if len(bc) > 0 { - baseAmt = bc[i].Amount - } - - vestingAmt := sdk.ZeroInt() - if len(vestingCoins) > 0 { - vestingAmt = vestingCoins[j].Amount - } - - delVestingAmt := sdk.ZeroInt() - if len(bva.DelegatedVesting) > 0 { - delVestingAmt = bva.DelegatedVesting[k].Amount - } + baseAmt := bc.AmountOf(coin.Denom) + vestingAmt := vestingCoins.AmountOf(coin.Denom) + delVestingAmt := bva.DelegatedVesting.AmountOf(coin.Denom) // Panic if the delegation amount is zero or if the base coins does not // exceed the desired delegation amount. @@ -325,21 +288,12 @@ func (bva *BaseVestingAccount) trackDelegation(vestingCoins, amount sdk.Coins) { // // CONTRACT: The account's coins and undelegation coins must be sorted. func (bva *BaseVestingAccount) TrackUndelegation(amount sdk.Coins) { - i := 0 for _, coin := range amount { // panic if the undelegation amount is zero if coin.Amount.IsZero() { panic("undelegation attempt with zero coins") } - - for i < len(bva.DelegatedFree) && bva.DelegatedFree[i].Denom != coin.Denom { - i++ - } - - delegatedFree := sdk.ZeroInt() - if len(bva.DelegatedFree) > 0 { - delegatedFree = bva.DelegatedFree[i].Amount - } + delegatedFree := bva.DelegatedFree.AmountOf(coin.Denom) // compute x and y per the specification, where: // X := min(DF, D) diff --git a/x/auth/account_test.go b/x/auth/account_test.go index 6a7753a59..7d4fb5346 100644 --- a/x/auth/account_test.go +++ b/x/auth/account_test.go @@ -12,7 +12,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -var testDenom = "testdenom" +var ( + stakeDenom = "stake" + feeDenom = "fee" +) func TestBaseAddressPubKey(t *testing.T) { _, pub1, addr1 := keyPubAddr() @@ -107,7 +110,7 @@ func TestGetVestedCoinsContVestingAcc(t *testing.T) { endTime := now.Add(24 * time.Hour) _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)} + origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := NewBaseAccountWithAddress(addr) bacc.SetCoins(origCoins) cva := NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) @@ -122,7 +125,7 @@ func TestGetVestedCoinsContVestingAcc(t *testing.T) { // require 50% of coins vested vestedCoins = cva.GetVestedCoins(now.Add(12 * time.Hour)) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, vestedCoins) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestedCoins) // require 100% of coins vested vestedCoins = cva.GetVestedCoins(now.Add(48 * time.Hour)) @@ -134,7 +137,7 @@ func TestGetVestingCoinsContVestingAcc(t *testing.T) { endTime := now.Add(24 * time.Hour) _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)} + origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := NewBaseAccountWithAddress(addr) bacc.SetCoins(origCoins) cva := NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) @@ -149,7 +152,7 @@ func TestGetVestingCoinsContVestingAcc(t *testing.T) { // require 50% of coins vesting vestingCoins = cva.GetVestingCoins(now.Add(12 * time.Hour)) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, vestingCoins) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, vestingCoins) } func TestSpendableCoinsContVestingAcc(t *testing.T) { @@ -157,7 +160,7 @@ func TestSpendableCoinsContVestingAcc(t *testing.T) { endTime := now.Add(24 * time.Hour) _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)} + origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := NewBaseAccountWithAddress(addr) bacc.SetCoins(origCoins) cva := NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) @@ -174,15 +177,15 @@ func TestSpendableCoinsContVestingAcc(t *testing.T) { // require that all vested coins (50%) are spendable spendableCoins = cva.SpendableCoins(now.Add(12 * time.Hour)) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, spendableCoins) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 50)}, spendableCoins) // receive some coins - recvAmt := sdk.Coins{sdk.NewInt64Coin(testDenom, 50)} + recvAmt := sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)} cva.SetCoins(cva.GetCoins().Plus(recvAmt)) // require that all vested coins (50%) are spendable plus any received spendableCoins = cva.SpendableCoins(now.Add(12 * time.Hour)) - require.Equal(t, origCoins, spendableCoins) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 500), sdk.NewInt64Coin(stakeDenom, 100)}, spendableCoins) // spend all spendable coins cva.SetCoins(cva.GetCoins().Minus(spendableCoins)) @@ -197,7 +200,7 @@ func TestTrackDelegationContVestingAcc(t *testing.T) { endTime := now.Add(24 * time.Hour) _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)} + origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := NewBaseAccountWithAddress(addr) bacc.SetCoins(origCoins) @@ -219,20 +222,20 @@ func TestTrackDelegationContVestingAcc(t *testing.T) { // require the ability to delegate all vesting coins (50%) and all vested coins (50%) bacc.SetCoins(origCoins) cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) - cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, cva.DelegatedVesting) + cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, cva.DelegatedVesting) require.Nil(t, cva.DelegatedFree) - cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, cva.DelegatedVesting) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, cva.DelegatedFree) - require.Nil(t, cva.GetCoins()) + cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, cva.DelegatedVesting) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, cva.DelegatedFree) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000)}, cva.GetCoins()) // require no modifications when delegation amount is zero or not enough funds bacc.SetCoins(origCoins) cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) require.Panics(t, func() { - cva.TrackDelegation(endTime, sdk.Coins{sdk.NewInt64Coin(testDenom, 1000000)}) + cva.TrackDelegation(endTime, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 1000000)}) }) require.Nil(t, cva.DelegatedVesting) require.Nil(t, cva.DelegatedFree) @@ -244,7 +247,7 @@ func TestTrackUndelegationContVestingAcc(t *testing.T) { endTime := now.Add(24 * time.Hour) _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)} + origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := NewBaseAccountWithAddress(addr) bacc.SetCoins(origCoins) @@ -271,7 +274,7 @@ func TestTrackUndelegationContVestingAcc(t *testing.T) { cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) require.Panics(t, func() { - cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 0)}) + cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 0)}) }) require.Nil(t, cva.DelegatedFree) require.Nil(t, cva.DelegatedVesting) @@ -279,20 +282,20 @@ func TestTrackUndelegationContVestingAcc(t *testing.T) { // vest 50% and delegate to two validators cva = NewContinuousVestingAccount(&bacc, now.Unix(), endTime.Unix()) - cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}) - cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}) + cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + cva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) // undelegate from one validator that got slashed 50% - cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}, cva.DelegatedFree) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}, cva.DelegatedVesting) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}, cva.GetCoins()) + cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}, cva.DelegatedFree) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}, cva.DelegatedVesting) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 25)}, cva.GetCoins()) // undelegate from the other validator that did not get slashed - cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}) + cva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) require.Nil(t, cva.DelegatedFree) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}, cva.DelegatedVesting) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 75)}, cva.GetCoins()) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}, cva.DelegatedVesting) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 75)}, cva.GetCoins()) } func TestGetVestedCoinsDelVestingAcc(t *testing.T) { @@ -300,7 +303,7 @@ func TestGetVestedCoinsDelVestingAcc(t *testing.T) { endTime := now.Add(24 * time.Hour) _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)} + origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := NewBaseAccountWithAddress(addr) bacc.SetCoins(origCoins) @@ -319,7 +322,7 @@ func TestGetVestingCoinsDelVestingAcc(t *testing.T) { endTime := now.Add(24 * time.Hour) _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)} + origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := NewBaseAccountWithAddress(addr) bacc.SetCoins(origCoins) @@ -338,7 +341,7 @@ func TestSpendableCoinsDelVestingAcc(t *testing.T) { endTime := now.Add(24 * time.Hour) _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)} + origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := NewBaseAccountWithAddress(addr) bacc.SetCoins(origCoins) @@ -358,7 +361,7 @@ func TestSpendableCoinsDelVestingAcc(t *testing.T) { require.Nil(t, spendableCoins) // receive some coins - recvAmt := sdk.Coins{sdk.NewInt64Coin(testDenom, 50)} + recvAmt := sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)} dva.SetCoins(dva.GetCoins().Plus(recvAmt)) // require that only received coins are spendable since the account is still @@ -379,7 +382,7 @@ func TestTrackDelegationDelVestingAcc(t *testing.T) { endTime := now.Add(24 * time.Hour) _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)} + origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := NewBaseAccountWithAddress(addr) bacc.SetCoins(origCoins) @@ -413,7 +416,7 @@ func TestTrackDelegationDelVestingAcc(t *testing.T) { dva = NewDelayedVestingAccount(&bacc, endTime.Unix()) require.Panics(t, func() { - dva.TrackDelegation(endTime, sdk.Coins{sdk.NewInt64Coin(testDenom, 1000000)}) + dva.TrackDelegation(endTime, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 1000000)}) }) require.Nil(t, dva.DelegatedVesting) require.Nil(t, dva.DelegatedFree) @@ -425,7 +428,7 @@ func TestTrackUndelegationDelVestingAcc(t *testing.T) { endTime := now.Add(24 * time.Hour) _, _, addr := keyPubAddr() - origCoins := sdk.Coins{sdk.NewInt64Coin(testDenom, 100)} + origCoins := sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 100)} bacc := NewBaseAccountWithAddress(addr) bacc.SetCoins(origCoins) @@ -452,7 +455,7 @@ func TestTrackUndelegationDelVestingAcc(t *testing.T) { dva = NewDelayedVestingAccount(&bacc, endTime.Unix()) require.Panics(t, func() { - dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 0)}) + dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 0)}) }) require.Nil(t, dva.DelegatedFree) require.Nil(t, dva.DelegatedVesting) @@ -461,19 +464,19 @@ func TestTrackUndelegationDelVestingAcc(t *testing.T) { // vest 50% and delegate to two validators bacc.SetCoins(origCoins) dva = NewDelayedVestingAccount(&bacc, endTime.Unix()) - dva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}) - dva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}) + dva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) + dva.TrackDelegation(now.Add(12*time.Hour), sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) // undelegate from one validator that got slashed 50% - dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}) + dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}) require.Nil(t, dva.DelegatedFree) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 75)}, dva.DelegatedVesting) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}, dva.GetCoins()) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 75)}, dva.DelegatedVesting) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 25)}, dva.GetCoins()) // undelegate from the other validator that did not get slashed - dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(testDenom, 50)}) + dva.TrackUndelegation(sdk.Coins{sdk.NewInt64Coin(stakeDenom, 50)}) require.Nil(t, dva.DelegatedFree) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 25)}, dva.DelegatedVesting) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(testDenom, 75)}, dva.GetCoins()) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakeDenom, 25)}, dva.DelegatedVesting) + require.Equal(t, sdk.Coins{sdk.NewInt64Coin(feeDenom, 1000), sdk.NewInt64Coin(stakeDenom, 75)}, dva.GetCoins()) } From 082295172ed4b405042072b9f755c18317274a21 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Mon, 4 Feb 2019 15:35:12 -0800 Subject: [PATCH 46/99] Merge PR #3447: Consume Gas Proportional to Tx Size --- PENDING.md | 2 + baseapp/baseapp.go | 7 +++- baseapp/baseapp_test.go | 8 ++-- baseapp/helpers.go | 4 +- client/flags.go | 6 +-- client/lcd/lcd_test.go | 8 +++- client/utils/utils.go | 14 ++++++- cmd/gaia/app/sim_test.go | 10 ++--- cmd/gaia/cli_test/cli_test.go | 4 +- docs/spec/auth/gas_fees.md | 8 ++-- x/auth/ante.go | 76 +++++++++++++++++++++++++---------- x/auth/ante_test.go | 4 +- x/auth/genesis.go | 4 +- x/auth/params.go | 21 +++++----- x/bank/app_test.go | 8 ++-- x/ibc/app_test.go | 8 ++-- x/mock/app.go | 13 ++++-- x/mock/app_test.go | 6 +-- x/mock/test_utils.go | 12 ++++-- x/slashing/app_test.go | 6 ++- x/staking/app_test.go | 10 ++--- 21 files changed, 154 insertions(+), 85 deletions(-) diff --git a/PENDING.md b/PENDING.md index 545d74380..9e3f1c1eb 100644 --- a/PENDING.md +++ b/PENDING.md @@ -58,6 +58,8 @@ IMPROVEMENTS * [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account genesis validation checks to `GaiaValidateGenesisState`. * [\#3420](https://github.com/cosmos/cosmos-sdk/issues/3420) Added maximum length to governance proposal descriptions and titles + * [\#3256](https://github.com/cosmos/cosmos-sdk/issues/3256) Add gas consumption + for tx size in the ante handler. * [\#3454](https://github.com/cosmos/cosmos-sdk/pull/3454) Add `--jail-whitelist` to `gaiad export` to enable testing of complex exports * [\#3424](https://github.com/cosmos/cosmos-sdk/issues/3424) Allow generation of gentxs with empty memo field. diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index ef5c692b9..6a6cc7960 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -390,7 +390,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc if err != nil { result = err.Result() } else { - result = app.Simulate(tx) + result = app.Simulate(txBytes, tx) } case "version": return abci.ResponseQuery{ @@ -718,7 +718,10 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk if r := recover(); r != nil { switch rType := r.(type) { case sdk.ErrorOutOfGas: - log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor) + log := fmt.Sprintf( + "out of gas in location: %v; gasWanted: %d, gasUsed: %d", + rType.Descriptor, gasWanted, ctx.GasMeter().GasConsumed(), + ) result = sdk.ErrOutOfGas(log).Result() default: log := fmt.Sprintf("recovered: %v\nstack:\n%v", r, string(debug.Stack())) diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 82ee0bd9c..378249106 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -655,20 +655,20 @@ func TestSimulateTx(t *testing.T) { app.BeginBlock(abci.RequestBeginBlock{}) tx := newTxCounter(count, count) + txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) + require.Nil(t, err) // simulate a message, check gas reported - result := app.Simulate(tx) + result := app.Simulate(txBytes, tx) require.True(t, result.IsOK(), result.Log) require.Equal(t, gasConsumed, result.GasUsed) // simulate again, same result - result = app.Simulate(tx) + result = app.Simulate(txBytes, tx) require.True(t, result.IsOK(), result.Log) require.Equal(t, gasConsumed, result.GasUsed) // simulate by calling Query with encoded tx - txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) - require.Nil(t, err) query := abci.RequestQuery{ Path: "/app/simulate", Data: txBytes, diff --git a/baseapp/helpers.go b/baseapp/helpers.go index 0ac99887a..fbc3df99d 100644 --- a/baseapp/helpers.go +++ b/baseapp/helpers.go @@ -14,8 +14,8 @@ func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) { } // nolint - full tx execution -func (app *BaseApp) Simulate(tx sdk.Tx) (result sdk.Result) { - return app.runTx(runTxModeSimulate, nil, tx) +func (app *BaseApp) Simulate(txBytes []byte, tx sdk.Tx) (result sdk.Result) { + return app.runTx(runTxModeSimulate, txBytes, tx) } // nolint diff --git a/client/flags.go b/client/flags.go index 31feacf6c..c87a8933c 100644 --- a/client/flags.go +++ b/client/flags.go @@ -11,9 +11,9 @@ import ( // nolint const ( - // DefaultGasAdjustment is applied to gas estimates to avoid tx - // execution failures due to state changes that might - // occur between the tx simulation and the actual run. + // DefaultGasAdjustment is applied to gas estimates to avoid tx execution + // failures due to state changes that might occur between the tx simulation + // and the actual run. DefaultGasAdjustment = 1.0 DefaultGasLimit = 200000 GasFlagAuto = "auto" diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index c531a0040..c29eb64a7 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -209,7 +209,9 @@ func TestCoinSend(t *testing.T) { require.Equal(t, http.StatusInternalServerError, res.StatusCode, body) // run simulation and test success with estimated gas - res, body, _ = doTransferWithGas(t, port, seed, name1, memo, pw, addr, "10000", 1.0, true, false, fees) + res, body, _ = doTransferWithGas( + t, port, seed, name1, memo, pw, addr, "10000", 1.0, true, false, fees, + ) require.Equal(t, http.StatusOK, res.StatusCode, body) var gasEstResp rest.GasEstimateResponse @@ -285,7 +287,9 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { acc := getAccount(t, port, addr) // simulate tx - res, body, _ := doTransferWithGas(t, port, seed, name1, memo, "", addr, client.GasFlagAuto, 1, true, false, fees) + res, body, _ := doTransferWithGas( + t, port, seed, name1, memo, "", addr, client.GasFlagAuto, 1.0, true, false, fees, + ) require.Equal(t, http.StatusOK, res.StatusCode, body) var gasEstResp rest.GasEstimateResponse diff --git a/client/utils/utils.go b/client/utils/utils.go index daad43156..b5ae87eab 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -19,6 +19,15 @@ import ( authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" ) +// GasEstimateResponse defines a response definition for tx gas estimation. +type GasEstimateResponse struct { + GasEstimate uint64 `json:"gas_estimate"` +} + +func (gr GasEstimateResponse) String() string { + return fmt.Sprintf("gas estimate: %d", gr.GasEstimate) +} + // CompleteAndBroadcastTxCLI implements a utility function that facilitates // sending a series of messages in a signed transaction given a TxBuilder and a // QueryContext. It ensures that the account exists, has a proper number and @@ -39,8 +48,11 @@ func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIConte if err != nil { return err } - fmt.Fprintf(os.Stderr, "estimated gas = %v\n", txBldr.GetGas()) + + gasEst := GasEstimateResponse{GasEstimate: txBldr.GetGas()} + fmt.Fprintf(os.Stderr, gasEst.String()) } + if cliCtx.Simulate { return nil } diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index dae525342..f7d645a61 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -79,7 +79,7 @@ func appStateFromGenesisFileFn(r *rand.Rand, accs []simulation.Account, genesisT privKey := secp256k1.GenPrivKeySecp256k1(privkeySeed) newAccs = append(newAccs, simulation.Account{privKey, privKey.PubKey(), acc.Address}) } - return json.RawMessage(genesis.AppState), newAccs, genesis.ChainID + return genesis.AppState, newAccs, genesis.ChainID } func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimestamp time.Time) (json.RawMessage, []simulation.Account, string) { @@ -138,11 +138,11 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest authGenesis := auth.GenesisState{ Params: auth.Params{ - MemoCostPerByte: uint64(r.Intn(10) + 1), - MaxMemoCharacters: uint64(r.Intn(200-100) + 100), + MaxMemoCharacters: uint64(randIntBetween(r, 100, 200)), TxSigLimit: uint64(r.Intn(7) + 1), - SigVerifyCostED25519: uint64(r.Intn(1000-500) + 500), - SigVerifyCostSecp256k1: uint64(r.Intn(1000-500) + 500), + TxSizeCostPerByte: uint64(randIntBetween(r, 5, 15)), + SigVerifyCostED25519: uint64(randIntBetween(r, 500, 1000)), + SigVerifyCostSecp256k1: uint64(randIntBetween(r, 500, 1000)), }, } fmt.Printf("Selected randomly generated auth parameters:\n\t%+v\n", authGenesis) diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 6e2e60536..0b6d5790d 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -279,7 +279,7 @@ func TestGaiaCLIGasAuto(t *testing.T) { }{} err := cdc.UnmarshalJSON([]byte(stdout), &sendResp) require.Nil(t, err) - require.Equal(t, sendResp.Response.GasWanted, sendResp.Response.GasUsed) + require.True(t, sendResp.Response.GasWanted >= sendResp.Response.GasUsed) tests.WaitForNextNBlocksTM(1, f.Port) // Check state has changed accordingly @@ -690,7 +690,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { // Unmarshal the response and ensure that gas was properly used require.Nil(t, app.MakeCodec().UnmarshalJSON([]byte(stdout), &result)) - require.Equal(t, msg.Fee.Gas, uint64(result.Response.GasUsed)) + require.True(t, msg.Fee.Gas >= uint64(result.Response.GasUsed)) require.Equal(t, msg.Fee.Gas, uint64(result.Response.GasWanted)) tests.WaitForNextNBlocksTM(1, f.Port) diff --git a/docs/spec/auth/gas_fees.md b/docs/spec/auth/gas_fees.md index fbe210394..533ccb002 100644 --- a/docs/spec/auth/gas_fees.md +++ b/docs/spec/auth/gas_fees.md @@ -7,9 +7,11 @@ general purpose censorship of transactions of little economic value. Fees are best suited as an anti-spam mechanism where validators are disinterested in the use of the network and identities of users. -Fees are determined by the gas limits and gas prices transactions provide. -Operators should set minimum gas prices when starting their nodes. They must set -the unit costs of gas in each token denomination they wish to support: +Fees are determined by the gas limits and gas prices transactions provide, where +`fees = ceil(gasLimit * gasPrices)`. Txs incur gas costs for all state reads/writes, +signature verification, as well as costs proportional to the tx size. Operators +should set minimum gas prices when starting their nodes. They must set the unit +costs of gas in each token denomination they wish to support: `gaiad start ... --minimum-gas-prices=0.00001steak,0.05photinos` diff --git a/x/auth/ante.go b/x/auth/ante.go index f82b7fc65..bc39f5323 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -15,6 +15,17 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) +var ( + // simulation signature values used to estimate gas consumption + simSecp256k1Pubkey secp256k1.PubKeySecp256k1 + simSecp256k1Sig [64]byte +) + +func init() { + bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") + copy(simSecp256k1Pubkey[:], bz) +} + // NewAnteHandler returns an AnteHandler that checks and increments sequence // numbers, checks signatures & account numbers, and deducts fees from the first // signer. @@ -54,8 +65,12 @@ func NewAnteHandler(ak AccountKeeper, fck FeeCollectionKeeper) sdk.AnteHandler { if r := recover(); r != nil { switch rType := r.(type) { case sdk.ErrorOutOfGas: - log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor) + log := fmt.Sprintf( + "out of gas in location: %v; gasWanted: %d, gasUsed: %d", + rType.Descriptor, stdTx.Fee.Gas, newCtx.GasMeter().GasConsumed(), + ) res = sdk.ErrOutOfGas(log).Result() + res.GasWanted = stdTx.Fee.Gas res.GasUsed = newCtx.GasMeter().GasConsumed() abort = true @@ -69,7 +84,9 @@ func NewAnteHandler(ak AccountKeeper, fck FeeCollectionKeeper) sdk.AnteHandler { return newCtx, err.Result(), true } - if res := ValidateMemo(newCtx.GasMeter(), stdTx, params); !res.IsOK() { + newCtx.GasMeter().ConsumeGas(params.TxSizeCostPerByte*sdk.Gas(len(newCtx.TxBytes())), "txSize") + + if res := ValidateMemo(stdTx, params); !res.IsOK() { return newCtx, res, true } @@ -131,9 +148,8 @@ func GetSignerAcc(ctx sdk.Context, ak AccountKeeper, addr sdk.AccAddress) (Accou return nil, sdk.ErrUnknownAddress(addr.String()).Result() } -// ValidateMemo validates the memo and if successful consumes gas for -// verification. -func ValidateMemo(gasMeter sdk.GasMeter, stdTx StdTx, params Params) sdk.Result { +// ValidateMemo validates the memo size. +func ValidateMemo(stdTx StdTx, params Params) sdk.Result { memoLength := len(stdTx.GetMemo()) if uint64(memoLength) > params.MaxMemoCharacters { return sdk.ErrMemoTooLarge( @@ -144,7 +160,6 @@ func ValidateMemo(gasMeter sdk.GasMeter, stdTx StdTx, params Params) sdk.Result ).Result() } - gasMeter.ConsumeGas(params.MemoCostPerByte*sdk.Gas(memoLength), "memo") return sdk.Result{} } @@ -164,7 +179,15 @@ func processSig( return nil, sdk.ErrInternal("setting PubKey on signer's account").Result() } - consumeSignatureVerificationGas(ctx.GasMeter(), sig.Signature, pubKey, params) + if simulate { + // Simulated txs should not contain a signature and are not required to + // contain a pubkey, so we must account for tx size of including a + // StdSignature (Amino encoding) and simulate gas consumption + // (assuming a SECP256k1 simulation key). + consumeSimSigGas(ctx.GasMeter(), pubKey, sig, params) + } + + consumeSigVerificationGas(ctx.GasMeter(), sig.Signature, pubKey, params) if !simulate && !pubKey.VerifyBytes(signBytes, sig.Signature) { return nil, sdk.ErrUnauthorized("signature verification failed").Result() } @@ -178,11 +201,22 @@ func processSig( return acc, res } -var dummySecp256k1Pubkey secp256k1.PubKeySecp256k1 +func consumeSimSigGas(gasmeter sdk.GasMeter, pubkey crypto.PubKey, sig StdSignature, params Params) { + simSig := StdSignature{PubKey: pubkey} + if len(sig.Signature) == 0 { + simSig.Signature = simSecp256k1Sig[:] + } -func init() { - bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A") - copy(dummySecp256k1Pubkey[:], bz) + sigBz := msgCdc.MustMarshalBinaryLengthPrefixed(simSig) + cost := sdk.Gas(len(sigBz) + 6) + + // If the pubkey is a multi-signature pubkey, then we estimate for the maximum + // number of signers. + if _, ok := pubkey.(multisig.PubKeyMultisigThreshold); ok { + cost *= params.TxSigLimit + } + + gasmeter.ConsumeGas(params.TxSizeCostPerByte*cost, "txSize") } // ProcessPubKey verifies that the given account address matches that of the @@ -197,7 +231,7 @@ func ProcessPubKey(acc Account, sig StdSignature, simulate bool) (crypto.PubKey, // shall consume the largest amount, i.e. it takes more gas to verify // secp256k1 keys than ed25519 ones. if pubKey == nil { - return dummySecp256k1Pubkey, sdk.Result{} + return simSecp256k1Pubkey, sdk.Result{} } return pubKey, sdk.Result{} @@ -218,25 +252,27 @@ func ProcessPubKey(acc Account, sig StdSignature, simulate bool) (crypto.PubKey, return pubKey, sdk.Result{} } -// consumeSignatureVerificationGas consumes gas for signature verification based -// upon the public key type. The cost is fetched from the given params and is -// matched by the concrete type. +// consumeSigVerificationGas consumes gas for signature verification based upon +// the public key type. The cost is fetched from the given params and is matched +// by the concrete type. // // TODO: Design a cleaner and flexible way to match concrete public key types. -func consumeSignatureVerificationGas(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params Params) { +func consumeSigVerificationGas(meter sdk.GasMeter, sig []byte, pubkey crypto.PubKey, params Params) { pubkeyType := strings.ToLower(fmt.Sprintf("%T", pubkey)) switch { case strings.Contains(pubkeyType, "ed25519"): meter.ConsumeGas(params.SigVerifyCostED25519, "ante verify: ed25519") + case strings.Contains(pubkeyType, "secp256k1"): meter.ConsumeGas(params.SigVerifyCostSecp256k1, "ante verify: secp256k1") - case strings.Contains(pubkeyType, "multisigthreshold"): + case strings.Contains(pubkeyType, "multisigthreshold"): var multisignature multisig.Multisignature codec.Cdc.MustUnmarshalBinaryBare(sig, &multisignature) - multisigPubKey := pubkey.(multisig.PubKeyMultisigThreshold) + multisigPubKey := pubkey.(multisig.PubKeyMultisigThreshold) consumeMultisignatureVerificationGas(meter, multisignature, multisigPubKey, params) + default: panic(fmt.Sprintf("unrecognized signature type: %s", pubkeyType)) } @@ -250,7 +286,7 @@ func consumeMultisignatureVerificationGas(meter sdk.GasMeter, sigIndex := 0 for i := 0; i < size; i++ { if sig.BitArray.GetIndex(i) { - consumeSignatureVerificationGas(meter, sig.Sigs[sigIndex], pubkey.PubKeys[i], params) + consumeSigVerificationGas(meter, sig.Sigs[sigIndex], pubkey.PubKeys[i], params) sigIndex++ } } @@ -294,8 +330,6 @@ func DeductFees(blockTime time.Time, acc Account, fee StdFee) (Account, sdk.Resu // enough fees to cover a proposer's minimum fees. An result object is returned // indicating success or failure. // -// TODO: Account for transaction size. -// // Contract: This should only be called during CheckTx as it cannot be part of // consensus. func EnsureSufficientMempoolFees(ctx sdk.Context, stdFee StdFee) sdk.Result { diff --git a/x/auth/ante_test.go b/x/auth/ante_test.go index bbe4f62fd..21e9f50cd 100644 --- a/x/auth/ante_test.go +++ b/x/auth/ante_test.go @@ -595,9 +595,9 @@ func TestConsumeSignatureVerificationGas(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.wantPanic { - require.Panics(t, func() { consumeSignatureVerificationGas(tt.args.meter, tt.args.sig, tt.args.pubkey, tt.args.params) }) + require.Panics(t, func() { consumeSigVerificationGas(tt.args.meter, tt.args.sig, tt.args.pubkey, tt.args.params) }) } else { - consumeSignatureVerificationGas(tt.args.meter, tt.args.sig, tt.args.pubkey, tt.args.params) + consumeSigVerificationGas(tt.args.meter, tt.args.sig, tt.args.pubkey, tt.args.params) require.Equal(t, tt.gasConsumed, tt.args.meter.GasConsumed(), fmt.Sprintf("%d != %d", tt.gasConsumed, tt.args.meter.GasConsumed())) } }) diff --git a/x/auth/genesis.go b/x/auth/genesis.go index fa9fa2d82..2836fd899 100644 --- a/x/auth/genesis.go +++ b/x/auth/genesis.go @@ -54,8 +54,8 @@ func ValidateGenesis(data GenesisState) error { if data.Params.MaxMemoCharacters == 0 { return fmt.Errorf("invalid max memo characters: %d", data.Params.MaxMemoCharacters) } - if data.Params.MemoCostPerByte == 0 { - return fmt.Errorf("invalid memo cost per byte: %d", data.Params.MemoCostPerByte) + if data.Params.TxSizeCostPerByte == 0 { + return fmt.Errorf("invalid tx size cost per byte: %d", data.Params.TxSizeCostPerByte) } return nil diff --git a/x/auth/params.go b/x/auth/params.go index 9fcde581c..2831dbd32 100644 --- a/x/auth/params.go +++ b/x/auth/params.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/params" ) @@ -15,18 +14,18 @@ const DefaultParamspace = "auth" // Default parameter values const ( - DefaultMemoCostPerByte sdk.Gas = 3 - DefaultMaxMemoCharacters uint64 = 256 - DefaultTxSigLimit uint64 = 7 - DefaultSigVerifyCostED25519 uint64 = 590 - DefaultSigVerifyCostSecp256k1 uint64 = 1000 + DefaultMaxMemoCharacters uint64 = 256 + DefaultTxSigLimit uint64 = 7 + DefaultTxSizeCostPerByte uint64 = 10 + DefaultSigVerifyCostED25519 uint64 = 590 + DefaultSigVerifyCostSecp256k1 uint64 = 1000 ) // Parameter keys var ( - KeyMemoCostPerByte = []byte("MemoCostPerByte") KeyMaxMemoCharacters = []byte("MaxMemoCharacters") KeyTxSigLimit = []byte("TxSigLimit") + KeyTxSizeCostPerByte = []byte("TxSizeCostPerByte") KeySigVerifyCostED25519 = []byte("SigVerifyCostED25519") KeySigVerifyCostSecp256k1 = []byte("SigVerifyCostSecp256k1") ) @@ -35,9 +34,9 @@ var _ params.ParamSet = &Params{} // Params defines the parameters for the auth module. type Params struct { - MemoCostPerByte sdk.Gas MaxMemoCharacters uint64 TxSigLimit uint64 // max total number of signatures per tx + TxSizeCostPerByte uint64 SigVerifyCostED25519 uint64 SigVerifyCostSecp256k1 uint64 } @@ -52,9 +51,9 @@ func ParamTypeTable() params.TypeTable { // nolint func (p *Params) KeyValuePairs() params.KeyValuePairs { return params.KeyValuePairs{ - {KeyMemoCostPerByte, &p.MemoCostPerByte}, {KeyMaxMemoCharacters, &p.MaxMemoCharacters}, {KeyTxSigLimit, &p.TxSigLimit}, + {KeyTxSizeCostPerByte, &p.TxSizeCostPerByte}, {KeySigVerifyCostED25519, &p.SigVerifyCostED25519}, {KeySigVerifyCostSecp256k1, &p.SigVerifyCostSecp256k1}, } @@ -70,9 +69,9 @@ func (p Params) Equal(p2 Params) bool { // DefaultParams returns a default set of parameters. func DefaultParams() Params { return Params{ - MemoCostPerByte: DefaultMemoCostPerByte, MaxMemoCharacters: DefaultMaxMemoCharacters, TxSigLimit: DefaultTxSigLimit, + TxSizeCostPerByte: DefaultTxSizeCostPerByte, SigVerifyCostED25519: DefaultSigVerifyCostED25519, SigVerifyCostSecp256k1: DefaultSigVerifyCostSecp256k1, } @@ -83,9 +82,9 @@ func (p Params) String() string { var sb strings.Builder sb.WriteString("Params: \n") - sb.WriteString(fmt.Sprintf("MemoCostPerByte: %v\n", p.MemoCostPerByte)) sb.WriteString(fmt.Sprintf("MaxMemoCharacters: %d\n", p.MaxMemoCharacters)) sb.WriteString(fmt.Sprintf("TxSigLimit: %d\n", p.TxSigLimit)) + sb.WriteString(fmt.Sprintf("TxSizeCostPerByte: %d\n", p.TxSizeCostPerByte)) sb.WriteString(fmt.Sprintf("SigVerifyCostED25519: %d\n", p.SigVerifyCostED25519)) sb.WriteString(fmt.Sprintf("SigVerifyCostSecp256k1: %d\n", p.SigVerifyCostSecp256k1)) diff --git a/x/bank/app_test.go b/x/bank/app_test.go index a6e9d6dfe..f32d03781 100644 --- a/x/bank/app_test.go +++ b/x/bank/app_test.go @@ -141,7 +141,7 @@ func TestMsgSendWithAccounts(t *testing.T) { } for _, tc := range testCases { - mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) for _, eb := range tc.expectedBalances { mock.CheckBalance(t, mapp, eb.addr, eb.coins) @@ -180,7 +180,7 @@ func TestMsgSendMultipleOut(t *testing.T) { } for _, tc := range testCases { - mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) for _, eb := range tc.expectedBalances { mock.CheckBalance(t, mapp, eb.addr, eb.coins) @@ -224,7 +224,7 @@ func TestSengMsgMultipleInOut(t *testing.T) { } for _, tc := range testCases { - mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) for _, eb := range tc.expectedBalances { mock.CheckBalance(t, mapp, eb.addr, eb.coins) @@ -269,7 +269,7 @@ func TestMsgSendDependent(t *testing.T) { } for _, tc := range testCases { - mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) + mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...) for _, eb := range tc.expectedBalances { mock.CheckBalance(t, mapp, eb.addr, eb.coins) diff --git a/x/ibc/app_test.go b/x/ibc/app_test.go index 8129783d4..6527c79eb 100644 --- a/x/ibc/app_test.go +++ b/x/ibc/app_test.go @@ -72,10 +72,10 @@ func TestIBCMsgs(t *testing.T) { Sequence: 0, } - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []uint64{0}, []uint64{0}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, []sdk.Msg{transferMsg}, []uint64{0}, []uint64{0}, true, true, priv1) mock.CheckBalance(t, mapp, addr1, emptyCoins) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{transferMsg}, []uint64{0}, []uint64{1}, false, false, priv1) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []uint64{0}, []uint64{2}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, []sdk.Msg{transferMsg}, []uint64{0}, []uint64{1}, false, false, priv1) + mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, []sdk.Msg{receiveMsg}, []uint64{0}, []uint64{2}, true, true, priv1) mock.CheckBalance(t, mapp, addr1, coins) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{receiveMsg}, []uint64{0}, []uint64{2}, false, false, priv1) + mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, []sdk.Msg{receiveMsg}, []uint64{0}, []uint64{2}, false, false, priv1) } diff --git a/x/mock/app.go b/x/mock/app.go index 541d923dc..902873ba1 100644 --- a/x/mock/app.go +++ b/x/mock/app.go @@ -51,10 +51,7 @@ func NewApp() *App { db := dbm.NewMemDB() // Create the cdc with some standard codecs - cdc := codec.New() - sdk.RegisterCodec(cdc) - codec.RegisterCrypto(cdc) - auth.RegisterCodec(cdc) + cdc := createCodec() // Create your application object app := &App{ @@ -337,3 +334,11 @@ func incrementAllSequenceNumbers(initSeqNums []uint64) { initSeqNums[i]++ } } + +func createCodec() *codec.Codec { + cdc := codec.New() + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + auth.RegisterCodec(cdc) + return cdc +} diff --git a/x/mock/app_test.go b/x/mock/app_test.go index 098126d89..a10116838 100644 --- a/x/mock/app_test.go +++ b/x/mock/app_test.go @@ -61,14 +61,14 @@ func TestCheckAndDeliverGenTx(t *testing.T) { require.Equal(t, accs[0], acct.(*auth.BaseAccount)) SignCheckDeliver( - t, mApp.BaseApp, []sdk.Msg{msg}, + t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{msg}, []uint64{accs[0].GetAccountNumber()}, []uint64{accs[0].GetSequence()}, true, true, privKeys[0], ) // Signing a tx with the wrong privKey should result in an auth error res := SignCheckDeliver( - t, mApp.BaseApp, []sdk.Msg{msg}, + t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{msg}, []uint64{accs[1].GetAccountNumber()}, []uint64{accs[1].GetSequence() + 1}, true, false, privKeys[1], ) @@ -78,7 +78,7 @@ func TestCheckAndDeliverGenTx(t *testing.T) { // Resigning the tx with the correct privKey should result in an OK result SignCheckDeliver( - t, mApp.BaseApp, []sdk.Msg{msg}, + t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{msg}, []uint64{accs[0].GetAccountNumber()}, []uint64{accs[0].GetSequence() + 1}, true, true, privKeys[0], ) diff --git a/x/mock/test_utils.go b/x/mock/test_utils.go index 9c24967ab..f2af216e1 100644 --- a/x/mock/test_utils.go +++ b/x/mock/test_utils.go @@ -5,6 +5,8 @@ import ( "math/rand" "testing" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" @@ -71,12 +73,16 @@ func CheckGenTx( // the parameter 'expPass' against the result. A corresponding result is // returned. func SignCheckDeliver( - t *testing.T, app *baseapp.BaseApp, msgs []sdk.Msg, accNums []uint64, - seq []uint64, expSimPass, expPass bool, priv ...crypto.PrivKey, + t *testing.T, cdc *codec.Codec, app *baseapp.BaseApp, msgs []sdk.Msg, + accNums []uint64, seq []uint64, expSimPass, expPass bool, priv ...crypto.PrivKey, ) sdk.Result { tx := GenTx(msgs, accNums, seq, priv...) + + txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) + require.Nil(t, err) + // Must simulate now as CheckTx doesn't run Msgs anymore - res := app.Simulate(tx) + res := app.Simulate(txBytes, tx) if expSimPass { require.Equal(t, sdk.CodeOK, res.Code, res.Log) diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index e9c17b89a..20e538080 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -26,6 +26,8 @@ func getMockApp(t *testing.T) (*mock.App, staking.Keeper, Keeper) { mapp := mock.NewApp() RegisterCodec(mapp.Cdc) + stakingTypes.RegisterCodec(mapp.Cdc) + keyStaking := sdk.NewKVStoreKey(staking.StoreKey) tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) keySlashing := sdk.NewKVStoreKey(StoreKey) @@ -107,7 +109,7 @@ func TestSlashingMsgs(t *testing.T) { createValidatorMsg := staking.NewMsgCreateValidator( sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commission, ) - mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1) + mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1) mock.CheckBalance(t, mapp, addr1, sdk.Coins{genCoin.Minus(bondCoin)}) mapp.BeginBlock(abci.RequestBeginBlock{}) @@ -121,7 +123,7 @@ func TestSlashingMsgs(t *testing.T) { checkValidatorSigningInfo(t, mapp, keeper, sdk.ConsAddress(addr1), false) // unjail should fail with unknown validator - res := mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{unjailMsg}, []uint64{0}, []uint64{1}, false, false, priv1) + res := mock.SignCheckDeliver(t, mapp.Cdc, mapp.BaseApp, []sdk.Msg{unjailMsg}, []uint64{0}, []uint64{1}, false, false, priv1) require.EqualValues(t, CodeValidatorNotJailed, res.Code) require.EqualValues(t, DefaultCodespace, res.Codespace) } diff --git a/x/staking/app_test.go b/x/staking/app_test.go index 852a9593a..c7203c6e7 100644 --- a/x/staking/app_test.go +++ b/x/staking/app_test.go @@ -120,7 +120,7 @@ func TestStakingMsgs(t *testing.T) { sdk.ValAddress(addr1), priv1.PubKey(), bondCoin, description, commissionMsg, ) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1) + mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{createValidatorMsg}, []uint64{0}, []uint64{0}, true, true, priv1) mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Minus(bondCoin)}) mApp.BeginBlock(abci.RequestBeginBlock{}) @@ -134,7 +134,7 @@ func TestStakingMsgs(t *testing.T) { addr1, sdk.ValAddress(addr2), priv2.PubKey(), bondCoin, description, commissionMsg, ) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []uint64{0, 0}, []uint64{1, 0}, true, true, priv1, priv2) + mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{createValidatorMsgOnBehalfOf}, []uint64{0, 0}, []uint64{1, 0}, true, true, priv1, priv2) mock.CheckBalance(t, mApp, addr1, sdk.Coins{genCoin.Minus(bondCoin).Minus(bondCoin)}) mApp.BeginBlock(abci.RequestBeginBlock{}) @@ -150,7 +150,7 @@ func TestStakingMsgs(t *testing.T) { description = NewDescription("bar_moniker", "", "", "") editValidatorMsg := NewMsgEditValidator(sdk.ValAddress(addr1), description, nil) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []uint64{0}, []uint64{2}, true, true, priv1) + mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{editValidatorMsg}, []uint64{0}, []uint64{2}, true, true, priv1) validator = checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true) require.Equal(t, description, validator.Description) @@ -158,13 +158,13 @@ func TestStakingMsgs(t *testing.T) { mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin}) delegateMsg := NewMsgDelegate(addr2, sdk.ValAddress(addr1), bondCoin) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{delegateMsg}, []uint64{0}, []uint64{1}, true, true, priv2) + mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{delegateMsg}, []uint64{0}, []uint64{1}, true, true, priv2) mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)}) checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), true, sdk.NewDec(10)) // begin unbonding beginUnbondingMsg := NewMsgUndelegate(addr2, sdk.ValAddress(addr1), sdk.NewDec(10)) - mock.SignCheckDeliver(t, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []uint64{0}, []uint64{2}, true, true, priv2) + mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []uint64{0}, []uint64{2}, true, true, priv2) // delegation should exist anymore checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), false, sdk.Dec{}) From 18eee0a3a8166f56b4cca0f6265b40a1ade5574c Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Mon, 4 Feb 2019 15:58:02 -0800 Subject: [PATCH 47/99] Merge PR #3428: SendMsg and MultiSendMsg seperation --- client/lcd/lcd_test.go | 4 +- cmd/gaia/app/benchmarks/txsize_test.go | 2 +- cmd/gaia/app/sim_test.go | 3 +- x/bank/app_test.go | 32 ++--- x/bank/bench_test.go | 30 +++++ x/bank/client/cli/sendtx.go | 4 +- x/bank/client/rest/sendtx.go | 5 +- x/bank/client/util.go | 14 -- x/bank/codec.go | 1 + x/bank/handler.go | 17 +++ x/bank/msgs.go | 62 +++++++-- x/bank/msgs_test.go | 101 ++++++++++++--- x/bank/simulation/msgs.go | 169 +++++++++++++++++++++---- 13 files changed, 349 insertions(+), 95 deletions(-) delete mode 100644 x/bank/client/util.go diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index c29eb64a7..d9e99c0e5 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -259,7 +259,7 @@ func TestCoinSendAccAuto(t *testing.T) { require.Equal(t, expectedBalance.Amount.SubRaw(1), coins[0].Amount) } -func TestCoinSendGenerateOnly(t *testing.T) { +func TestCoinMultiSendGenerateOnly(t *testing.T) { addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() @@ -277,7 +277,7 @@ func TestCoinSendGenerateOnly(t *testing.T) { require.Equal(t, memo, stdTx.Memo) require.NotZero(t, stdTx.Fee.Gas) require.IsType(t, stdTx.GetMsgs()[0], bank.MsgSend{}) - require.Equal(t, addr, stdTx.GetMsgs()[0].(bank.MsgSend).Inputs[0].Address) + require.Equal(t, addr, stdTx.GetMsgs()[0].(bank.MsgSend).FromAddress) } func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { diff --git a/cmd/gaia/app/benchmarks/txsize_test.go b/cmd/gaia/app/benchmarks/txsize_test.go index 2726dcd1b..af381a3fd 100644 --- a/cmd/gaia/app/benchmarks/txsize_test.go +++ b/cmd/gaia/app/benchmarks/txsize_test.go @@ -23,7 +23,7 @@ func ExampleTxSendSize() { priv2 := secp256k1.GenPrivKeySecp256k1([]byte{1}) addr2 := sdk.AccAddress(priv2.PubKey().Address()) coins := sdk.Coins{sdk.NewCoin("denom", sdk.NewInt(10))} - msg1 := bank.MsgSend{ + msg1 := bank.MsgMultiSend{ Inputs: []bank.Input{bank.NewInput(addr1, coins)}, Outputs: []bank.Output{bank.NewOutput(addr2, coins)}, } diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index f7d645a61..860404a56 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -266,7 +266,8 @@ func randIntBetween(r *rand.Rand, min, max int) int { func testAndRunTxs(app *GaiaApp) []simulation.WeightedOperation { return []simulation.WeightedOperation{ {5, authsim.SimulateDeductFee(app.accountKeeper, app.feeCollectionKeeper)}, - {100, banksim.SingleInputSendMsg(app.accountKeeper, app.bankKeeper)}, + {100, banksim.SendMsg(app.accountKeeper, app.bankKeeper)}, + {10, banksim.SingleInputMsgMultiSend(app.accountKeeper, app.bankKeeper)}, {50, distrsim.SimulateMsgSetWithdrawAddress(app.accountKeeper, app.distrKeeper)}, {50, distrsim.SimulateMsgWithdrawDelegatorReward(app.accountKeeper, app.distrKeeper)}, {50, distrsim.SimulateMsgWithdrawValidatorCommission(app.accountKeeper, app.distrKeeper)}, diff --git a/x/bank/app_test.go b/x/bank/app_test.go index f32d03781..824f48670 100644 --- a/x/bank/app_test.go +++ b/x/bank/app_test.go @@ -45,18 +45,20 @@ var ( manyCoins = sdk.Coins{sdk.NewInt64Coin("foocoin", 1), sdk.NewInt64Coin("barcoin", 1)} freeFee = auth.NewStdFee(100000, sdk.Coins{sdk.NewInt64Coin("foocoin", 0)}) - sendMsg1 = MsgSend{ + sendMsg1 = NewMsgSend(addr1, addr2, coins) + + multiSendMsg1 = MsgMultiSend{ Inputs: []Input{NewInput(addr1, coins)}, Outputs: []Output{NewOutput(addr2, coins)}, } - sendMsg2 = MsgSend{ + multiSendMsg2 = MsgMultiSend{ Inputs: []Input{NewInput(addr1, coins)}, Outputs: []Output{ NewOutput(addr2, halfCoins), NewOutput(addr3, halfCoins), }, } - sendMsg3 = MsgSend{ + multiSendMsg3 = MsgMultiSend{ Inputs: []Input{ NewInput(addr1, coins), NewInput(addr4, coins), @@ -66,7 +68,7 @@ var ( NewOutput(addr3, coins), }, } - sendMsg4 = MsgSend{ + multiSendMsg4 = MsgMultiSend{ Inputs: []Input{ NewInput(addr2, coins), }, @@ -74,7 +76,7 @@ var ( NewOutput(addr1, coins), }, } - sendMsg5 = MsgSend{ + multiSendMsg5 = MsgMultiSend{ Inputs: []Input{ NewInput(addr1, manyCoins), }, @@ -102,7 +104,7 @@ func getInitChainer(mapp *mock.App, keeper BaseKeeper) sdk.InitChainer { } } -func TestMsgSendWithAccounts(t *testing.T) { +func TestMsgMultiSendWithAccounts(t *testing.T) { mapp := getMockApp(t) acc := &auth.BaseAccount{ Address: addr1, @@ -119,7 +121,7 @@ func TestMsgSendWithAccounts(t *testing.T) { testCases := []appTestCase{ { - msgs: []sdk.Msg{sendMsg1}, + msgs: []sdk.Msg{multiSendMsg1}, accNums: []uint64{0}, accSeqs: []uint64{0}, expSimPass: true, @@ -131,7 +133,7 @@ func TestMsgSendWithAccounts(t *testing.T) { }, }, { - msgs: []sdk.Msg{sendMsg1, sendMsg2}, + msgs: []sdk.Msg{multiSendMsg1, multiSendMsg2}, accNums: []uint64{0}, accSeqs: []uint64{0}, expSimPass: true, // doesn't check signature @@ -149,7 +151,7 @@ func TestMsgSendWithAccounts(t *testing.T) { } } -func TestMsgSendMultipleOut(t *testing.T) { +func TestMsgMultiSendMultipleOut(t *testing.T) { mapp := getMockApp(t) acc1 := &auth.BaseAccount{ @@ -165,7 +167,7 @@ func TestMsgSendMultipleOut(t *testing.T) { testCases := []appTestCase{ { - msgs: []sdk.Msg{sendMsg2}, + msgs: []sdk.Msg{multiSendMsg2}, accNums: []uint64{0}, accSeqs: []uint64{0}, expSimPass: true, @@ -188,7 +190,7 @@ func TestMsgSendMultipleOut(t *testing.T) { } } -func TestSengMsgMultipleInOut(t *testing.T) { +func TestMsgMultiSendMultipleInOut(t *testing.T) { mapp := getMockApp(t) acc1 := &auth.BaseAccount{ @@ -208,7 +210,7 @@ func TestSengMsgMultipleInOut(t *testing.T) { testCases := []appTestCase{ { - msgs: []sdk.Msg{sendMsg3}, + msgs: []sdk.Msg{multiSendMsg3}, accNums: []uint64{0, 0}, accSeqs: []uint64{0, 0}, expSimPass: true, @@ -232,7 +234,7 @@ func TestSengMsgMultipleInOut(t *testing.T) { } } -func TestMsgSendDependent(t *testing.T) { +func TestMsgMultiSendDependent(t *testing.T) { mapp := getMockApp(t) acc1 := &auth.BaseAccount{ @@ -244,7 +246,7 @@ func TestMsgSendDependent(t *testing.T) { testCases := []appTestCase{ { - msgs: []sdk.Msg{sendMsg1}, + msgs: []sdk.Msg{multiSendMsg1}, accNums: []uint64{0}, accSeqs: []uint64{0}, expSimPass: true, @@ -256,7 +258,7 @@ func TestMsgSendDependent(t *testing.T) { }, }, { - msgs: []sdk.Msg{sendMsg4}, + msgs: []sdk.Msg{multiSendMsg4}, accNums: []uint64{0}, accSeqs: []uint64{0}, expSimPass: true, diff --git a/x/bank/bench_test.go b/x/bank/bench_test.go index 5eb702713..95918110c 100644 --- a/x/bank/bench_test.go +++ b/x/bank/bench_test.go @@ -57,3 +57,33 @@ func BenchmarkOneBankSendTxPerBlock(b *testing.B) { benchmarkApp.Commit() } } + +func BenchmarkOneBankMultiSendTxPerBlock(b *testing.B) { + benchmarkApp, _ := getBenchmarkMockApp() + + // Add an account at genesis + acc := &auth.BaseAccount{ + Address: addr1, + // Some value conceivably higher than the benchmarks would ever go + Coins: sdk.Coins{sdk.NewInt64Coin("foocoin", 100000000000)}, + } + accs := []auth.Account{acc} + + // Construct genesis state + mock.SetGenesis(benchmarkApp, accs) + // Precompute all txs + txs := mock.GenSequenceOfTxs([]sdk.Msg{multiSendMsg1}, []uint64{0}, []uint64{uint64(0)}, b.N, priv1) + b.ResetTimer() + // Run this with a profiler, so its easy to distinguish what time comes from + // Committing, and what time comes from Check/Deliver Tx. + for i := 0; i < b.N; i++ { + benchmarkApp.BeginBlock(abci.RequestBeginBlock{}) + x := benchmarkApp.Check(txs[i]) + if !x.IsOK() { + panic("something is broken in checking transaction") + } + benchmarkApp.Deliver(txs[i]) + benchmarkApp.EndBlock(abci.RequestEndBlock{}) + benchmarkApp.Commit() + } +} diff --git a/x/bank/client/cli/sendtx.go b/x/bank/client/cli/sendtx.go index 9da232789..4f805bf42 100644 --- a/x/bank/client/cli/sendtx.go +++ b/x/bank/client/cli/sendtx.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - bankClient "github.com/cosmos/cosmos-sdk/x/bank/client" + "github.com/cosmos/cosmos-sdk/x/bank" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -62,7 +62,7 @@ func SendTxCmd(cdc *codec.Codec) *cobra.Command { } // build and sign the transaction, then broadcast to Tendermint - msg := bankClient.CreateMsg(from, to, coins) + msg := bank.NewMsgSend(from, to, coins) if cliCtx.GenerateOnly { return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, []sdk.Msg{msg}, false) } diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index 70aae05fc..c53e1431a 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -10,7 +10,6 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" - bankclient "github.com/cosmos/cosmos-sdk/x/bank/client" "github.com/gorilla/mux" ) @@ -64,7 +63,7 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC return } - msg := bankclient.CreateMsg(fromAddr, toAddr, req.Amount) + msg := bank.NewMsgSend(fromAddr, toAddr, req.Amount) rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) return } @@ -77,7 +76,7 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC } cliCtx = cliCtx.WithFromName(fromName).WithFromAddress(fromAddress) - msg := bankclient.CreateMsg(cliCtx.GetFromAddress(), toAddr, req.Amount) + msg := bank.NewMsgSend(cliCtx.GetFromAddress(), toAddr, req.Amount) rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) } diff --git a/x/bank/client/util.go b/x/bank/client/util.go deleted file mode 100644 index b647f00d3..000000000 --- a/x/bank/client/util.go +++ /dev/null @@ -1,14 +0,0 @@ -package client - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - bank "github.com/cosmos/cosmos-sdk/x/bank" -) - -// create the sendTx msg -func CreateMsg(from sdk.AccAddress, to sdk.AccAddress, coins sdk.Coins) sdk.Msg { - input := bank.NewInput(from, coins) - output := bank.NewOutput(to, coins) - msg := bank.NewMsgSend([]bank.Input{input}, []bank.Output{output}) - return msg -} diff --git a/x/bank/codec.go b/x/bank/codec.go index 2195e4853..505ffd5b8 100644 --- a/x/bank/codec.go +++ b/x/bank/codec.go @@ -7,6 +7,7 @@ import ( // Register concrete types on codec codec func RegisterCodec(cdc *codec.Codec) { cdc.RegisterConcrete(MsgSend{}, "cosmos-sdk/Send", nil) + cdc.RegisterConcrete(MsgMultiSend{}, "cosmos-sdk/MultiSend", nil) } var msgCdc = codec.New() diff --git a/x/bank/handler.go b/x/bank/handler.go index 2d1abf822..e58738135 100644 --- a/x/bank/handler.go +++ b/x/bank/handler.go @@ -10,6 +10,8 @@ func NewHandler(k Keeper) sdk.Handler { switch msg := msg.(type) { case MsgSend: return handleMsgSend(ctx, k, msg) + case MsgMultiSend: + return handleMsgMultiSend(ctx, k, msg) default: errMsg := "Unrecognized bank Msg type: %s" + msg.Type() return sdk.ErrUnknownRequest(errMsg).Result() @@ -19,6 +21,21 @@ func NewHandler(k Keeper) sdk.Handler { // Handle MsgSend. func handleMsgSend(ctx sdk.Context, k Keeper, msg MsgSend) sdk.Result { + if !k.GetSendEnabled(ctx) { + return ErrSendDisabled(k.Codespace()).Result() + } + tags, err := k.SendCoins(ctx, msg.FromAddress, msg.ToAddress, msg.Amount) + if err != nil { + return err.Result() + } + + return sdk.Result{ + Tags: tags, + } +} + +// Handle MsgMultiSend. +func handleMsgMultiSend(ctx sdk.Context, k Keeper, msg MsgMultiSend) sdk.Result { // NOTE: totalIn == totalOut should already have been checked if !k.GetSendEnabled(ctx) { return ErrSendDisabled(k.Codespace()).Result() diff --git a/x/bank/msgs.go b/x/bank/msgs.go index 059b49525..83bdc6633 100644 --- a/x/bank/msgs.go +++ b/x/bank/msgs.go @@ -9,15 +9,16 @@ const RouterKey = "bank" // MsgSend - high level transaction of the coin module type MsgSend struct { - Inputs []Input `json:"inputs"` - Outputs []Output `json:"outputs"` + FromAddress sdk.AccAddress `json:"from_address"` + ToAddress sdk.AccAddress `json:"to_address"` + Amount sdk.Coins `json:"amount"` } var _ sdk.Msg = MsgSend{} // NewMsgSend - construct arbitrary multi-in, multi-out send msg. -func NewMsgSend(in []Input, out []Output) MsgSend { - return MsgSend{Inputs: in, Outputs: out} +func NewMsgSend(fromAddr, toAddr sdk.AccAddress, amount sdk.Coins) MsgSend { + return MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: amount} } // Implements Msg. @@ -27,6 +28,48 @@ func (msg MsgSend) Type() string { return "send" } // Implements Msg. func (msg MsgSend) ValidateBasic() sdk.Error { + if msg.FromAddress.Empty() { + return sdk.ErrInvalidAddress("missing sender address") + } + if msg.ToAddress.Empty() { + return sdk.ErrInvalidAddress("missing recipient address") + } + if !msg.Amount.IsPositive() { + return sdk.ErrInsufficientCoins("send amount must be positive") + } + return nil +} + +// Implements Msg. +func (msg MsgSend) GetSignBytes() []byte { + return sdk.MustSortJSON(msgCdc.MustMarshalJSON(msg)) +} + +// Implements Msg. +func (msg MsgSend) GetSigners() []sdk.AccAddress { + return []sdk.AccAddress{msg.FromAddress} +} + +// MsgMultiSend - high level transaction of the coin module +type MsgMultiSend struct { + Inputs []Input `json:"inputs"` + Outputs []Output `json:"outputs"` +} + +var _ sdk.Msg = MsgMultiSend{} + +// NewMsgMultiSend - construct arbitrary multi-in, multi-out send msg. +func NewMsgMultiSend(in []Input, out []Output) MsgMultiSend { + return MsgMultiSend{Inputs: in, Outputs: out} +} + +// Implements Msg. +// nolint +func (msg MsgMultiSend) Route() string { return RouterKey } +func (msg MsgMultiSend) Type() string { return "multisend" } + +// Implements Msg. +func (msg MsgMultiSend) ValidateBasic() sdk.Error { // this just makes sure all the inputs and outputs are properly formatted, // not that they actually have the money inside if len(msg.Inputs) == 0 { @@ -40,13 +83,12 @@ func (msg MsgSend) ValidateBasic() sdk.Error { } // Implements Msg. -func (msg MsgSend) GetSignBytes() []byte { - bz := msgCdc.MustMarshalJSON(msg) - return sdk.MustSortJSON(bz) +func (msg MsgMultiSend) GetSignBytes() []byte { + return sdk.MustSortJSON(msgCdc.MustMarshalJSON(msg)) } // Implements Msg. -func (msg MsgSend) GetSigners() []sdk.AccAddress { +func (msg MsgMultiSend) GetSigners() []sdk.AccAddress { addrs := make([]sdk.AccAddress, len(msg.Inputs)) for i, in := range msg.Inputs { addrs[i] = in.Address @@ -77,7 +119,7 @@ func (in Input) ValidateBasic() sdk.Error { return nil } -// NewInput - create a transaction input, used with MsgSend +// NewInput - create a transaction input, used with MsgMultiSend func NewInput(addr sdk.AccAddress, coins sdk.Coins) Input { return Input{ Address: addr, @@ -108,7 +150,7 @@ func (out Output) ValidateBasic() sdk.Error { return nil } -// NewOutput - create a transaction output, used with MsgSend +// NewOutput - create a transaction output, used with MsgMultiSend func NewOutput(addr sdk.AccAddress, coins sdk.Coins) Output { return Output{ Address: addr, diff --git a/x/bank/msgs_test.go b/x/bank/msgs_test.go index 3f8b68fd4..fbc42ddc2 100644 --- a/x/bank/msgs_test.go +++ b/x/bank/msgs_test.go @@ -9,20 +9,79 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -func TestNewMsgSend(t *testing.T) {} - func TestMsgSendRoute(t *testing.T) { + addr1 := sdk.AccAddress([]byte("from")) + addr2 := sdk.AccAddress([]byte("to")) + coins := sdk.Coins{sdk.NewInt64Coin("atom", 10)} + var msg = NewMsgSend(addr1, addr2, coins) + + require.Equal(t, msg.Route(), "bank") + require.Equal(t, msg.Type(), "send") +} + +func TestMsgSendValidation(t *testing.T) { + addr1 := sdk.AccAddress([]byte("from")) + addr2 := sdk.AccAddress([]byte("to")) + atom123 := sdk.Coins{sdk.NewInt64Coin("atom", 123)} + atom0 := sdk.Coins{sdk.NewInt64Coin("atom", 0)} + atom123eth123 := sdk.Coins{sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 123)} + atom123eth0 := sdk.Coins{sdk.NewInt64Coin("atom", 123), sdk.NewInt64Coin("eth", 0)} + + var emptyAddr sdk.AccAddress + + cases := []struct { + valid bool + tx MsgSend + }{ + {true, NewMsgSend(addr1, addr2, atom123)}, // valid send + {true, NewMsgSend(addr1, addr2, atom123eth123)}, // valid send with multiple coins + {false, NewMsgSend(addr1, addr2, atom0)}, // non positive coin + {false, NewMsgSend(addr1, addr2, atom123eth0)}, // non positive coin in multicoins + {false, NewMsgSend(emptyAddr, addr2, atom123)}, // empty from addr + {false, NewMsgSend(addr1, emptyAddr, atom123)}, // empty to addr + } + + for i, tc := range cases { + err := tc.tx.ValidateBasic() + if tc.valid { + require.Nil(t, err, "%d: %+v", i, err) + } else { + require.NotNil(t, err, "%d", i) + } + } +} + +func TestMsgSendGetSignBytes(t *testing.T) { + addr1 := sdk.AccAddress([]byte("input")) + addr2 := sdk.AccAddress([]byte("output")) + coins := sdk.Coins{sdk.NewInt64Coin("atom", 10)} + var msg = NewMsgSend(addr1, addr2, coins) + res := msg.GetSignBytes() + + expected := `{"type":"cosmos-sdk/Send","value":{"amount":[{"amount":"10","denom":"atom"}],"from_address":"cosmos1d9h8qat57ljhcm","to_address":"cosmos1da6hgur4wsmpnjyg"}}` + require.Equal(t, expected, string(res)) +} + +func TestMsgSendGetSigners(t *testing.T) { + var msg = NewMsgSend(sdk.AccAddress([]byte("input1")), sdk.AccAddress{}, sdk.Coins{}) + res := msg.GetSigners() + // TODO: fix this ! + require.Equal(t, fmt.Sprintf("%v", res), "[696E70757431]") +} + +func TestMsgMultiSendRoute(t *testing.T) { // Construct a MsgSend addr1 := sdk.AccAddress([]byte("input")) addr2 := sdk.AccAddress([]byte("output")) coins := sdk.Coins{sdk.NewInt64Coin("atom", 10)} - var msg = MsgSend{ + var msg = MsgMultiSend{ Inputs: []Input{NewInput(addr1, coins)}, Outputs: []Output{NewOutput(addr2, coins)}, } // TODO some failures for bad result require.Equal(t, msg.Route(), "bank") + require.Equal(t, msg.Type(), "multisend") } func TestInputValidation(t *testing.T) { @@ -101,7 +160,7 @@ func TestOutputValidation(t *testing.T) { } } -func TestMsgSendValidation(t *testing.T) { +func TestMsgMultiSendValidation(t *testing.T) { addr1 := sdk.AccAddress([]byte{1, 2}) addr2 := sdk.AccAddress([]byte{7, 8}) atom123 := sdk.Coins{sdk.NewInt64Coin("atom", 123)} @@ -120,40 +179,40 @@ func TestMsgSendValidation(t *testing.T) { cases := []struct { valid bool - tx MsgSend + tx MsgMultiSend }{ - {false, MsgSend{}}, // no input or output - {false, MsgSend{Inputs: []Input{input1}}}, // just input - {false, MsgSend{Outputs: []Output{output1}}}, // just output - {false, MsgSend{ + {false, MsgMultiSend{}}, // no input or output + {false, MsgMultiSend{Inputs: []Input{input1}}}, // just input + {false, MsgMultiSend{Outputs: []Output{output1}}}, // just output + {false, MsgMultiSend{ Inputs: []Input{NewInput(emptyAddr, atom123)}, // invalid input Outputs: []Output{output1}}}, - {false, MsgSend{ + {false, MsgMultiSend{ Inputs: []Input{input1}, Outputs: []Output{{emptyAddr, atom123}}}, // invalid output }, - {false, MsgSend{ + {false, MsgMultiSend{ Inputs: []Input{input1}, Outputs: []Output{output2}}, // amounts dont match }, - {false, MsgSend{ + {false, MsgMultiSend{ Inputs: []Input{input1}, Outputs: []Output{output3}}, // amounts dont match }, - {false, MsgSend{ + {false, MsgMultiSend{ Inputs: []Input{input1}, Outputs: []Output{outputMulti}}, // amounts dont match }, - {false, MsgSend{ + {false, MsgMultiSend{ Inputs: []Input{input2}, Outputs: []Output{output1}}, // amounts dont match }, - {true, MsgSend{ + {true, MsgMultiSend{ Inputs: []Input{input1}, Outputs: []Output{output1}}, }, - {true, MsgSend{ + {true, MsgMultiSend{ Inputs: []Input{input1, input2}, Outputs: []Output{outputMulti}}, }, @@ -169,22 +228,22 @@ func TestMsgSendValidation(t *testing.T) { } } -func TestMsgSendGetSignBytes(t *testing.T) { +func TestMsgMultiSendGetSignBytes(t *testing.T) { addr1 := sdk.AccAddress([]byte("input")) addr2 := sdk.AccAddress([]byte("output")) coins := sdk.Coins{sdk.NewInt64Coin("atom", 10)} - var msg = MsgSend{ + var msg = MsgMultiSend{ Inputs: []Input{NewInput(addr1, coins)}, Outputs: []Output{NewOutput(addr2, coins)}, } res := msg.GetSignBytes() - expected := `{"type":"cosmos-sdk/Send","value":{"inputs":[{"address":"cosmos1d9h8qat57ljhcm","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmos1da6hgur4wsmpnjyg","coins":[{"amount":"10","denom":"atom"}]}]}}` + expected := `{"type":"cosmos-sdk/MultiSend","value":{"inputs":[{"address":"cosmos1d9h8qat57ljhcm","coins":[{"amount":"10","denom":"atom"}]}],"outputs":[{"address":"cosmos1da6hgur4wsmpnjyg","coins":[{"amount":"10","denom":"atom"}]}]}}` require.Equal(t, expected, string(res)) } -func TestMsgSendGetSigners(t *testing.T) { - var msg = MsgSend{ +func TestMsgMultiSendGetSigners(t *testing.T) { + var msg = MsgMultiSend{ Inputs: []Input{ NewInput(sdk.AccAddress([]byte("input1")), nil), NewInput(sdk.AccAddress([]byte("input2")), nil), diff --git a/x/bank/simulation/msgs.go b/x/bank/simulation/msgs.go index c4d4919eb..a3b47823d 100644 --- a/x/bank/simulation/msgs.go +++ b/x/bank/simulation/msgs.go @@ -16,11 +16,30 @@ import ( "github.com/cosmos/cosmos-sdk/x/mock/simulation" ) -// SingleInputSendTx tests and runs a single msg send w/ auth, with one input and one output, where both +// SendTx tests and runs a single msg send where both // accounts already exist. -func SingleInputSendTx(mapper auth.AccountKeeper) simulation.Operation { +func SendMsg(mapper auth.AccountKeeper, bk bank.Keeper) simulation.Operation { + handler := bank.NewHandler(bk) return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { - fromAcc, action, msg, abort := createSingleInputSendMsg(r, ctx, accs, mapper) + fromAcc, action, msg, abort := createSendMsg(r, ctx, accs, mapper) + if abort { + return action, nil, nil + } + err = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}, handler) + if err != nil { + return "", nil, err + } + event("bank/sendAndVerifyTxSend/ok") + + return action, nil, nil + } +} + +// SendTx tests and runs a single tx send, with auth where both +// accounts already exist. +func SendTx(mapper auth.AccountKeeper) simulation.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { + fromAcc, action, msg, abort := createSendMsg(r, ctx, accs, mapper) if abort { return action, nil, nil } @@ -34,26 +53,7 @@ func SingleInputSendTx(mapper auth.AccountKeeper) simulation.Operation { } } -// SingleInputSendMsg tests and runs a single msg send, with one input and one output, where both -// accounts already exist. -func SingleInputSendMsg(mapper auth.AccountKeeper, bk bank.Keeper) simulation.Operation { - handler := bank.NewHandler(bk) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { - fromAcc, action, msg, abort := createSingleInputSendMsg(r, ctx, accs, mapper) - if abort { - return action, nil, nil - } - err = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}, handler) - if err != nil { - return "", nil, err - } - event("bank/sendAndVerifyMsgSend/ok") - - return action, nil, nil - } -} - -func createSingleInputSendMsg(r *rand.Rand, ctx sdk.Context, accs []simulation.Account, mapper auth.AccountKeeper) (fromAcc simulation.Account, action string, msg bank.MsgSend, abort bool) { +func createSendMsg(r *rand.Rand, ctx sdk.Context, accs []simulation.Account, mapper auth.AccountKeeper) (fromAcc simulation.Account, action string, msg bank.MsgSend, abort bool) { fromAcc = simulation.RandomAcc(r, accs) toAcc := simulation.RandomAcc(r, accs) // Disallow sending money to yourself @@ -84,16 +84,133 @@ func createSingleInputSendMsg(r *rand.Rand, ctx sdk.Context, accs []simulation.A ) coins := sdk.Coins{sdk.NewCoin(initFromCoins[denomIndex].Denom, amt)} - msg = bank.MsgSend{ + msg = bank.NewMsgSend(fromAcc.Address, toAcc.Address, coins) + return +} + +// Sends and verifies the transition of a msg send. +func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper auth.AccountKeeper, msg bank.MsgSend, ctx sdk.Context, privkeys []crypto.PrivKey, handler sdk.Handler) error { + fromAcc := mapper.GetAccount(ctx, msg.FromAddress) + AccountNumbers := []uint64{fromAcc.GetAccountNumber()} + SequenceNumbers := []uint64{fromAcc.GetSequence()} + initialFromAddrCoins := fromAcc.GetCoins() + + toAcc := mapper.GetAccount(ctx, msg.ToAddress) + initialToAddrCoins := toAcc.GetCoins() + + if handler != nil { + res := handler(ctx, msg) + if !res.IsOK() { + if res.Code == bank.CodeSendDisabled { + return nil + } + // TODO: Do this in a more 'canonical' way + return fmt.Errorf("handling msg failed %v", res) + } + } else { + tx := mock.GenTx([]sdk.Msg{msg}, + AccountNumbers, + SequenceNumbers, + privkeys...) + res := app.Deliver(tx) + if !res.IsOK() { + // TODO: Do this in a more 'canonical' way + return fmt.Errorf("Deliver failed %v", res) + } + } + + fromAcc = mapper.GetAccount(ctx, msg.FromAddress) + toAcc = mapper.GetAccount(ctx, msg.ToAddress) + + if !initialFromAddrCoins.Minus(msg.Amount).IsEqual(fromAcc.GetCoins()) { + return fmt.Errorf("fromAddress %s had an incorrect amount of coins", fromAcc.GetAddress()) + } + + if !initialToAddrCoins.Plus(msg.Amount).IsEqual(toAcc.GetCoins()) { + return fmt.Errorf("toAddress %s had an incorrect amount of coins", toAcc.GetAddress()) + } + + return nil +} + +// SingleInputSendTx tests and runs a single msg multisend w/ auth, with one input and one output, where both +// accounts already exist. +func SingleInputMultiSendTx(mapper auth.AccountKeeper) simulation.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { + fromAcc, action, msg, abort := createSingleInputMsgMultiSend(r, ctx, accs, mapper) + if abort { + return action, nil, nil + } + err = sendAndVerifyMsgMultiSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}, nil) + if err != nil { + return "", nil, err + } + event("bank/sendAndVerifyTxMultiSend/ok") + + return action, nil, nil + } +} + +// SingleInputSendMsg tests and runs a single msg multisend, with one input and one output, where both +// accounts already exist. +func SingleInputMsgMultiSend(mapper auth.AccountKeeper, bk bank.Keeper) simulation.Operation { + handler := bank.NewHandler(bk) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { + fromAcc, action, msg, abort := createSingleInputMsgMultiSend(r, ctx, accs, mapper) + if abort { + return action, nil, nil + } + err = sendAndVerifyMsgMultiSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}, handler) + if err != nil { + return "", nil, err + } + event("bank/sendAndVerifyMsgMultiSend/ok") + + return action, nil, nil + } +} + +func createSingleInputMsgMultiSend(r *rand.Rand, ctx sdk.Context, accs []simulation.Account, mapper auth.AccountKeeper) (fromAcc simulation.Account, action string, msg bank.MsgMultiSend, abort bool) { + fromAcc = simulation.RandomAcc(r, accs) + toAcc := simulation.RandomAcc(r, accs) + // Disallow sending money to yourself + for { + if !fromAcc.PubKey.Equals(toAcc.PubKey) { + break + } + toAcc = simulation.RandomAcc(r, accs) + } + toAddr := toAcc.Address + initFromCoins := mapper.GetAccount(ctx, fromAcc.Address).SpendableCoins(ctx.BlockHeader().Time) + + if len(initFromCoins) == 0 { + return fromAcc, "skipping, no coins at all", msg, true + } + + denomIndex := r.Intn(len(initFromCoins)) + amt, goErr := randPositiveInt(r, initFromCoins[denomIndex].Amount) + if goErr != nil { + return fromAcc, "skipping bank send due to account having no coins of denomination " + initFromCoins[denomIndex].Denom, msg, true + } + + action = fmt.Sprintf("%s is sending %s %s to %s", + fromAcc.Address.String(), + amt.String(), + initFromCoins[denomIndex].Denom, + toAddr.String(), + ) + + coins := sdk.Coins{sdk.NewCoin(initFromCoins[denomIndex].Denom, amt)} + msg = bank.MsgMultiSend{ Inputs: []bank.Input{bank.NewInput(fromAcc.Address, coins)}, Outputs: []bank.Output{bank.NewOutput(toAddr, coins)}, } return } -// Sends and verifies the transition of a msg send. This fails if there are repeated inputs or outputs +// Sends and verifies the transition of a msg multisend. This fails if there are repeated inputs or outputs // pass in handler as nil to handle txs, otherwise handle msgs -func sendAndVerifyMsgSend(app *baseapp.BaseApp, mapper auth.AccountKeeper, msg bank.MsgSend, ctx sdk.Context, privkeys []crypto.PrivKey, handler sdk.Handler) error { +func sendAndVerifyMsgMultiSend(app *baseapp.BaseApp, mapper auth.AccountKeeper, msg bank.MsgMultiSend, ctx sdk.Context, privkeys []crypto.PrivKey, handler sdk.Handler) error { initialInputAddrCoins := make([]sdk.Coins, len(msg.Inputs)) initialOutputAddrCoins := make([]sdk.Coins, len(msg.Outputs)) AccountNumbers := make([]uint64, len(msg.Inputs)) From af733684ac80bc96c99265fa0db0708cb7815098 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Mon, 4 Feb 2019 16:04:33 -0800 Subject: [PATCH 48/99] name is cosmos-gaia --- snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapcraft.yaml b/snapcraft.yaml index 0c24dafe0..301c4031f 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -1,4 +1,4 @@ -name: gaia +name: cosmos-gaia version: git summary: Gaia Daemon # 79 char long summary description: | From 254c39a9e2bd9bd84222469dcfc2027cabbb6161 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Mon, 4 Feb 2019 16:31:08 -0800 Subject: [PATCH 49/99] Merge PR #3496: Gov validator power sdk.Int --- PENDING.md | 1 + x/gov/tally.go | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/PENDING.md b/PENDING.md index 9e3f1c1eb..0d8ad89ad 100644 --- a/PENDING.md +++ b/PENDING.md @@ -15,6 +15,7 @@ BREAKING CHANGES - `--tls` is now used to enable secure layer. * Gaia + * [\#3457](https://github.com/cosmos/cosmos-sdk/issues/3457) Changed governance tally validatorGovInfo to use sdk.Int power instead of sdk.Dec * SDK * [\#3487](https://github.com/cosmos/cosmos-sdk/pull/3487) Move HTTP/REST utilities out of client/utils into a new dedicated client/rest package. diff --git a/x/gov/tally.go b/x/gov/tally.go index 8b44b034c..7877511d4 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -7,7 +7,7 @@ import ( // validatorGovInfo used for tallying type validatorGovInfo struct { Address sdk.ValAddress // address of the validator operator - Power sdk.Dec // Power of a Validator + Power sdk.Int // Power of a Validator DelegatorShares sdk.Dec // Total outstanding delegator shares Minus sdk.Dec // Minus of validator, used to compute validator's voting power Vote VoteOption // Vote of the validator @@ -26,7 +26,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall keeper.vs.IterateBondedValidatorsByPower(ctx, func(index int64, validator sdk.Validator) (stop bool) { currValidators[validator.GetOperator().String()] = validatorGovInfo{ Address: validator.GetOperator(), - Power: sdk.NewDecFromInt(validator.GetPower()), + Power: validator.GetPower(), DelegatorShares: validator.GetDelegatorShares(), Minus: sdk.ZeroDec(), Vote: OptionEmpty, @@ -57,7 +57,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall currValidators[valAddrStr] = val delegatorShare := delegation.GetShares().Quo(val.DelegatorShares) - votingPower := val.Power.Mul(delegatorShare) + votingPower := delegatorShare.MulInt(val.Power) results[vote.Option] = results[vote.Option].Add(votingPower) totalVotingPower = totalVotingPower.Add(votingPower) @@ -78,7 +78,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall sharesAfterMinus := val.DelegatorShares.Sub(val.Minus) percentAfterMinus := sharesAfterMinus.Quo(val.DelegatorShares) - votingPower := val.Power.Mul(percentAfterMinus) + votingPower := percentAfterMinus.MulInt(val.Power) results[val.Vote] = results[val.Vote].Add(votingPower) totalVotingPower = totalVotingPower.Add(votingPower) From a2b73c8ab4600c9130c5c5924b368d9e39e27884 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 4 Feb 2019 16:45:18 -0800 Subject: [PATCH 50/99] Merge PR #3460: Implement fee distribution RESTful endpoints --- PENDING.md | 5 + client/lcd/lcd_test.go | 98 +++++++ client/lcd/swagger-ui/swagger.yaml | 38 +-- client/lcd/test_helpers.go | 42 +++ client/rest/types.go | 14 +- cmd/gaia/cmd/gaiacli/main.go | 6 +- x/auth/client/rest/sign.go | 3 +- x/bank/client/rest/sendtx.go | 3 +- x/distribution/alias.go | 21 +- x/distribution/client/cli/query.go | 73 +----- x/distribution/client/cli/tx.go | 35 ++- x/distribution/client/common/common.go | 145 +++++++++++ x/distribution/client/common/common_test.go | 35 +++ .../{cli/util.go => common/pretty_params.go} | 2 +- x/distribution/client/module_client.go | 1 + x/distribution/client/rest/query.go | 244 ++++++++++++++++++ x/distribution/client/rest/rest.go | 13 + x/distribution/client/rest/tx.go | 222 ++++++++++++++++ x/distribution/keeper/querier.go | 96 ++++++- x/gov/client/rest/rest.go | 19 +- x/ibc/client/rest/transfer.go | 3 +- x/slashing/client/rest/tx.go | 3 +- x/staking/client/rest/tx.go | 21 +- 23 files changed, 985 insertions(+), 157 deletions(-) create mode 100644 x/distribution/client/common/common.go create mode 100644 x/distribution/client/common/common_test.go rename x/distribution/client/{cli/util.go => common/pretty_params.go} (98%) create mode 100644 x/distribution/client/rest/query.go create mode 100644 x/distribution/client/rest/rest.go create mode 100644 x/distribution/client/rest/tx.go diff --git a/PENDING.md b/PENDING.md index 0d8ad89ad..916e54358 100644 --- a/PENDING.md +++ b/PENDING.md @@ -6,6 +6,7 @@ BREAKING CHANGES * [\#3284](https://github.com/cosmos/cosmos-sdk/issues/3284) Rename the `name` field to `from` in the `base_req` body. * [\#3485](https://github.com/cosmos/cosmos-sdk/pull/3485) Error responses are now JSON objects. + * [\#3477][distribution] endpoint changed "all_delegation_rewards" -> "delegator_total_rewards" * Gaia CLI (`gaiacli`) - [#3399](https://github.com/cosmos/cosmos-sdk/pull/3399) Add `gaiad validate-genesis` command to facilitate checking of genesis files @@ -19,6 +20,7 @@ BREAKING CHANGES * SDK * [\#3487](https://github.com/cosmos/cosmos-sdk/pull/3487) Move HTTP/REST utilities out of client/utils into a new dedicated client/rest package. + * [\#3490](https://github.com/cosmos/cosmos-sdk/issues/3490) ReadRESTReq() returns bool to avoid callers to write error responses twice. * Tendermint @@ -26,6 +28,7 @@ BREAKING CHANGES FEATURES * Gaia REST API + * [\#2358](https://github.com/cosmos/cosmos-sdk/issues/2358) Add distribution module REST interface * Gaia CLI (`gaiacli`) * [\#3429](https://github.com/cosmos/cosmos-sdk/issues/3429) Support querying @@ -38,6 +41,7 @@ FEATURES * SDK * \#3270 [x/staking] limit number of ongoing unbonding delegations /redelegations per pair/trio + * [\#3477][distribution] new query endpoint "delegator_validators" * Tendermint @@ -54,6 +58,7 @@ IMPROVEMENTS (auto gas) to work with generate only. * Gaia CLI (`gaiacli`) + * [\#3476](https://github.com/cosmos/cosmos-sdk/issues/3476) New `withdraw-all-rewards` command to withdraw all delegations rewards for delegators. * Gaia * [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index d9e99c0e5..0a2e72b05 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -26,6 +26,8 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" "github.com/cosmos/cosmos-sdk/x/bank" + dclcommon "github.com/cosmos/cosmos-sdk/x/distribution/client/common" + distrrest "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" @@ -918,3 +920,99 @@ func TestSlashingGetParams(t *testing.T) { err := cdc.UnmarshalJSON([]byte(body), ¶ms) require.NoError(t, err) } + +func TestDistributionGetParams(t *testing.T) { + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + + res, body := Request(t, port, "GET", "/distribution/parameters", nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &dclcommon.PrettyParams{})) +} + +func TestDistributionFlow(t *testing.T) { + addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) + //addr2, seed2 = CreateAddr(t, name2, pw, GetKeyBase(t)) + cleanup, _, valAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + defer cleanup() + + valAddr := valAddrs[0] + operAddr := sdk.AccAddress(valAddr) + + var rewards sdk.DecCoins + res, body := Request(t, port, "GET", fmt.Sprintf("/distribution/outstanding_rewards"), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.Equal(t, sdk.DecCoins(nil), rewards) + + var valDistInfo distrrest.ValidatorDistInfo + res, body = Request(t, port, "GET", "/distribution/validators/"+valAddr.String(), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &valDistInfo)) + require.Equal(t, valDistInfo.OperatorAddress.String(), sdk.AccAddress(valAddr).String()) + require.Equal(t, valDistInfo.ValidatorCommission, sdk.DecCoins(nil)) + require.Equal(t, valDistInfo.SelfBondRewards, sdk.DecCoins(nil)) + + // Delegate some coins + resultTx := doDelegate(t, port, name1, pw, addr, valAddr, 60, fees) + tests.WaitForHeight(resultTx.Height+1, port) + require.Equal(t, uint32(0), resultTx.CheckTx.Code) + require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + + // send some coins + _, resultTx = doTransfer(t, port, seed, name1, memo, pw, addr, fees) + tests.WaitForHeight(resultTx.Height+5, port) + require.Equal(t, uint32(0), resultTx.CheckTx.Code) + require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + + // Query outstanding rewards changed + oustandingRewards := mustParseDecCoins("9.80stake") + res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/outstanding_rewards"), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.Equal(t, oustandingRewards, rewards) + + // Query validator distribution info + res, body = Request(t, port, "GET", "/distribution/validators/"+valAddr.String(), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + valRewards := mustParseDecCoins("6.125stake") + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &valDistInfo)) + require.Equal(t, valRewards, valDistInfo.SelfBondRewards) + + // Query validator's rewards + res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/validators/%s/rewards", valAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.Equal(t, valRewards, rewards) + + // Query self-delegation + res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/delegators/%s/rewards/%s", operAddr, valAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.Equal(t, valRewards, rewards) + + // Query delegation + res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/delegators/%s/rewards/%s", addr, valAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.Equal(t, mustParseDecCoins("3.675stake"), rewards) + + // Query delegator's rewards total + res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/delegators/%s/rewards", operAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &rewards)) + require.Equal(t, valRewards, rewards) + + // Query delegator's withdrawal address + var withdrawAddr string + res, body = Request(t, port, "GET", fmt.Sprintf("/distribution/delegators/%s/withdraw_address", operAddr), nil) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.NoError(t, cdc.UnmarshalJSON([]byte(body), &withdrawAddr)) + require.Equal(t, operAddr.String(), withdrawAddr) + + // Withdraw delegator's rewards + resultTx = doWithdrawDelegatorAllRewards(t, port, seed, name1, pw, addr, fees) + require.Equal(t, uint32(0), resultTx.CheckTx.Code) + require.Equal(t, uint32(0), resultTx.DeliverTx.Code) +} diff --git a/client/lcd/swagger-ui/swagger.yaml b/client/lcd/swagger-ui/swagger.yaml index 363345917..38e9c1864 100644 --- a/client/lcd/swagger-ui/swagger.yaml +++ b/client/lcd/swagger-ui/swagger.yaml @@ -18,7 +18,7 @@ tags: - name: ICS23 description: Slashing module APIs - name: ICS24 - description: WIP - Fee distribution module APIs + description: Fee distribution module APIs - name: version description: Query app version schemes: @@ -1846,9 +1846,9 @@ paths: type: string 500: description: Internal Server Error - /distribution/pool: + /distribution/outstanding_rewards: get: - summary: Fee distribution pool + summary: Fee distribution outstanding rewards tags: - ICS24 produces: @@ -1857,7 +1857,9 @@ paths: 200: description: OK schema: - $ref: "#/definitions/FeePool" + type: array + items: + $ref: "#/definitions/Coin" 500: description: Internal Server Error definitions: @@ -2198,7 +2200,7 @@ definitions: power: type: string example: "1000" - accum: + proposer_priority: type: string example: "1000" TextProposal: @@ -2367,36 +2369,12 @@ definitions: type: string shares_dst: type: string - FeePool: - type: object - properties: - community_pool: - type: array - items: - $ref: "#/definitions/Coin" - val_accum: - $ref: "#/definitions/TotalAccum" - val_pool: - type: array - items: - $ref: "#/definitions/Coin" - TotalAccum: - type: object - properties: - update_height: - type: integer - accum: - type: string ValidatorDistInfo: type: object properties: operator_addr: $ref: "#/definitions/ValidatorAddress" - fee_pool_withdrawal_height: - type: integer - del_accum: - $ref: "#/definitions/TotalAccum" - del_pool: + self_bond_rewards: type: array items: $ref: "#/definitions/Coin" diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index b420215eb..efef932a6 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -60,6 +60,8 @@ import ( authRest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bankRest "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + distrRest "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" govRest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" slashingRest "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" stakingRest "github.com/cosmos/cosmos-sdk/x/staking/client/rest" @@ -294,6 +296,11 @@ func InitializeTestLCD( genesisState.StakingData.Pool.NotBondedTokens = genesisState.StakingData.Pool.NotBondedTokens.Add(sdk.NewInt(100)) } + inflationMin := sdk.MustNewDecFromStr("10000.0") + genesisState.MintData.Minter.Inflation = inflationMin + genesisState.MintData.Params.InflationMax = sdk.MustNewDecFromStr("15000.0") + genesisState.MintData.Params.InflationMin = inflationMin + appState, err := codec.MarshalJSONIndent(cdc, genesisState) require.NoError(t, err) genDoc.AppState = appState @@ -390,6 +397,7 @@ func registerRoutes(rs *RestServer) { tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) authRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, auth.StoreKey) bankRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + distrRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, distr.StoreKey) stakingRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) slashingRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) govRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) @@ -1382,3 +1390,37 @@ func doUnjail(t *testing.T, port, seed, name, password string, type unjailReq struct { BaseReq rest.BaseReq `json:"base_req"` } + +// ICS24 - fee distribution + +// POST /distribution/delegators/{delgatorAddr}/rewards Withdraw delegator rewards +func doWithdrawDelegatorAllRewards(t *testing.T, port, seed, name, password string, + delegatorAddr sdk.AccAddress, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) { + // get the account to get the sequence + acc := getAccount(t, port, delegatorAddr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + chainID := viper.GetString(client.FlagChainID) + baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) + + wr := struct { + BaseReq rest.BaseReq `json:"base_req"` + }{BaseReq: baseReq} + + req := cdc.MustMarshalJSON(wr) + res, body := Request(t, port, "POST", fmt.Sprintf("/distribution/delegators/%s/rewards", delegatorAddr), req) + require.Equal(t, http.StatusOK, res.StatusCode, body) + + var results ctypes.ResultBroadcastTxCommit + cdc.MustUnmarshalJSON([]byte(body), &results) + + return results +} + +func mustParseDecCoins(dcstring string) sdk.DecCoins { + dcoins, err := sdk.ParseDecCoins(dcstring) + if err != nil { + panic(err) + } + return dcoins +} diff --git a/client/rest/types.go b/client/rest/types.go index 113dc0650..cb409627d 100644 --- a/client/rest/types.go +++ b/client/rest/types.go @@ -98,7 +98,7 @@ func (br BaseReq) ValidateBasic(w http.ResponseWriter) bool { /* ReadRESTReq is a simple convenience wrapper that reads the body and -unmarshals to the req interface. +unmarshals to the req interface. Returns false if errors occurred. Usage: type SomeReq struct { @@ -107,20 +107,22 @@ unmarshals to the req interface. } req := new(SomeReq) - err := ReadRESTReq(w, r, cdc, req) + if ok := ReadRESTReq(w, r, cdc, req); !ok { + return + } */ -func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) error { +func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req interface{}) bool { body, err := ioutil.ReadAll(r.Body) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, err.Error()) - return err + return false } err = cdc.UnmarshalJSON(body, req) if err != nil { WriteErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("failed to decode JSON payload: %s", err)) - return err + return false } - return nil + return true } diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 3042c35d6..3065ee64c 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -25,7 +25,7 @@ import ( at "github.com/cosmos/cosmos-sdk/x/auth" auth "github.com/cosmos/cosmos-sdk/x/auth/client/rest" bank "github.com/cosmos/cosmos-sdk/x/bank/client/rest" - dist "github.com/cosmos/cosmos-sdk/x/distribution" + dist "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" gv "github.com/cosmos/cosmos-sdk/x/gov" gov "github.com/cosmos/cosmos-sdk/x/gov/client/rest" sl "github.com/cosmos/cosmos-sdk/x/slashing" @@ -35,6 +35,7 @@ import ( authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli" bankcmd "github.com/cosmos/cosmos-sdk/x/bank/client/cli" + distcmd "github.com/cosmos/cosmos-sdk/x/distribution" distClient "github.com/cosmos/cosmos-sdk/x/distribution/client" govClient "github.com/cosmos/cosmos-sdk/x/gov/client" slashingClient "github.com/cosmos/cosmos-sdk/x/slashing/client" @@ -65,7 +66,7 @@ func main() { // TODO: Make the lcd command take a list of ModuleClient mc := []sdk.ModuleClients{ govClient.NewModuleClient(gv.StoreKey, cdc), - distClient.NewModuleClient(dist.StoreKey, cdc), + distClient.NewModuleClient(distcmd.StoreKey, cdc), stakingClient.NewModuleClient(st.StoreKey, cdc), slashingClient.NewModuleClient(sl.StoreKey, cdc), } @@ -161,6 +162,7 @@ func registerRoutes(rs *lcd.RestServer) { tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) auth.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, at.StoreKey) bank.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + dist.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, distcmd.StoreKey) staking.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) slashing.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) gov.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) diff --git a/x/auth/client/rest/sign.go b/x/auth/client/rest/sign.go index 50ec66055..67572eefe 100644 --- a/x/auth/client/rest/sign.go +++ b/x/auth/client/rest/sign.go @@ -26,8 +26,7 @@ func SignTxRequestHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Ha return func(w http.ResponseWriter, r *http.Request) { var m SignBody - if err := rest.ReadRESTReq(w, r, cdc, &m); err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &m) { return } diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index c53e1431a..2e7cbbdd9 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -44,8 +44,7 @@ func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIC } var req sendReq - err = rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { + if !rest.ReadRESTReq(w, r, cdc, &req) { return } diff --git a/x/distribution/alias.go b/x/distribution/alias.go index d050eddad..85b93fc17 100644 --- a/x/distribution/alias.go +++ b/x/distribution/alias.go @@ -23,9 +23,10 @@ type ( FeeCollectionKeeper = types.FeeCollectionKeeper // querier param types - QueryValidatorCommissionParams = keeper.QueryValidatorCommissionParams - QueryValidatorSlashesParams = keeper.QueryValidatorSlashesParams - QueryDelegationRewardsParams = keeper.QueryDelegationRewardsParams + QueryValidatorCommissionParams = keeper.QueryValidatorCommissionParams + QueryValidatorSlashesParams = keeper.QueryValidatorSlashesParams + QueryDelegationRewardsParams = keeper.QueryDelegationRewardsParams + QueryDelegatorWithdrawAddrParams = keeper.QueryDelegatorWithdrawAddrParams ) const ( @@ -49,12 +50,14 @@ var ( NewMsgWithdrawDelegatorReward = types.NewMsgWithdrawDelegatorReward NewMsgWithdrawValidatorCommission = types.NewMsgWithdrawValidatorCommission - NewKeeper = keeper.NewKeeper - NewQuerier = keeper.NewQuerier - NewQueryValidatorCommissionParams = keeper.NewQueryValidatorCommissionParams - NewQueryValidatorSlashesParams = keeper.NewQueryValidatorSlashesParams - NewQueryDelegationRewardsParams = keeper.NewQueryDelegationRewardsParams - DefaultParamspace = keeper.DefaultParamspace + NewKeeper = keeper.NewKeeper + NewQuerier = keeper.NewQuerier + NewQueryValidatorCommissionParams = keeper.NewQueryValidatorCommissionParams + NewQueryValidatorSlashesParams = keeper.NewQueryValidatorSlashesParams + NewQueryDelegationRewardsParams = keeper.NewQueryDelegationRewardsParams + NewQueryDelegatorParams = keeper.NewQueryDelegatorParams + NewQueryDelegatorWithdrawAddrParams = keeper.NewQueryDelegatorWithdrawAddrParams + DefaultParamspace = keeper.DefaultParamspace RegisterCodec = types.RegisterCodec DefaultGenesisState = types.DefaultGenesisState diff --git a/x/distribution/client/cli/query.go b/x/distribution/client/cli/query.go index 4f4eb9390..2a23a56b9 100644 --- a/x/distribution/client/cli/query.go +++ b/x/distribution/client/cli/query.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" distr "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) @@ -21,34 +22,10 @@ func GetCmdQueryParams(queryRoute string, cdc *codec.Codec) *cobra.Command { Short: "Query distribution params", RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - - route := fmt.Sprintf("custom/%s/params/community_tax", queryRoute) - retCommunityTax, err := cliCtx.QueryWithData(route, []byte{}) + params, err := common.QueryParams(cliCtx, queryRoute) if err != nil { return err } - - route = fmt.Sprintf("custom/%s/params/base_proposer_reward", queryRoute) - retBaseProposerReward, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return err - } - - route = fmt.Sprintf("custom/%s/params/bonus_proposer_reward", queryRoute) - retBonusProposerReward, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return err - } - - route = fmt.Sprintf("custom/%s/params/withdraw_addr_enabled", queryRoute) - retWithdrawAddrEnabled, err := cliCtx.QueryWithData(route, []byte{}) - if err != nil { - return err - } - - params := NewPrettyParams(retCommunityTax, retBaseProposerReward, - retBonusProposerReward, retWithdrawAddrEnabled) - return cliCtx.PrintOutput(params) }, } @@ -90,13 +67,7 @@ func GetCmdQueryValidatorCommission(queryRoute string, cdc *codec.Codec) *cobra. return err } - bz, err := cdc.MarshalJSON(distr.NewQueryValidatorCommissionParams(validatorAddr)) - if err != nil { - return err - } - - route := fmt.Sprintf("custom/%s/validator_commission", queryRoute) - res, err := cliCtx.QueryWithData(route, bz) + res, err := common.QueryValidatorCommission(cliCtx, cdc, queryRoute, validatorAddr) if err != nil { return err } @@ -159,42 +130,20 @@ func GetCmdQueryDelegatorRewards(queryRoute string, cdc *codec.Codec) *cobra.Com RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) - delegatorAddr, err := sdk.AccAddressFromBech32(args[0]) - if err != nil { - return err - } - - var ( - route string - params distr.QueryDelegationRewardsParams - result sdk.DecCoins - ) - - if len(args) == 1 { - // query for all rewards - params = distr.NewQueryDelegationRewardsParams(delegatorAddr, nil) - route = fmt.Sprintf("custom/%s/all_delegation_rewards", queryRoute) + var resp []byte + var err error + if len(args) == 2 { + // query for rewards from a particular delegation + resp, err = common.QueryDelegationRewards(cliCtx, cdc, queryRoute, args[0], args[1]) } else { - // query for rewards from a particular validator - validatorAddr, err := sdk.ValAddressFromBech32(args[1]) - if err != nil { - return err - } - - params = distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr) - route = fmt.Sprintf("custom/%s/delegation_rewards", queryRoute) + // query for delegator total rewards + resp, err = common.QueryDelegatorTotalRewards(cliCtx, cdc, queryRoute, args[0]) } - - bz, err := cdc.MarshalJSON(params) - if err != nil { - return err - } - - resp, err := cliCtx.QueryWithData(route, bz) if err != nil { return err } + var result sdk.DecCoins cdc.MustUnmarshalJSON(resp, &result) return cliCtx.PrintOutput(result) }, diff --git a/x/distribution/client/cli/tx.go b/x/distribution/client/cli/tx.go index 9a761d8de..21a3bb4c3 100644 --- a/x/distribution/client/cli/tx.go +++ b/x/distribution/client/cli/tx.go @@ -16,6 +16,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" + "github.com/cosmos/cosmos-sdk/x/distribution/client/common" "github.com/cosmos/cosmos-sdk/x/distribution/types" ) @@ -89,7 +90,39 @@ func GetCmdWithdrawRewards(cdc *codec.Codec) *cobra.Command { return cmd } -// GetCmdDelegate implements the delegate command. +// command to withdraw all rewards +func GetCmdWithdrawAllRewards(cdc *codec.Codec, queryRoute string) *cobra.Command { + cmd := &cobra.Command{ + Use: "withdraw-all-rewards [delegator-addr]", + Short: "withdraw all delegations rewards for a delegator", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + + txBldr := authtxb.NewTxBuilderFromCLI().WithTxEncoder(utils.GetTxEncoder(cdc)) + cliCtx := context.NewCLIContext(). + WithCodec(cdc). + WithAccountDecoder(cdc) + + delAddr := cliCtx.GetFromAddress() + msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, cdc, queryRoute, delAddr) + if err != nil { + return err + } + + if cliCtx.GenerateOnly { + return utils.PrintUnsignedStdTx(os.Stdout, txBldr, cliCtx, msgs, false) + } + + // build and sign the transaction, then broadcast to Tendermint + return utils.CompleteAndBroadcastTxCLI(txBldr, cliCtx, msgs) + }, + } + cmd.Flags().String(flagOnlyFromValidator, "", "only withdraw from this validator address (in bech)") + cmd.Flags().Bool(flagIsValidator, false, "also withdraw validator's commission") + return cmd +} + +// command to replace a delegator's withdrawal address func GetCmdSetWithdrawAddr(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "set-withdraw-addr [withdraw-addr]", diff --git a/x/distribution/client/common/common.go b/x/distribution/client/common/common.go new file mode 100644 index 000000000..de78a9bf2 --- /dev/null +++ b/x/distribution/client/common/common.go @@ -0,0 +1,145 @@ +package common + +import ( + "fmt" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + distr "github.com/cosmos/cosmos-sdk/x/distribution" +) + +// QueryParams actually queries distribution params. +func QueryParams(cliCtx context.CLIContext, queryRoute string) (PrettyParams, error) { + route := fmt.Sprintf("custom/%s/params/community_tax", queryRoute) + + retCommunityTax, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + route = fmt.Sprintf("custom/%s/params/base_proposer_reward", queryRoute) + retBaseProposerReward, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + route = fmt.Sprintf("custom/%s/params/bonus_proposer_reward", queryRoute) + retBonusProposerReward, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + route = fmt.Sprintf("custom/%s/params/withdraw_addr_enabled", queryRoute) + retWithdrawAddrEnabled, err := cliCtx.QueryWithData(route, []byte{}) + if err != nil { + return PrettyParams{}, err + } + + return NewPrettyParams(retCommunityTax, retBaseProposerReward, + retBonusProposerReward, retWithdrawAddrEnabled), nil +} + +// QueryDelegatorTotalRewards queries delegator total rewards. +func QueryDelegatorTotalRewards(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute, delAddr string) ([]byte, error) { + + delegatorAddr, err := sdk.AccAddressFromBech32(delAddr) + if err != nil { + return nil, err + } + + return cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/delegator_total_rewards", queryRoute), + cdc.MustMarshalJSON(distr.NewQueryDelegatorParams(delegatorAddr)), + ) +} + +// QueryDelegationRewards queries a delegation rewards. +func QueryDelegationRewards(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute, delAddr, valAddr string) ([]byte, error) { + + delegatorAddr, err := sdk.AccAddressFromBech32(delAddr) + if err != nil { + return nil, err + } + validatorAddr, err := sdk.ValAddressFromBech32(valAddr) + if err != nil { + return nil, err + } + + return cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/delegation_rewards", queryRoute), + cdc.MustMarshalJSON(distr.NewQueryDelegationRewardsParams(delegatorAddr, validatorAddr)), + ) +} + +// QueryDelegatorValidators returns delegator's list of validators +// it submitted delegations to. +func QueryDelegatorValidators(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string, delegatorAddr sdk.AccAddress) ([]byte, error) { + + return cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/delegator_validators", queryRoute), + cdc.MustMarshalJSON(distr.NewQueryDelegatorParams(delegatorAddr)), + ) +} + +// QueryValidatorCommission returns a validator's commission. +func QueryValidatorCommission(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string, validatorAddr sdk.ValAddress) ([]byte, error) { + + return cliCtx.QueryWithData( + fmt.Sprintf("custom/%s/validator_commission", queryRoute), + cdc.MustMarshalJSON(distr.NewQueryValidatorCommissionParams(validatorAddr)), + ) +} + +// WithdrawAllDelegatorRewards builds a multi-message slice to be used +// to withdraw all delegations rewards for the given delegator. +func WithdrawAllDelegatorRewards(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string, delegatorAddr sdk.AccAddress) ([]sdk.Msg, error) { + + // retrieve the comprehensive list of all validators which the + // delegator had submitted delegations to + bz, err := QueryDelegatorValidators(cliCtx, cdc, queryRoute, delegatorAddr) + if err != nil { + return nil, err + } + + var validators []sdk.ValAddress + if err := cdc.UnmarshalJSON(bz, &validators); err != nil { + return nil, err + } + + // build multi-message transaction + var msgs []sdk.Msg + for _, valAddr := range validators { + msg := distr.NewMsgWithdrawDelegatorReward(delegatorAddr, valAddr) + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + msgs = append(msgs, msg) + } + + return msgs, nil +} + +// WithdrawValidatorRewardsAndCommission builds a two-message message slice to be +// used to withdraw both validation's commission and self-delegation reward. +func WithdrawValidatorRewardsAndCommission(validatorAddr sdk.ValAddress) ([]sdk.Msg, error) { + + commissionMsg := distr.NewMsgWithdrawValidatorCommission(validatorAddr) + if err := commissionMsg.ValidateBasic(); err != nil { + return nil, err + } + + // build and validate MsgWithdrawDelegatorReward + rewardMsg := distr.NewMsgWithdrawDelegatorReward( + sdk.AccAddress(validatorAddr.Bytes()), validatorAddr) + if err := rewardMsg.ValidateBasic(); err != nil { + return nil, err + } + + return []sdk.Msg{commissionMsg, rewardMsg}, nil +} diff --git a/x/distribution/client/common/common_test.go b/x/distribution/client/common/common_test.go new file mode 100644 index 000000000..622326232 --- /dev/null +++ b/x/distribution/client/common/common_test.go @@ -0,0 +1,35 @@ +package common + +import ( + "testing" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/stretchr/testify/require" +) + +func TestQueryDelegationRewardsAddrValidation(t *testing.T) { + cdc := codec.New() + ctx := context.NewCLIContext().WithCodec(cdc) + type args struct { + delAddr string + valAddr string + } + tests := []struct { + name string + args args + want []byte + wantErr bool + }{ + {"invalid delegator address", args{"invalid", ""}, nil, true}, + {"empty delegator address", args{"", ""}, nil, true}, + {"invalid validator address", args{"cosmos1zxcsu7l5qxs53lvp0fqgd09a9r2g6kqrk2cdpa", "invalid"}, nil, true}, + {"empty validator address", args{"cosmos1zxcsu7l5qxs53lvp0fqgd09a9r2g6kqrk2cdpa", ""}, nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := QueryDelegationRewards(ctx, cdc, "", tt.args.delAddr, tt.args.valAddr) + require.True(t, err != nil, tt.wantErr) + }) + } +} diff --git a/x/distribution/client/cli/util.go b/x/distribution/client/common/pretty_params.go similarity index 98% rename from x/distribution/client/cli/util.go rename to x/distribution/client/common/pretty_params.go index 06e24a6b7..cbe22869b 100644 --- a/x/distribution/client/cli/util.go +++ b/x/distribution/client/common/pretty_params.go @@ -1,4 +1,4 @@ -package cli +package common import ( "encoding/json" diff --git a/x/distribution/client/module_client.go b/x/distribution/client/module_client.go index 412bf9d6a..b9dcb1c82 100644 --- a/x/distribution/client/module_client.go +++ b/x/distribution/client/module_client.go @@ -46,6 +46,7 @@ func (mc ModuleClient) GetTxCmd() *cobra.Command { distTxCmd.AddCommand(client.PostCommands( distCmds.GetCmdWithdrawRewards(mc.cdc), distCmds.GetCmdSetWithdrawAddr(mc.cdc), + distCmds.GetCmdWithdrawAllRewards(mc.cdc, mc.storeKey), )...) return distTxCmd diff --git a/x/distribution/client/rest/query.go b/x/distribution/client/rest/query.go new file mode 100644 index 000000000..6f7730407 --- /dev/null +++ b/x/distribution/client/rest/query.go @@ -0,0 +1,244 @@ +package rest + +import ( + "fmt" + "net/http" + + "github.com/cosmos/cosmos-sdk/x/distribution" + "github.com/cosmos/cosmos-sdk/x/distribution/client/common" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/rest" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/gorilla/mux" +) + +func registerQueryRoutes(cliCtx context.CLIContext, r *mux.Router, + cdc *codec.Codec, queryRoute string) { + + // Get the total rewards balance from all delegations + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/rewards", + delegatorRewardsHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") + + // Query a delegation reward + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", + delegationRewardsHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") + + // Get the rewards withdrawal address + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/withdraw_address", + delegatorWithdrawalAddrHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") + + // Validator distribution information + r.HandleFunc( + "/distribution/validators/{validatorAddr}", + validatorInfoHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") + + // Commission and self-delegation rewards of a single a validator + r.HandleFunc( + "/distribution/validators/{validatorAddr}/rewards", + validatorRewardsHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") + + // Get the current distribution parameter values + r.HandleFunc( + "/distribution/parameters", + paramsHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") + + // Get the current distribution pool + r.HandleFunc( + "/distribution/outstanding_rewards", + outstandingRewardsHandlerFn(cliCtx, cdc, queryRoute), + ).Methods("GET") +} + +// HTTP request handler to query the total rewards balance from all delegations +func delegatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + // query for rewards from a particular delegator + res, ok := checkResponseQueryDelegatorTotalRewards(w, cliCtx, cdc, queryRoute, + mux.Vars(r)["delegatorAddr"]) + if !ok { + return + } + + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} + +// HTTP request handler to query a delegation rewards +func delegationRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + // query for rewards from a particular delegation + res, ok := checkResponseQueryDelegationRewards(w, cliCtx, cdc, queryRoute, + mux.Vars(r)["delegatorAddr"], mux.Vars(r)["validatorAddr"]) + if !ok { + return + } + + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} + +// HTTP request handler to query a delegation rewards +func delegatorWithdrawalAddrHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + delegatorAddr, ok := checkDelegatorAddressVar(w, r) + if !ok { + return + } + + bz := cdc.MustMarshalJSON(distribution.NewQueryDelegatorWithdrawAddrParams(delegatorAddr)) + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/withdraw_addr", queryRoute), bz) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} + +// ValidatorDistInfo defines the properties of +// validator distribution information response. +type ValidatorDistInfo struct { + OperatorAddress sdk.AccAddress `json:"operator_addr"` + SelfBondRewards sdk.DecCoins `json:"self_bond_rewards"` + ValidatorCommission types.ValidatorAccumulatedCommission `json:"val_commission"` +} + +// NewValidatorDistInfo creates a new instance of ValidatorDistInfo. +func NewValidatorDistInfo(operatorAddr sdk.AccAddress, rewards sdk.DecCoins, + commission types.ValidatorAccumulatedCommission) ValidatorDistInfo { + return ValidatorDistInfo{ + OperatorAddress: operatorAddr, + SelfBondRewards: rewards, + ValidatorCommission: commission, + } +} + +// HTTP request handler to query validator's distribution information +func validatorInfoHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + valAddr := mux.Vars(r)["validatorAddr"] + validatorAddr, ok := checkValidatorAddressVar(w, r) + if !ok { + return + } + + // query commission + commissionRes, err := common.QueryValidatorCommission(cliCtx, cdc, queryRoute, validatorAddr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + var valCom types.ValidatorAccumulatedCommission + cdc.MustUnmarshalJSON(commissionRes, &valCom) + + // self bond rewards + delAddr := sdk.AccAddress(validatorAddr) + rewardsRes, ok := checkResponseQueryDelegationRewards(w, cliCtx, cdc, queryRoute, + delAddr.String(), valAddr) + if !ok { + return + } + + var rewards sdk.DecCoins + cdc.MustUnmarshalJSON(rewardsRes, &rewards) + + // Prepare response + res := cdc.MustMarshalJSON(NewValidatorDistInfo(delAddr, rewards, valCom)) + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} + +// HTTP request handler to query validator's commission and self-delegation rewards +func validatorRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + valAddr := mux.Vars(r)["validatorAddr"] + validatorAddr, ok := checkValidatorAddressVar(w, r) + if !ok { + return + } + + delAddr := sdk.AccAddress(validatorAddr).String() + res, ok := checkResponseQueryDelegationRewards(w, cliCtx, cdc, queryRoute, delAddr, valAddr) + if !ok { + return + } + + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} + +// HTTP request handler to query the distribution params values +func paramsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + params, err := common.QueryParams(cliCtx, queryRoute) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + rest.PostProcessResponse(w, cdc, params, cliCtx.Indent) + } +} + +// HTTP request handler to query the outstanding rewards +func outstandingRewardsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute string) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + res, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/outstanding_rewards", queryRoute), []byte{}) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + rest.PostProcessResponse(w, cdc, res, cliCtx.Indent) + } +} + +func checkResponseQueryDelegatorTotalRewards(w http.ResponseWriter, cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute, delAddr string) (res []byte, ok bool) { + + res, err := common.QueryDelegatorTotalRewards(cliCtx, cdc, queryRoute, delAddr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return nil, false + } + + return res, true +} + +func checkResponseQueryDelegationRewards(w http.ResponseWriter, cliCtx context.CLIContext, cdc *codec.Codec, + queryRoute, delAddr, valAddr string) (res []byte, ok bool) { + + res, err := common.QueryDelegationRewards(cliCtx, cdc, queryRoute, delAddr, valAddr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return nil, false + } + + return res, true +} diff --git a/x/distribution/client/rest/rest.go b/x/distribution/client/rest/rest.go new file mode 100644 index 000000000..769e7360e --- /dev/null +++ b/x/distribution/client/rest/rest.go @@ -0,0 +1,13 @@ +package rest + +import ( + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/gorilla/mux" +) + +// RegisterRoutes register distribution REST routes. +func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *codec.Codec, queryRoute string) { + registerQueryRoutes(cliCtx, r, cdc, queryRoute) + registerTxRoutes(cliCtx, r, cdc, queryRoute) +} diff --git a/x/distribution/client/rest/tx.go b/x/distribution/client/rest/tx.go new file mode 100644 index 000000000..8e5bff39f --- /dev/null +++ b/x/distribution/client/rest/tx.go @@ -0,0 +1,222 @@ +package rest + +import ( + "net/http" + + "github.com/cosmos/cosmos-sdk/client/rest" + + "github.com/gorilla/mux" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/x/distribution/client/common" + "github.com/cosmos/cosmos-sdk/x/distribution/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func registerTxRoutes(cliCtx context.CLIContext, r *mux.Router, + cdc *codec.Codec, queryRoute string) { + + // Withdraw all delegator rewards + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/rewards", + withdrawDelegatorRewardsHandlerFn(cdc, cliCtx, queryRoute), + ).Methods("POST") + + // Withdraw delegation rewards + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/rewards/{validatorAddr}", + withdrawDelegationRewardsHandlerFn(cdc, cliCtx), + ).Methods("POST") + + // Replace the rewards withdrawal address + r.HandleFunc( + "/distribution/delegators/{delegatorAddr}/withdraw_address", + setDelegatorWithdrawalAddrHandlerFn(cdc, cliCtx), + ).Methods("POST") + + // Withdraw validator rewards and commission + r.HandleFunc( + "/distribution/validators/{validatorAddr}/rewards", + withdrawValidatorRewardsHandlerFn(cdc, cliCtx), + ).Methods("POST") + +} + +type ( + withdrawRewardsReq struct { + BaseReq rest.BaseReq `json:"base_req"` + } + + setWithdrawalAddrReq struct { + BaseReq rest.BaseReq `json:"base_req"` + WithdrawAddress sdk.AccAddress `json:"withdraw_address"` + } +) + +// Withdraw delegator rewards +func withdrawDelegatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext, + queryRoute string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req withdrawRewardsReq + if !rest.ReadRESTReq(w, r, cdc, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + // read and validate URL's variables + delAddr, ok := checkDelegatorAddressVar(w, r) + if !ok { + return + } + + msgs, err := common.WithdrawAllDelegatorRewards(cliCtx, cdc, queryRoute, delAddr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + if req.BaseReq.GenerateOnly { + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs) + return + } + + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, msgs, cdc) + } +} + +// Withdraw delegation rewards +func withdrawDelegationRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req withdrawRewardsReq + + if !rest.ReadRESTReq(w, r, cdc, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + // read and validate URL's variables + delAddr, ok := checkDelegatorAddressVar(w, r) + if !ok { + return + } + + valAddr, ok := checkValidatorAddressVar(w, r) + if !ok { + return + } + + msg := types.NewMsgWithdrawDelegatorReward(delAddr, valAddr) + if err := msg.ValidateBasic(); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + if req.BaseReq.GenerateOnly { + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + return + } + + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + } +} + +// Replace the rewards withdrawal address +func setDelegatorWithdrawalAddrHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req setWithdrawalAddrReq + + if !rest.ReadRESTReq(w, r, cdc, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + // read and validate URL's variables + delAddr, ok := checkDelegatorAddressVar(w, r) + if !ok { + return + } + + msg := types.NewMsgSetWithdrawAddress(delAddr, req.WithdrawAddress) + if err := msg.ValidateBasic(); err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + if req.BaseReq.GenerateOnly { + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, []sdk.Msg{msg}) + return + } + + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, []sdk.Msg{msg}, cdc) + } +} + +// Withdraw validator rewards and commission +func withdrawValidatorRewardsHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req withdrawRewardsReq + + if !rest.ReadRESTReq(w, r, cdc, &req) { + return + } + + req.BaseReq = req.BaseReq.Sanitize() + if !req.BaseReq.ValidateBasic(w) { + return + } + + // read and validate URL's variable + valAddr, ok := checkValidatorAddressVar(w, r) + if !ok { + return + } + + // prepare multi-message transaction + msgs, err := common.WithdrawValidatorRewardsAndCommission(valAddr) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + if req.BaseReq.GenerateOnly { + rest.WriteGenerateStdTxResponse(w, cdc, cliCtx, req.BaseReq, msgs) + return + } + + rest.CompleteAndBroadcastTxREST(w, r, cliCtx, req.BaseReq, msgs, cdc) + } +} + +// Auxiliary + +func checkDelegatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.AccAddress, bool) { + addr, err := sdk.AccAddressFromBech32(mux.Vars(r)["delegatorAddr"]) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return nil, false + } + return addr, true +} + +func checkValidatorAddressVar(w http.ResponseWriter, r *http.Request) (sdk.ValAddress, bool) { + addr, err := sdk.ValAddressFromBech32(mux.Vars(r)["validatorAddr"]) + if err != nil { + rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return nil, false + } + return addr, true +} diff --git a/x/distribution/keeper/querier.go b/x/distribution/keeper/querier.go index 619f2c39e..d8948f2fb 100644 --- a/x/distribution/keeper/querier.go +++ b/x/distribution/keeper/querier.go @@ -12,12 +12,14 @@ import ( // nolint const ( - QueryParams = "params" - QueryOutstandingRewards = "outstanding_rewards" - QueryValidatorCommission = "validator_commission" - QueryValidatorSlashes = "validator_slashes" - QueryDelegationRewards = "delegation_rewards" - QueryAllDelegationRewards = "all_delegation_rewards" + QueryParams = "params" + QueryOutstandingRewards = "outstanding_rewards" + QueryValidatorCommission = "validator_commission" + QueryValidatorSlashes = "validator_slashes" + QueryDelegationRewards = "delegation_rewards" + QueryDelegatorTotalRewards = "delegator_total_rewards" + QueryDelegatorValidators = "delegator_validators" + QueryWithdrawAddr = "withdraw_addr" ParamCommunityTax = "community_tax" ParamBaseProposerReward = "base_proposer_reward" @@ -43,8 +45,14 @@ func NewQuerier(k Keeper) sdk.Querier { case QueryDelegationRewards: return queryDelegationRewards(ctx, path[1:], req, k) - case QueryAllDelegationRewards: - return queryAllDelegationRewards(ctx, path[1:], req, k) + case QueryDelegatorTotalRewards: + return queryDelegatorTotalRewards(ctx, path[1:], req, k) + + case QueryDelegatorValidators: + return queryDelegatorValidators(ctx, path[1:], req, k) + + case QueryWithdrawAddr: + return queryDelegatorWithdrawAddress(ctx, path[1:], req, k) default: return nil, sdk.ErrUnknownRequest("unknown distr query endpoint") @@ -190,8 +198,20 @@ func queryDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, return bz, nil } -func queryAllDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { - var params QueryDelegationRewardsParams +// params for query 'custom/distr/delegator_total_rewards' and 'custom/distr/delegator_validators' +type QueryDelegatorParams struct { + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` +} + +// creates a new instance of QueryDelegationRewardsParams +func NewQueryDelegatorParams(delegatorAddr sdk.AccAddress) QueryDelegatorParams { + return QueryDelegatorParams{ + DelegatorAddr: delegatorAddr, + } +} + +func queryDelegatorTotalRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params QueryDelegatorParams err := k.cdc.UnmarshalJSON(req.Data, ¶ms) if err != nil { return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) @@ -221,3 +241,59 @@ func queryAllDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuer return bz, nil } + +func queryDelegatorValidators(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params QueryDelegatorParams + err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + // cache-wrap context as to not persist state changes during querying + ctx, _ = ctx.CacheContext() + + var validators []sdk.ValAddress + + k.stakingKeeper.IterateDelegations( + ctx, params.DelegatorAddr, + func(_ int64, del sdk.Delegation) (stop bool) { + validators = append(validators[:], del.GetValidatorAddr()) + return false + }, + ) + + bz, err := codec.MarshalJSONIndent(k.cdc, validators) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + return bz, nil +} + +// params for query 'custom/distr/withdraw_addr' +type QueryDelegatorWithdrawAddrParams struct { + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` +} + +// NewQueryDelegatorWithdrawAddrParams creates a new instance of QueryDelegatorWithdrawAddrParams. +func NewQueryDelegatorWithdrawAddrParams(delegatorAddr sdk.AccAddress) QueryDelegatorWithdrawAddrParams { + return QueryDelegatorWithdrawAddrParams{DelegatorAddr: delegatorAddr} +} + +func queryDelegatorWithdrawAddress(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper) ([]byte, sdk.Error) { + var params QueryDelegatorWithdrawAddrParams + err := k.cdc.UnmarshalJSON(req.Data, ¶ms) + if err != nil { + return nil, sdk.ErrUnknownRequest(sdk.AppendMsgToErr("incorrectly formatted request data", err.Error())) + } + + // cache-wrap context as to not persist state changes during querying + ctx, _ = ctx.CacheContext() + withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, params.DelegatorAddr) + + bz, err := codec.MarshalJSONIndent(k.cdc, withdrawAddr) + if err != nil { + return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error())) + } + + return bz, nil +} diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index e37f7870e..9d040d206 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -77,9 +77,7 @@ type voteReq struct { func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var req postProposalReq - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -96,8 +94,7 @@ func postProposalHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.Han // create the message msg := gov.NewMsgSubmitProposal(req.Title, req.Description, proposalType, req.Proposer, req.InitialDeposit) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -128,8 +125,7 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF } var req depositReq - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -140,8 +136,7 @@ func depositHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerF // create the message msg := gov.NewMsgDeposit(req.Depositor, proposalID, req.Amount) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -172,8 +167,7 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc } var req voteReq - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -190,8 +184,7 @@ func voteHandlerFn(cdc *codec.Codec, cliCtx context.CLIContext) http.HandlerFunc // create the message msg := gov.NewMsgVote(req.Voter, proposalID, voteOption) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index fd35764b0..f3ca528dc 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -39,8 +39,7 @@ func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. } var req transferReq - err = rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { + if !rest.ReadRESTReq(w, r, cdc, &req) { return } diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index edbfaec92..609da021c 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -34,8 +34,7 @@ func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CL bech32validator := vars["validatorAddr"] var req UnjailReq - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { + if !rest.ReadRESTReq(w, r, cdc, &req) { return } diff --git a/x/staking/client/rest/tx.go b/x/staking/client/rest/tx.go index 397e61a73..6258d7a7e 100644 --- a/x/staking/client/rest/tx.go +++ b/x/staking/client/rest/tx.go @@ -58,9 +58,7 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. return func(w http.ResponseWriter, r *http.Request) { var req msgDelegationsInput - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -70,8 +68,7 @@ func postDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context. } msg := staking.NewMsgDelegate(req.DelegatorAddr, req.ValidatorAddr, req.Delegation) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -103,9 +100,7 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex return func(w http.ResponseWriter, r *http.Request) { var req msgBeginRedelegateInput - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -115,8 +110,7 @@ func postRedelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx contex } msg := staking.NewMsgBeginRedelegate(req.DelegatorAddr, req.ValidatorSrcAddr, req.ValidatorDstAddr, req.SharesAmount) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } @@ -148,9 +142,7 @@ func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx return func(w http.ResponseWriter, r *http.Request) { var req msgUndelegateInput - err := rest.ReadRESTReq(w, r, cdc, &req) - if err != nil { - rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + if !rest.ReadRESTReq(w, r, cdc, &req) { return } @@ -160,8 +152,7 @@ func postUnbondingDelegationsHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx } msg := staking.NewMsgUndelegate(req.DelegatorAddr, req.ValidatorAddr, req.SharesAmount) - err = msg.ValidateBasic() - if err != nil { + if err := msg.ValidateBasic(); err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) return } From f15ad04a57e47b725af91896f10b9b3bfa6f5722 Mon Sep 17 00:00:00 2001 From: Joon Date: Mon, 4 Feb 2019 18:13:04 -0800 Subject: [PATCH 51/99] Merge PR #2605: Paramstore Subkey --- PENDING.md | 1 + docs/spec/params/keeper.md | 2 + docs/spec/params/subspace.md | 76 ++++------------------ x/auth/keeper.go | 2 +- x/auth/params.go | 10 +-- x/bank/keeper.go | 2 +- x/bank/params.go | 4 +- x/distribution/keeper/keeper.go | 2 +- x/distribution/keeper/params.go | 4 +- x/gov/keeper.go | 8 +-- x/mint/keeper.go | 6 +- x/params/doc.go | 36 +++++++++- x/params/keeper_test.go | 8 +-- x/params/subspace.go | 17 +++-- x/params/subspace/{pair.go => paramset.go} | 6 +- x/params/subspace/subspace.go | 75 ++++++++++++++++----- x/params/subspace/table.go | 30 +++++---- x/params/subspace/table_test.go | 8 +-- x/params/subspace/test_common.go | 4 +- x/slashing/keeper.go | 2 +- x/slashing/params.go | 10 +-- x/staking/keeper/keeper.go | 2 +- x/staking/keeper/params.go | 4 +- x/staking/types/params.go | 4 +- 24 files changed, 181 insertions(+), 142 deletions(-) rename x/params/subspace/{pair.go => paramset.go} (72%) diff --git a/PENDING.md b/PENDING.md index 916e54358..dc5351877 100644 --- a/PENDING.md +++ b/PENDING.md @@ -70,6 +70,7 @@ IMPROVEMENTS * [\#3424](https://github.com/cosmos/cosmos-sdk/issues/3424) Allow generation of gentxs with empty memo field. * SDK + * [\#2605] x/params add subkey accessing * [\#2986](https://github.com/cosmos/cosmos-sdk/pull/2986) Store Refactor * \#3435 Test that store implementations do not allow nil values diff --git a/docs/spec/params/keeper.md b/docs/spec/params/keeper.md index fe376911e..5cfd42caf 100644 --- a/docs/spec/params/keeper.md +++ b/docs/spec/params/keeper.md @@ -2,6 +2,8 @@ In the app initialization stage, `Keeper.Subspace(Paramspace)` is passed to the user modules, and the subspaces are stored in `Keeper.spaces`. Later it can be retrieved with `Keeper.GetSubspace`, so the keepers holding `Keeper` can access to any subspace. For example, Gov module can take `Keeper` as its argument and modify parameter of any subspace when a `ParameterChangeProposal` is accepted. +Example: + ```go type MasterKeeper struct { pk params.Keeper diff --git a/docs/spec/params/subspace.md b/docs/spec/params/subspace.md index 378177898..9c2245e0f 100644 --- a/docs/spec/params/subspace.md +++ b/docs/spec/params/subspace.md @@ -1,76 +1,26 @@ # Subspace -## Basic Usage +`Subspace` is a prefixed subspace of the parameter store. Each module who use the parameter store will take a `Subspace`, not the `Keeper`, to isolate permission to access. -First, declare parameter space and parameter keys for the module. Then include params.Subspace in the keeper. Since we prefix the keys with the spacename, it is recommended to use the same name with the module's. +## Key -```go -const ( - DefaultParamspace = "mymodule" -) +Parameter keys are human readable alphanumeric strings. A parameter for the key `"ExampleParameter"` is stored under `[]byte("SubspaceName" + "/" + "ExampleParameter")`, where `"SubspaceName"` is the name of the subspace. -const ( - KeyParameter1 = "myparameter1" - KeyParameter2 = "myparameter2" -) +Subkeys are secondary parameter keys those are used along with a primary parameter key. Subkeys can be used for grouping or dynamic parameter key generation during runtime. -type Keeper struct { - cdc *wire.Codec - key sdk.StoreKey +## KeyTable - ps params.Subspace -} -``` +All of the paramter keys that will be used should be registered at the compile time. `KeyTable` is essentially a `map[string]attribute`, where the `string` is a parameter key. -Pass a params.Subspace to NewKeeper with DefaultParamSubspace (or another) +Currently, `attribute` only consists of `reflect.Type`, which indicates the parameter type. It is needed even if the state machine has no error, because the paraeter can be modified externally, for example via the governance. -```go -app.myKeeper = mymodule.NewKeeper(cdc, key, app.paramStore.SubStore(mymodule.DefaultParamspace)) -``` +Only primary keys have to be registered on the `KeyTable`. Subkeys inherit the attribute of the primary key. -`NewKeeper` should register a `TypeTable`, which defines a map from parameter keys from types. +## ParamSet -```go -func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, space params.Subspace) Keeper { - return Keeper { - cdc: cdc, - key: key, - ps: space.WithTypeTable(ParamTypeTable()), - } -} -``` +Modules often define a struct of parameters. Instead of calling methods with each of those parameters, when the struct implements `ParamSet`, it can be used with the following methods: -Now we can access to the paramstore using Paramstore Keys - -```go -var param MyStruct -k.ps.Get(KeyParameter1, ¶m) -k.ps.Set(KeyParameter2, param) -``` - -# Genesis Usage - -Declare a struct for parameters and make it implement params.ParamSet. It will then be able to be passed to SetParamSet. - -```go -type MyParams struct { - Parameter1 uint64 - Parameter2 string -} - -// Implements params.ParamSet -// KeyValuePairs must return the list of (ParamKey, PointerToTheField) -func (p *MyParams) KeyValuePairs() params.KeyValuePairs { - return params.KeyFieldPairs { - {KeyParameter1, &p.Parameter1}, - {KeyParameter2, &p.Parameter2}, - } -} - -func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) { - k.ps.SetParamSet(ctx, &data.params) -} -``` - -The method is pointer receiver because there could be a case that we read from the store and set the result to the struct. +* `KeyTable.RegisterParamSet()`: registers all parameters in the struct +* `Subspace.{Get, Set}ParamSet()`: Get to & Set from the struct +The implementor should be a pointer in order to use `GetParamSet()` diff --git a/x/auth/keeper.go b/x/auth/keeper.go index f2cf86820..7167cc32d 100644 --- a/x/auth/keeper.go +++ b/x/auth/keeper.go @@ -47,7 +47,7 @@ func NewAccountKeeper( key: key, proto: proto, cdc: cdc, - paramSubspace: paramstore.WithTypeTable(ParamTypeTable()), + paramSubspace: paramstore.WithKeyTable(ParamKeyTable()), } } diff --git a/x/auth/params.go b/x/auth/params.go index 2831dbd32..bf0607cf2 100644 --- a/x/auth/params.go +++ b/x/auth/params.go @@ -42,15 +42,15 @@ type Params struct { } // ParamTable for staking module -func ParamTypeTable() params.TypeTable { - return params.NewTypeTable().RegisterParamSet(&Params{}) +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable().RegisterParamSet(&Params{}) } -// KeyValuePairs implements the ParamSet interface and returns all the key/value +// ParamSetPairs implements the ParamSet interface and returns all the key/value pairs // pairs of auth module's parameters. // nolint -func (p *Params) KeyValuePairs() params.KeyValuePairs { - return params.KeyValuePairs{ +func (p *Params) ParamSetPairs() params.ParamSetPairs { + return params.ParamSetPairs{ {KeyMaxMemoCharacters, &p.MaxMemoCharacters}, {KeyTxSigLimit, &p.TxSigLimit}, {KeyTxSizeCostPerByte, &p.TxSizeCostPerByte}, diff --git a/x/bank/keeper.go b/x/bank/keeper.go index 3055d8df4..978507e5c 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -42,7 +42,7 @@ func NewBaseKeeper(ak auth.AccountKeeper, paramSpace params.Subspace, codespace sdk.CodespaceType) BaseKeeper { - ps := paramSpace.WithTypeTable(ParamTypeTable()) + ps := paramSpace.WithKeyTable(ParamKeyTable()) return BaseKeeper{ BaseSendKeeper: NewBaseSendKeeper(ak, ps, codespace), ak: ak, diff --git a/x/bank/params.go b/x/bank/params.go index 9aea6adae..fcd2c0bea 100644 --- a/x/bank/params.go +++ b/x/bank/params.go @@ -15,8 +15,8 @@ const ( var ParamStoreKeySendEnabled = []byte("sendenabled") // type declaration for parameters -func ParamTypeTable() params.TypeTable { - return params.NewTypeTable( +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable( ParamStoreKeySendEnabled, false, ) } diff --git a/x/distribution/keeper/keeper.go b/x/distribution/keeper/keeper.go index a40e37380..1d38ebcc7 100644 --- a/x/distribution/keeper/keeper.go +++ b/x/distribution/keeper/keeper.go @@ -26,7 +26,7 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramSpace params.Subspace, c keeper := Keeper{ storeKey: key, cdc: cdc, - paramSpace: paramSpace.WithTypeTable(ParamTypeTable()), + paramSpace: paramSpace.WithKeyTable(ParamKeyTable()), bankKeeper: ck, stakingKeeper: sk, feeCollectionKeeper: fck, diff --git a/x/distribution/keeper/params.go b/x/distribution/keeper/params.go index fb1b23ed5..b95a6efb2 100644 --- a/x/distribution/keeper/params.go +++ b/x/distribution/keeper/params.go @@ -6,8 +6,8 @@ import ( ) // type declaration for parameters -func ParamTypeTable() params.TypeTable { - return params.NewTypeTable( +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable( ParamStoreKeyCommunityTax, sdk.Dec{}, ParamStoreKeyBaseProposerReward, sdk.Dec{}, ParamStoreKeyBonusProposerReward, sdk.Dec{}, diff --git a/x/gov/keeper.go b/x/gov/keeper.go index f59cdfd15..28b4832f3 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -35,9 +35,9 @@ var ( BurnedDepositCoinsAccAddr = sdk.AccAddress(crypto.AddressHash([]byte("govBurnedDepositCoins"))) ) -// Type declaration for parameters -func ParamTypeTable() params.TypeTable { - return params.NewTypeTable( +// Key declaration for parameters +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable( ParamStoreKeyDepositParams, DepositParams{}, ParamStoreKeyVotingParams, VotingParams{}, ParamStoreKeyTallyParams, TallyParams{}, @@ -80,7 +80,7 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, paramsKeeper params.Keeper, p return Keeper{ storeKey: key, paramsKeeper: paramsKeeper, - paramSpace: paramSpace.WithTypeTable(ParamTypeTable()), + paramSpace: paramSpace.WithKeyTable(ParamKeyTable()), ck: ck, ds: ds, vs: ds.GetValidatorSet(), diff --git a/x/mint/keeper.go b/x/mint/keeper.go index 1a508dc29..e532fa744 100644 --- a/x/mint/keeper.go +++ b/x/mint/keeper.go @@ -21,7 +21,7 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, keeper := Keeper{ storeKey: key, cdc: cdc, - paramSpace: paramSpace.WithTypeTable(ParamTypeTable()), + paramSpace: paramSpace.WithKeyTable(ParamKeyTable()), sk: sk, fck: fck, } @@ -39,8 +39,8 @@ var ( ) // ParamTable for staking module -func ParamTypeTable() params.TypeTable { - return params.NewTypeTable( +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable( ParamStoreKeyParams, Params{}, ) } diff --git a/x/params/doc.go b/x/params/doc.go index bee45e79e..1591507b4 100644 --- a/x/params/doc.go +++ b/x/params/doc.go @@ -27,12 +27,27 @@ recommended to use the same name with the module's. ) type Keeper struct { - cdc *wire.Codec + cdc *codec.Codec key sdk.StoreKey ps params.Subspace } + func ParamKeyTable() params.KeyTable { + return params.NewKeyTable( + KeyParameter1, MyStruct{}, + KeyParameter2, MyStruct{}, + ) + } + + func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ps params.Subspace) Keeper { + return Keeper { + cdc: cdc, + key: key, + ps: ps.WithKeyTable(ParamKeyTable()), + } + } + Pass a params.Subspace to NewKeeper with DefaultParamspace (or another) app.myKeeper = mymodule.NewKeeper(app.paramStore.SubStore(mymodule.DefaultParamspace)) @@ -40,8 +55,23 @@ Pass a params.Subspace to NewKeeper with DefaultParamspace (or another) Now we can access to the paramstore using Paramstore Keys var param MyStruct - k.ps.Get(KeyParameter1, ¶m) - k.ps.Set(KeyParameter2, param) + k.ps.Get(ctx, KeyParameter1, ¶m) + k.ps.Set(ctx, KeyParameter2, param) + +If you want to store an unknown number of parameters, or want to store a mapping, +you can use subkeys. Subkeys can be used with a main key, where the subkeys are +inheriting the key properties. + + func ParamKeyTable() params.KeyTable { + return params.NewKeyTable( + KeyParamMain, MyStruct{}, + ) + } + + + func (k Keeper) GetDynamicParameter(ctx sdk.Context, subkey []byte) (res MyStruct) { + k.ps.GetWithSubkey(ctx, KeyParamMain, subkey, &res) + } Genesis Usage: diff --git a/x/params/keeper_test.go b/x/params/keeper_test.go index cda57de8e..4d20e56ad 100644 --- a/x/params/keeper_test.go +++ b/x/params/keeper_test.go @@ -54,7 +54,7 @@ func TestKeeper(t *testing.T) { {"key7", 9058701}, } - table := NewTypeTable( + table := NewKeyTable( []byte("key1"), int64(0), []byte("key2"), int64(0), []byte("key3"), int64(0), @@ -71,8 +71,8 @@ func TestKeeper(t *testing.T) { tkey := sdk.NewTransientStoreKey("transient_test") ctx := defaultContext(skey, tkey) keeper := NewKeeper(cdc, skey, tkey) - space := keeper.Subspace("test").WithTypeTable(table) store := prefix.NewStore(ctx.KVStore(skey), []byte("test/")) + space := keeper.Subspace("test").WithKeyTable(table) // Set params for i, kv := range kvs { @@ -163,7 +163,7 @@ func TestSubspace(t *testing.T) { {"struct", s{1}, s{0}, new(s)}, } - table := NewTypeTable( + table := NewKeyTable( []byte("string"), string(""), []byte("bool"), bool(false), []byte("int16"), int16(0), @@ -179,7 +179,7 @@ func TestSubspace(t *testing.T) { ) store := prefix.NewStore(ctx.KVStore(key), []byte("test/")) - space := keeper.Subspace("test").WithTypeTable(table) + space := keeper.Subspace("test").WithKeyTable(table) // Test space.Set, space.Modified for i, kv := range kvs { diff --git a/x/params/subspace.go b/x/params/subspace.go index 203d32e12..6df312409 100644 --- a/x/params/subspace.go +++ b/x/params/subspace.go @@ -1,6 +1,10 @@ package params import ( + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/params/subspace" ) @@ -9,11 +13,14 @@ type ( Subspace = subspace.Subspace ReadOnlySubspace = subspace.ReadOnlySubspace ParamSet = subspace.ParamSet - KeyValuePairs = subspace.KeyValuePairs - TypeTable = subspace.TypeTable + ParamSetPairs = subspace.ParamSetPairs + KeyTable = subspace.KeyTable ) -// re-export functions from subspace -func NewTypeTable(keytypes ...interface{}) TypeTable { - return subspace.NewTypeTable(keytypes...) +// nolint - re-export functions from subspace +func NewKeyTable(keytypes ...interface{}) KeyTable { + return subspace.NewKeyTable(keytypes...) +} +func DefaultTestComponents(t *testing.T) (sdk.Context, Subspace, func() sdk.CommitID) { + return subspace.DefaultTestComponents(t) } diff --git a/x/params/subspace/pair.go b/x/params/subspace/paramset.go similarity index 72% rename from x/params/subspace/pair.go rename to x/params/subspace/paramset.go index 06fde6cef..66ebf5366 100644 --- a/x/params/subspace/pair.go +++ b/x/params/subspace/paramset.go @@ -1,15 +1,15 @@ package subspace // Used for associating paramsubspace key and field of param structs -type KeyValuePair struct { +type ParamSetPair struct { Key []byte Value interface{} } // Slice of KeyFieldPair -type KeyValuePairs []KeyValuePair +type ParamSetPairs []ParamSetPair // Interface for structs containing parameters for a module type ParamSet interface { - KeyValuePairs() KeyValuePairs + ParamSetPairs() ParamSetPairs } diff --git a/x/params/subspace/subspace.go b/x/params/subspace/subspace.go index cf8e345dd..72cb5f209 100644 --- a/x/params/subspace/subspace.go +++ b/x/params/subspace/subspace.go @@ -27,7 +27,7 @@ type Subspace struct { name []byte - table TypeTable + table KeyTable } // NewSubspace constructs a store with namestore @@ -37,21 +37,21 @@ func NewSubspace(cdc *codec.Codec, key sdk.StoreKey, tkey sdk.StoreKey, name str key: key, tkey: tkey, name: []byte(name), - table: TypeTable{ - m: make(map[string]reflect.Type), + table: KeyTable{ + m: make(map[string]attribute), }, } return } -// WithTypeTable initializes TypeTable and returns modified Subspace -func (s Subspace) WithTypeTable(table TypeTable) Subspace { +// WithKeyTable initializes KeyTable and returns modified Subspace +func (s Subspace) WithKeyTable(table KeyTable) Subspace { if table.m == nil { - panic("SetTypeTable() called with nil TypeTable") + panic("SetKeyTable() called with nil KeyTable") } if len(s.table.m) != 0 { - panic("SetTypeTable() called on already initialized Subspace") + panic("SetKeyTable() called on already initialized Subspace") } for k, v := range table.m { @@ -74,13 +74,21 @@ func (s Subspace) kvStore(ctx sdk.Context) sdk.KVStore { return prefix.NewStore(ctx.KVStore(s.key), append(s.name, '/')) } -// Returns a KVStore identical with ctx.TransientStore(s.tkey).Prefix() +// Returns a transient store for modification func (s Subspace) transientStore(ctx sdk.Context) sdk.KVStore { // append here is safe, appends within a function won't cause // weird side effects when its singlethreaded return prefix.NewStore(ctx.TransientStore(s.tkey), append(s.name, '/')) } +func concatKeys(key, subkey []byte) (res []byte) { + res = make([]byte, len(key)+1+len(subkey)) + copy(res, key) + res[len(key)] = '/' + copy(res[len(key)+1:], subkey) + return +} + // Get parameter from store func (s Subspace) Get(ctx sdk.Context, key []byte, ptr interface{}) { store := s.kvStore(ctx) @@ -104,6 +112,17 @@ func (s Subspace) GetIfExists(ctx sdk.Context, key []byte, ptr interface{}) { } } +// GetWithSubkey returns a parameter with a given key and a subkey. +func (s Subspace) GetWithSubkey(ctx sdk.Context, key, subkey []byte, ptr interface{}) { + s.Get(ctx, concatKeys(key, subkey), ptr) +} + +// GetWithSubkeyIfExists returns a parameter with a given key and a subkey but does not +// modify ptr if the stored parameter is nil. +func (s Subspace) GetWithSubkeyIfExists(ctx sdk.Context, key, subkey []byte, ptr interface{}) { + s.GetIfExists(ctx, concatKeys(key, subkey), ptr) +} + // Get raw bytes of parameter from store func (s Subspace) GetRaw(ctx sdk.Context, key []byte) []byte { store := s.kvStore(ctx) @@ -122,16 +141,13 @@ func (s Subspace) Modified(ctx sdk.Context, key []byte) bool { return tstore.Has(key) } -// Set parameter, return error if stored parameter has different type from input -// Also set to the transient store to record change -func (s Subspace) Set(ctx sdk.Context, key []byte, param interface{}) { - store := s.kvStore(ctx) - - ty, ok := s.table.m[string(key)] +func (s Subspace) checkType(store sdk.KVStore, key []byte, param interface{}) { + attr, ok := s.table.m[string(key)] if !ok { panic("Parameter not registered") } + ty := attr.ty pty := reflect.TypeOf(param) if pty.Kind() == reflect.Ptr { pty = pty.Elem() @@ -140,6 +156,14 @@ func (s Subspace) Set(ctx sdk.Context, key []byte, param interface{}) { if pty != ty { panic("Type mismatch with registered table") } +} + +// Set stores the parameter. It returns error if stored parameter has different type from input. +// It also set to the transient store to record change. +func (s Subspace) Set(ctx sdk.Context, key []byte, param interface{}) { + store := s.kvStore(ctx) + + s.checkType(store, key, param) bz, err := s.cdc.MarshalJSON(param) if err != nil { @@ -152,16 +176,35 @@ func (s Subspace) Set(ctx sdk.Context, key []byte, param interface{}) { } +// SetWithSubkey set a parameter with a key and subkey +// Checks parameter type only over the key +func (s Subspace) SetWithSubkey(ctx sdk.Context, key []byte, subkey []byte, param interface{}) { + store := s.kvStore(ctx) + + s.checkType(store, key, param) + + newkey := concatKeys(key, subkey) + + bz, err := s.cdc.MarshalJSON(param) + if err != nil { + panic(err) + } + store.Set(newkey, bz) + + tstore := s.transientStore(ctx) + tstore.Set(newkey, []byte{}) +} + // Get to ParamSet func (s Subspace) GetParamSet(ctx sdk.Context, ps ParamSet) { - for _, pair := range ps.KeyValuePairs() { + for _, pair := range ps.ParamSetPairs() { s.Get(ctx, pair.Key, pair.Value) } } // Set from ParamSet func (s Subspace) SetParamSet(ctx sdk.Context, ps ParamSet) { - for _, pair := range ps.KeyValuePairs() { + for _, pair := range ps.ParamSetPairs() { // pair.Field is a pointer to the field, so indirecting the ptr. // go-amino automatically handles it but just for sure, // since SetStruct is meant to be used in InitGenesis diff --git a/x/params/subspace/table.go b/x/params/subspace/table.go index 19e41da09..75269cc06 100644 --- a/x/params/subspace/table.go +++ b/x/params/subspace/table.go @@ -4,19 +4,23 @@ import ( "reflect" ) -// TypeTable subspaces appropriate type for each parameter key -type TypeTable struct { - m map[string]reflect.Type +type attribute struct { + ty reflect.Type +} + +// KeyTable subspaces appropriate type for each parameter key +type KeyTable struct { + m map[string]attribute } // Constructs new table -func NewTypeTable(keytypes ...interface{}) (res TypeTable) { +func NewKeyTable(keytypes ...interface{}) (res KeyTable) { if len(keytypes)%2 != 0 { - panic("odd number arguments in NewTypeTypeTable") + panic("odd number arguments in NewTypeKeyTable") } - res = TypeTable{ - m: make(map[string]reflect.Type), + res = KeyTable{ + m: make(map[string]attribute), } for i := 0; i < len(keytypes); i += 2 { @@ -38,7 +42,7 @@ func isAlphaNumeric(key []byte) bool { } // Register single key-type pair -func (t TypeTable) RegisterType(key []byte, ty interface{}) TypeTable { +func (t KeyTable) RegisterType(key []byte, ty interface{}) KeyTable { if len(key) == 0 { panic("cannot register empty key") } @@ -57,20 +61,22 @@ func (t TypeTable) RegisterType(key []byte, ty interface{}) TypeTable { rty = rty.Elem() } - t.m[keystr] = rty + t.m[keystr] = attribute{ + ty: rty, + } return t } // Register multiple pairs from ParamSet -func (t TypeTable) RegisterParamSet(ps ParamSet) TypeTable { - for _, kvp := range ps.KeyValuePairs() { +func (t KeyTable) RegisterParamSet(ps ParamSet) KeyTable { + for _, kvp := range ps.ParamSetPairs() { t = t.RegisterType(kvp.Key, kvp.Value) } return t } -func (t TypeTable) maxKeyLength() (res int) { +func (t KeyTable) maxKeyLength() (res int) { for k := range t.m { l := len(k) if l > res { diff --git a/x/params/subspace/table_test.go b/x/params/subspace/table_test.go index 8f9142e1e..f1485b6e2 100644 --- a/x/params/subspace/table_test.go +++ b/x/params/subspace/table_test.go @@ -11,15 +11,15 @@ type testparams struct { b bool } -func (tp *testparams) KeyValuePairs() KeyValuePairs { - return KeyValuePairs{ +func (tp *testparams) ParamSetPairs() ParamSetPairs { + return ParamSetPairs{ {[]byte("i"), &tp.i}, {[]byte("b"), &tp.b}, } } -func TestTypeTable(t *testing.T) { - table := NewTypeTable() +func TestKeyTable(t *testing.T) { + table := NewKeyTable() require.Panics(t, func() { table.RegisterType([]byte(""), nil) }) require.Panics(t, func() { table.RegisterType([]byte("!@#$%"), nil) }) diff --git a/x/params/subspace/test_common.go b/x/params/subspace/test_common.go index f6bf9c73f..fc259f765 100644 --- a/x/params/subspace/test_common.go +++ b/x/params/subspace/test_common.go @@ -21,7 +21,7 @@ const ( ) // Returns components for testing -func DefaultTestComponents(t *testing.T, table TypeTable) (sdk.Context, Subspace, func() sdk.CommitID) { +func DefaultTestComponents(t *testing.T) (sdk.Context, Subspace, func() sdk.CommitID) { cdc := codec.New() key := sdk.NewKVStoreKey(StoreKey) tkey := sdk.NewTransientStoreKey(TStoreKey) @@ -34,7 +34,7 @@ func DefaultTestComponents(t *testing.T, table TypeTable) (sdk.Context, Subspace err := ms.LoadLatestVersion() require.Nil(t, err) ctx := sdk.NewContext(ms, abci.Header{}, false, log.NewTMLogger(os.Stdout)) - subspace := NewSubspace(cdc, key, tkey, TestParamStore).WithTypeTable(table) + subspace := NewSubspace(cdc, key, tkey, TestParamStore) return ctx, subspace, ms.Commit } diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index f8494a887..aa0e1c5d0 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -29,7 +29,7 @@ func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, vs sdk.ValidatorSet, paramspa storeKey: key, cdc: cdc, validatorSet: vs, - paramspace: paramspace.WithTypeTable(ParamTypeTable()), + paramspace: paramspace.WithKeyTable(ParamKeyTable()), codespace: codespace, } return keeper diff --git a/x/slashing/params.go b/x/slashing/params.go index 12afad7e9..87b3edddd 100644 --- a/x/slashing/params.go +++ b/x/slashing/params.go @@ -28,9 +28,9 @@ var ( KeySlashFractionDowntime = []byte("SlashFractionDowntime") ) -// ParamTypeTable for slashing module -func ParamTypeTable() params.TypeTable { - return params.NewTypeTable().RegisterParamSet(&Params{}) +// ParamKeyTable for slashing module +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable().RegisterParamSet(&Params{}) } // Params - used for initializing default parameter for slashing at genesis @@ -57,8 +57,8 @@ func (p Params) String() string { } // Implements params.ParamStruct -func (p *Params) KeyValuePairs() params.KeyValuePairs { - return params.KeyValuePairs{ +func (p *Params) ParamSetPairs() params.ParamSetPairs { + return params.ParamSetPairs{ {KeyMaxEvidenceAge, &p.MaxEvidenceAge}, {KeySignedBlocksWindow, &p.SignedBlocksWindow}, {KeyMinSignedPerWindow, &p.MinSignedPerWindow}, diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go index ea3527d5d..81b79bf02 100644 --- a/x/staking/keeper/keeper.go +++ b/x/staking/keeper/keeper.go @@ -34,7 +34,7 @@ func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, paramst storeTKey: tkey, cdc: cdc, bankKeeper: ck, - paramstore: paramstore.WithTypeTable(ParamTypeTable()), + paramstore: paramstore.WithKeyTable(ParamKeyTable()), hooks: nil, validatorCache: make(map[string]cachedValidator, aminoCacheSize), validatorCacheList: list.New(), diff --git a/x/staking/keeper/params.go b/x/staking/keeper/params.go index ffc5d45af..a0f1acacc 100644 --- a/x/staking/keeper/params.go +++ b/x/staking/keeper/params.go @@ -14,8 +14,8 @@ const ( ) // ParamTable for staking module -func ParamTypeTable() params.TypeTable { - return params.NewTypeTable().RegisterParamSet(&types.Params{}) +func ParamKeyTable() params.KeyTable { + return params.NewKeyTable().RegisterParamSet(&types.Params{}) } // UnbondingTime diff --git a/x/staking/types/params.go b/x/staking/types/params.go index 9a7ca2748..182abfce6 100644 --- a/x/staking/types/params.go +++ b/x/staking/types/params.go @@ -54,8 +54,8 @@ func NewParams(unbondingTime time.Duration, maxValidators, maxEntries uint16, } // Implements params.ParamSet -func (p *Params) KeyValuePairs() params.KeyValuePairs { - return params.KeyValuePairs{ +func (p *Params) ParamSetPairs() params.ParamSetPairs { + return params.ParamSetPairs{ {KeyUnbondingTime, &p.UnbondingTime}, {KeyMaxValidators, &p.MaxValidators}, {KeyMaxEntries, &p.MaxEntries}, From 5e353542692c5e08dac4f42b0561de1e583e2b05 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 4 Feb 2019 18:20:56 -0800 Subject: [PATCH 52/99] Merge PR #3451: Make tags and responses legible --- PENDING.md | 3 +- baseapp/baseapp.go | 2 +- client/context/broadcast.go | 142 ++++++------------------------ client/lcd/lcd_test.go | 64 +++++--------- client/lcd/test_helpers.go | 52 +++++------ client/tx/query.go | 25 +----- client/tx/search.go | 8 +- client/utils/utils.go | 3 +- cmd/gaia/cli_test/cli_test.go | 18 ++-- cmd/gaia/cli_test/test_helpers.go | 5 +- types/result.go | 110 ++++++++++++++++++++++- types/tags.go | 60 ++++++++++++- types/tags_test.go | 16 ++-- x/bank/client/cli/broadcast.go | 3 +- x/bank/keeper.go | 4 +- x/gov/handler.go | 22 ++--- x/gov/tags/tags.go | 6 +- x/slashing/handler.go | 2 +- x/slashing/tags/tags.go | 2 +- x/staking/client/rest/query.go | 11 ++- x/staking/client/rest/utils.go | 2 +- x/staking/handler.go | 40 ++++----- x/staking/tags/tags.go | 4 +- 23 files changed, 317 insertions(+), 287 deletions(-) diff --git a/PENDING.md b/PENDING.md index dc5351877..109e18ab5 100644 --- a/PENDING.md +++ b/PENDING.md @@ -14,6 +14,7 @@ BREAKING CHANGES - [\#3465](https://github.com/cosmos/cosmos-sdk/issues/3465) `gaiacli rest-server` switched back to insecure mode by default: - `--insecure` flag is removed. - `--tls` is now used to enable secure layer. + - [\#3451](https://github.com/cosmos/cosmos-sdk/pull/3451) `gaiacli` now returns transactions in plain text including tags. * Gaia * [\#3457](https://github.com/cosmos/cosmos-sdk/issues/3457) Changed governance tally validatorGovInfo to use sdk.Int power instead of sdk.Dec @@ -86,7 +87,7 @@ BUG FIXES - [\#3345](https://github.com/cosmos/cosmos-sdk/issues/3345) Upgrade ledger-cosmos-go dependency to v0.9.3 to pull https://github.com/ZondaX/ledger-cosmos-go/commit/ed9aa39ce8df31bad1448c72d3d226bf2cb1a8d1 in order to fix a derivation path issue that causes `gaiacli keys add --recover` to malfunction. - - [\#3419](https://github.com/cosmos/cosmos-sdk/pull/3419) Fix `q distr slashes` panic + - [\#3419](https://github.com/cosmos/cosmos-sdk/pull/3419) Fix `q distr slashes` panic - [\#3453](https://github.com/cosmos/cosmos-sdk/pull/3453) The `rest-server` command didn't respect persistent flags such as `--chain-id` and `--trust-node` if they were passed on the command line. diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 6a6cc7960..2c24a57b1 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -630,7 +630,7 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re // Append Data and Tags data = append(data, msgResult.Data...) - tags = append(tags, sdk.MakeTag(sdk.TagAction, []byte(msg.Type()))) + tags = append(tags, sdk.MakeTag(sdk.TagAction, msg.Type())) tags = append(tags, msgResult.Tags...) // Stop execution and return on first failed message. diff --git a/client/context/broadcast.go b/client/context/broadcast.go index c844e519d..21a88d99d 100644 --- a/client/context/broadcast.go +++ b/client/context/broadcast.go @@ -1,170 +1,80 @@ package context import ( - "fmt" - "io" - "github.com/pkg/errors" - abci "github.com/tendermint/tendermint/abci/types" - ctypes "github.com/tendermint/tendermint/rpc/core/types" + sdk "github.com/cosmos/cosmos-sdk/types" ) -// TODO: This should get deleted eventually, and perhaps -// ctypes.ResultBroadcastTx be stripped of unused fields, and -// ctypes.ResultBroadcastTxCommit returned for tendermint RPC BroadcastTxSync. -// -// The motivation is that we want a unified type to return, and the better -// option is the one that can hold CheckTx/DeliverTx responses optionally. -func resultBroadcastTxToCommit(res *ctypes.ResultBroadcastTx) *ctypes.ResultBroadcastTxCommit { - return &ctypes.ResultBroadcastTxCommit{ - Hash: res.Hash, - // NOTE: other fields are unused for async. - } -} - // BroadcastTx broadcasts a transactions either synchronously or asynchronously // based on the context parameters. The result of the broadcast is parsed into // an intermediate structure which is logged if the context has a logger // defined. -func (ctx CLIContext) BroadcastTx(txBytes []byte) (*ctypes.ResultBroadcastTxCommit, error) { +func (ctx CLIContext) BroadcastTx(txBytes []byte) (res sdk.TxResponse, err error) { if ctx.Async { - res, err := ctx.broadcastTxAsync(txBytes) - if err != nil { - return nil, err + if res, err = ctx.BroadcastTxAsync(txBytes); err != nil { + return } - - resCommit := resultBroadcastTxToCommit(res) - return resCommit, err + return } - return ctx.broadcastTxCommit(txBytes) + if res, err = ctx.BroadcastTxAndAwaitCommit(txBytes); err != nil { + return + } + + return } // BroadcastTxAndAwaitCommit broadcasts transaction bytes to a Tendermint node // and waits for a commit. -func (ctx CLIContext) BroadcastTxAndAwaitCommit(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) { +func (ctx CLIContext) BroadcastTxAndAwaitCommit(tx []byte) (sdk.TxResponse, error) { node, err := ctx.GetNode() if err != nil { - return nil, err + return sdk.TxResponse{}, err } res, err := node.BroadcastTxCommit(tx) if err != nil { - return res, err + return sdk.NewResponseFormatBroadcastTxCommit(res), err } if !res.CheckTx.IsOK() { - return res, errors.Errorf(res.CheckTx.Log) + return sdk.NewResponseFormatBroadcastTxCommit(res), errors.Errorf(res.CheckTx.Log) } if !res.DeliverTx.IsOK() { - return res, errors.Errorf(res.DeliverTx.Log) + return sdk.NewResponseFormatBroadcastTxCommit(res), errors.Errorf(res.DeliverTx.Log) } - return res, err + return sdk.NewResponseFormatBroadcastTxCommit(res), err } -// BroadcastTxSync broadcasts transaction bytes to a Tendermint node -// synchronously. -func (ctx CLIContext) BroadcastTxSync(tx []byte) (*ctypes.ResultBroadcastTx, error) { +// BroadcastTxSync broadcasts transaction bytes to a Tendermint node synchronously. +func (ctx CLIContext) BroadcastTxSync(tx []byte) (sdk.TxResponse, error) { node, err := ctx.GetNode() if err != nil { - return nil, err + return sdk.TxResponse{}, err } res, err := node.BroadcastTxSync(tx) if err != nil { - return res, err + return sdk.NewResponseFormatBroadcastTx(res), err } - return res, err + return sdk.NewResponseFormatBroadcastTx(res), err } -// BroadcastTxAsync broadcasts transaction bytes to a Tendermint node -// asynchronously. -func (ctx CLIContext) BroadcastTxAsync(tx []byte) (*ctypes.ResultBroadcastTx, error) { +// BroadcastTxAsync broadcasts transaction bytes to a Tendermint node asynchronously. +func (ctx CLIContext) BroadcastTxAsync(tx []byte) (sdk.TxResponse, error) { node, err := ctx.GetNode() if err != nil { - return nil, err + return sdk.TxResponse{}, err } res, err := node.BroadcastTxAsync(tx) if err != nil { - return res, err + return sdk.NewResponseFormatBroadcastTx(res), err } - return res, err -} - -func (ctx CLIContext) broadcastTxAsync(txBytes []byte) (*ctypes.ResultBroadcastTx, error) { - res, err := ctx.BroadcastTxAsync(txBytes) - if err != nil { - return res, err - } - - if ctx.Output != nil { - if ctx.OutputFormat == "json" { - type toJSON struct { - TxHash string - } - - resJSON := toJSON{res.Hash.String()} - bz, err := ctx.Codec.MarshalJSON(resJSON) - if err != nil { - return res, err - } - - ctx.Output.Write(bz) - io.WriteString(ctx.Output, "\n") - } else { - io.WriteString(ctx.Output, fmt.Sprintf("async tx sent (tx hash: %s)\n", res.Hash)) - } - } - - return res, nil -} - -func (ctx CLIContext) broadcastTxCommit(txBytes []byte) (*ctypes.ResultBroadcastTxCommit, error) { - res, err := ctx.BroadcastTxAndAwaitCommit(txBytes) - if err != nil { - return res, err - } - - if ctx.OutputFormat == "json" { - // Since JSON is intended for automated scripts, always include response in - // JSON mode. - type toJSON struct { - Height int64 - TxHash string - Response abci.ResponseDeliverTx - } - - if ctx.Output != nil { - resJSON := toJSON{res.Height, res.Hash.String(), res.DeliverTx} - bz, err := ctx.Codec.MarshalJSON(resJSON) - if err != nil { - return res, err - } - - ctx.Output.Write(bz) - io.WriteString(ctx.Output, "\n") - } - - return res, nil - } - - if ctx.Output != nil { - resStr := fmt.Sprintf("Committed at block %d (tx hash: %s)\n", res.Height, res.Hash.String()) - - if ctx.PrintResponse { - resStr = fmt.Sprintf("Committed at block %d (tx hash: %s, response: %+v)\n", - res.Height, res.Hash.String(), res.DeliverTx, - ) - } - - io.WriteString(ctx.Output, resStr) - } - - return res, nil + return sdk.NewResponseFormatBroadcastTx(res), err } diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 0a2e72b05..c9842c7eb 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -12,11 +12,8 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/require" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - - "github.com/cosmos/cosmos-sdk/client" + client "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/rest" - "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" @@ -170,8 +167,7 @@ func TestCoinSend(t *testing.T) { tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + require.Equal(t, uint32(0), resultTx.Code) // query sender acc = getAccount(t, port, addr) @@ -232,8 +228,7 @@ func TestCoinSend(t *testing.T) { require.Nil(t, err) tests.WaitForHeight(resultTx.Height+1, port) - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + require.Equal(t, uint32(0), resultTx.Code) acc = getAccount(t, port, addr) expectedBalance = expectedBalance.Minus(fees[0]) @@ -348,11 +343,10 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { require.Equal(t, http.StatusOK, res.StatusCode, body) // check if tx was committed - var resultTx ctypes.ResultBroadcastTxCommit + var resultTx sdk.TxResponse require.Nil(t, cdc.UnmarshalJSON([]byte(body), &resultTx)) - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) - require.Equal(t, gasEstimate, resultTx.DeliverTx.GasWanted) + require.Equal(t, uint32(0), resultTx.Code) + require.Equal(t, gasEstimate, resultTx.GasWanted) } func TestTxs(t *testing.T) { @@ -360,7 +354,7 @@ func TestTxs(t *testing.T) { cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() - var emptyTxs []tx.Info + var emptyTxs []sdk.TxResponse txs := getTransactions(t, port) require.Equal(t, emptyTxs, txs) @@ -380,14 +374,13 @@ func TestTxs(t *testing.T) { tests.WaitForHeight(resultTx.Height+1, port) // check if tx is queryable - tx := getTransaction(t, port, resultTx.Hash.String()) - require.Equal(t, resultTx.Hash, tx.Hash) + tx := getTransaction(t, port, resultTx.TxHash) + require.Equal(t, resultTx.TxHash, tx.TxHash) // query sender txs = getTransactions(t, port, fmt.Sprintf("sender=%s", addr.String())) require.Len(t, txs, 1) require.Equal(t, resultTx.Height, txs[0].Height) - fmt.Println(txs[0]) // query recipient txs = getTransactions(t, port, fmt.Sprintf("recipient=%s", receiveAddr.String())) @@ -465,8 +458,7 @@ func TestBonding(t *testing.T) { resultTx := doDelegate(t, port, name1, pw, addr, operAddrs[0], 60, fees) tests.WaitForHeight(resultTx.Height+1, port) - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + require.Equal(t, uint32(0), resultTx.Code) // query tx txs := getTransactions(t, port, @@ -507,8 +499,7 @@ func TestBonding(t *testing.T) { resultTx = doUndelegate(t, port, name1, pw, addr, operAddrs[0], 30, fees) tests.WaitForHeight(resultTx.Height+1, port) - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + require.Equal(t, uint32(0), resultTx.Code) // sender should have not received any coins as the unbonding has only just begun acc = getAccount(t, port, addr) @@ -537,8 +528,7 @@ func TestBonding(t *testing.T) { resultTx = doBeginRedelegation(t, port, name1, pw, addr, operAddrs[0], operAddrs[1], 30, fees) tests.WaitForHeight(resultTx.Height+1, port) - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + require.Equal(t, uint32(0), resultTx.Code) // verify balance after paying fees acc = getAccount(t, port, addr) @@ -618,11 +608,10 @@ func TestSubmitProposal(t *testing.T) { tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + require.Equal(t, uint32(0), resultTx.Code) var proposalID uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID) + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID) // verify balance acc = getAccount(t, port, addr) @@ -651,11 +640,10 @@ func TestDeposit(t *testing.T) { tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + require.Equal(t, uint32(0), resultTx.Code) var proposalID uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID) + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID) // verify balance acc = getAccount(t, port, addr) @@ -704,11 +692,10 @@ func TestVote(t *testing.T) { tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + require.Equal(t, uint32(0), resultTx.Code) var proposalID uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID) + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID) // verify balance acc = getAccount(t, port, addr) @@ -802,18 +789,18 @@ func TestProposalsQuery(t *testing.T) { // Addr1 proposes (and deposits) proposals #1 and #2 resultTx := doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], halfMinDeposit, fees) var proposalID1 uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID1) + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID1) tests.WaitForHeight(resultTx.Height+1, port) resultTx = doSubmitProposal(t, port, seeds[0], names[0], passwords[0], addrs[0], halfMinDeposit, fees) var proposalID2 uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID2) + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID2) tests.WaitForHeight(resultTx.Height+1, port) // Addr2 proposes (and deposits) proposals #3 resultTx = doSubmitProposal(t, port, seeds[1], names[1], passwords[1], addrs[1], halfMinDeposit, fees) var proposalID3 uint64 - cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.DeliverTx.GetData(), &proposalID3) + cdc.MustUnmarshalBinaryLengthPrefixed(resultTx.Data, &proposalID3) tests.WaitForHeight(resultTx.Height+1, port) // Addr2 deposits on proposals #2 & #3 @@ -956,14 +943,12 @@ func TestDistributionFlow(t *testing.T) { // Delegate some coins resultTx := doDelegate(t, port, name1, pw, addr, valAddr, 60, fees) tests.WaitForHeight(resultTx.Height+1, port) - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + require.Equal(t, uint32(0), resultTx.Code) // send some coins _, resultTx = doTransfer(t, port, seed, name1, memo, pw, addr, fees) tests.WaitForHeight(resultTx.Height+5, port) - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + require.Equal(t, uint32(0), resultTx.Code) // Query outstanding rewards changed oustandingRewards := mustParseDecCoins("9.80stake") @@ -1013,6 +998,5 @@ func TestDistributionFlow(t *testing.T) { // Withdraw delegator's rewards resultTx = doWithdrawDelegatorAllRewards(t, port, seed, name1, pw, addr, fees) - require.Equal(t, uint32(0), resultTx.CheckTx.Code) - require.Equal(t, uint32(0), resultTx.DeliverTx.Code) + require.Equal(t, uint32(0), resultTx.Code) } diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index efef932a6..58b6319a6 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -506,8 +506,8 @@ func getValidatorSets(t *testing.T, port string, height int, expectFail bool) rp } // GET /txs/{hash} get tx by hash -func getTransaction(t *testing.T, port string, hash string) tx.Info { - var tx tx.Info +func getTransaction(t *testing.T, port string, hash string) sdk.TxResponse { + var tx sdk.TxResponse res, body := Request(t, port, "GET", fmt.Sprintf("/txs/%s", hash), nil) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -519,8 +519,8 @@ func getTransaction(t *testing.T, port string, hash string) tx.Info { // POST /txs broadcast txs // GET /txs search transactions -func getTransactions(t *testing.T, port string, tags ...string) []tx.Info { - var txs []tx.Info +func getTransactions(t *testing.T, port string, tags ...string) []sdk.TxResponse { + var txs []sdk.TxResponse if len(tags) == 0 { return txs } @@ -667,13 +667,13 @@ func doSign(t *testing.T, port, name, password, chainID string, accnum, sequence } // POST /tx/broadcast Send a signed Tx -func doBroadcast(t *testing.T, port string, msg auth.StdTx) ctypes.ResultBroadcastTxCommit { +func doBroadcast(t *testing.T, port string, msg auth.StdTx) sdk.TxResponse { tx := broadcastReq{Tx: msg, Return: "block"} req, err := cdc.MarshalJSON(tx) require.Nil(t, err) res, body := Request(t, port, "POST", "/tx/broadcast", req) require.Equal(t, http.StatusOK, res.StatusCode, body) - var resultTx ctypes.ResultBroadcastTxCommit + var resultTx sdk.TxResponse require.Nil(t, cdc.UnmarshalJSON([]byte(body), &resultTx)) return resultTx } @@ -686,7 +686,7 @@ type broadcastReq struct { // GET /bank/balances/{address} Get the account balances // POST /bank/accounts/{address}/transfers Send coins (build -> sign -> send) -func doTransfer(t *testing.T, port, seed, name, memo, password string, addr sdk.AccAddress, fees sdk.Coins) (receiveAddr sdk.AccAddress, resultTx ctypes.ResultBroadcastTxCommit) { +func doTransfer(t *testing.T, port, seed, name, memo, password string, addr sdk.AccAddress, fees sdk.Coins) (receiveAddr sdk.AccAddress, resultTx sdk.TxResponse) { res, body, receiveAddr := doTransferWithGas(t, port, seed, name, memo, password, addr, "", 1.0, false, false, fees) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -782,7 +782,7 @@ type sendReq struct { // POST /staking/delegators/{delegatorAddr}/delegations Submit delegation func doDelegate(t *testing.T, port, name, password string, - delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) { + delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64, fees sdk.Coins) (resultTx sdk.TxResponse) { acc := getAccount(t, port, delAddr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() @@ -799,7 +799,7 @@ func doDelegate(t *testing.T, port, name, password string, res, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/delegations", delAddr.String()), req) require.Equal(t, http.StatusOK, res.StatusCode, body) - var result ctypes.ResultBroadcastTxCommit + var result sdk.TxResponse err = cdc.UnmarshalJSON([]byte(body), &result) require.Nil(t, err) @@ -815,7 +815,7 @@ type msgDelegationsInput struct { // POST /staking/delegators/{delegatorAddr}/delegations Submit delegation func doUndelegate(t *testing.T, port, name, password string, - delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) { + delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64, fees sdk.Coins) (resultTx sdk.TxResponse) { acc := getAccount(t, port, delAddr) accnum := acc.GetAccountNumber() @@ -834,7 +834,7 @@ func doUndelegate(t *testing.T, port, name, password string, res, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/unbonding_delegations", delAddr), req) require.Equal(t, http.StatusOK, res.StatusCode, body) - var result ctypes.ResultBroadcastTxCommit + var result sdk.TxResponse err = cdc.UnmarshalJSON([]byte(body), &result) require.Nil(t, err) @@ -850,7 +850,7 @@ type msgUndelegateInput struct { // POST /staking/delegators/{delegatorAddr}/delegations Submit delegation func doBeginRedelegation(t *testing.T, port, name, password string, - delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, amount int64, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) { + delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, amount int64, fees sdk.Coins) (resultTx sdk.TxResponse) { acc := getAccount(t, port, delAddr) accnum := acc.GetAccountNumber() @@ -872,7 +872,7 @@ func doBeginRedelegation(t *testing.T, port, name, password string, res, body := Request(t, port, "POST", fmt.Sprintf("/staking/delegators/%s/redelegations", delAddr), req) require.Equal(t, http.StatusOK, res.StatusCode, body) - var result ctypes.ResultBroadcastTxCommit + var result sdk.TxResponse err = cdc.UnmarshalJSON([]byte(body), &result) require.Nil(t, err) @@ -961,7 +961,7 @@ func getDelegatorValidator(t *testing.T, port string, delegatorAddr sdk.AccAddre } // GET /staking/delegators/{delegatorAddr}/txs Get all staking txs (i.e msgs) from a delegator -func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, query string) []tx.Info { +func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, query string) []sdk.TxResponse { var res *http.Response var body string @@ -972,7 +972,7 @@ func getBondingTxs(t *testing.T, port string, delegatorAddr sdk.AccAddress, quer } require.Equal(t, http.StatusOK, res.StatusCode, body) - var txs []tx.Info + var txs []sdk.TxResponse err := cdc.UnmarshalJSON([]byte(body), &txs) require.Nil(t, err) @@ -1083,7 +1083,7 @@ func getStakingParams(t *testing.T, port string) staking.Params { // ICS 22 - Gov // ---------------------------------------------------------------------- // POST /gov/proposals Submit a proposal -func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, amount int64, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) { +func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, amount int64, fees sdk.Coins) (resultTx sdk.TxResponse) { acc := getAccount(t, port, proposerAddr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() @@ -1106,7 +1106,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA res, body := Request(t, port, "POST", "/gov/proposals", req) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results ctypes.ResultBroadcastTxCommit + var results sdk.TxResponse err = cdc.UnmarshalJSON([]byte(body), &results) require.Nil(t, err) @@ -1178,7 +1178,7 @@ func getProposalsFilterStatus(t *testing.T, port string, status gov.ProposalStat } // POST /gov/proposals/{proposalId}/deposits Deposit tokens to a proposal -func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, amount int64, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) { +func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, amount int64, fees sdk.Coins) (resultTx sdk.TxResponse) { acc := getAccount(t, port, proposerAddr) accnum := acc.GetAccountNumber() @@ -1198,7 +1198,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/deposits", proposalID), req) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results ctypes.ResultBroadcastTxCommit + var results sdk.TxResponse err = cdc.UnmarshalJSON([]byte(body), &results) require.Nil(t, err) @@ -1232,7 +1232,7 @@ func getTally(t *testing.T, port string, proposalID uint64) gov.TallyResult { } // POST /gov/proposals/{proposalId}/votes Vote a proposal -func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, option string, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) { +func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, option string, fees sdk.Coins) (resultTx sdk.TxResponse) { // get the account to get the sequence acc := getAccount(t, port, proposerAddr) accnum := acc.GetAccountNumber() @@ -1252,7 +1252,7 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ac res, body := Request(t, port, "POST", fmt.Sprintf("/gov/proposals/%d/votes", proposalID), req) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results ctypes.ResultBroadcastTxCommit + var results sdk.TxResponse err = cdc.UnmarshalJSON([]byte(body), &results) require.Nil(t, err) @@ -1368,7 +1368,7 @@ func getSigningInfo(t *testing.T, port string, validatorPubKey string) slashing. // TODO: Test this functionality, it is not currently in any of the tests // POST /slashing/validators/{validatorAddr}/unjail Unjail a jailed validator func doUnjail(t *testing.T, port, seed, name, password string, - valAddr sdk.ValAddress, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) { + valAddr sdk.ValAddress, fees sdk.Coins) (resultTx sdk.TxResponse) { chainID := viper.GetString(client.FlagChainID) baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", 1, 1, fees, nil, false, false) @@ -1380,11 +1380,11 @@ func doUnjail(t *testing.T, port, seed, name, password string, res, body := Request(t, port, "POST", fmt.Sprintf("/slashing/validators/%s/unjail", valAddr.String()), req) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results []ctypes.ResultBroadcastTxCommit + var results sdk.TxResponse err = cdc.UnmarshalJSON([]byte(body), &results) require.Nil(t, err) - return results[0] + return results } type unjailReq struct { @@ -1395,7 +1395,7 @@ type unjailReq struct { // POST /distribution/delegators/{delgatorAddr}/rewards Withdraw delegator rewards func doWithdrawDelegatorAllRewards(t *testing.T, port, seed, name, password string, - delegatorAddr sdk.AccAddress, fees sdk.Coins) (resultTx ctypes.ResultBroadcastTxCommit) { + delegatorAddr sdk.AccAddress, fees sdk.Coins) (resultTx sdk.TxResponse) { // get the account to get the sequence acc := getAccount(t, port, delegatorAddr) accnum := acc.GetAccountNumber() @@ -1411,7 +1411,7 @@ func doWithdrawDelegatorAllRewards(t *testing.T, port, seed, name, password stri res, body := Request(t, port, "POST", fmt.Sprintf("/distribution/delegators/%s/rewards", delegatorAddr), req) require.Equal(t, http.StatusOK, res.StatusCode, body) - var results ctypes.ResultBroadcastTxCommit + var results sdk.TxResponse cdc.MustUnmarshalJSON([]byte(body), &results) return results diff --git a/client/tx/query.go b/client/tx/query.go index 754b5ba96..cbf64f34e 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -5,19 +5,15 @@ import ( "fmt" "net/http" - "github.com/cosmos/cosmos-sdk/client/rest" - - "github.com/tendermint/tendermint/libs/common" - "github.com/gorilla/mux" "github.com/spf13/cobra" - abci "github.com/tendermint/tendermint/abci/types" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/spf13/viper" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/rest" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -100,26 +96,13 @@ func ValidateTxResult(cliCtx context.CLIContext, res *ctypes.ResultTx) error { return nil } -func formatTxResult(cdc *codec.Codec, res *ctypes.ResultTx) (Info, error) { +func formatTxResult(cdc *codec.Codec, res *ctypes.ResultTx) (sdk.TxResponse, error) { tx, err := parseTx(cdc, res.Tx) if err != nil { - return Info{}, err + return sdk.TxResponse{}, err } - return Info{ - Hash: res.Hash, - Height: res.Height, - Tx: tx, - Result: res.TxResult, - }, nil -} - -// Info is used to prepare info to display -type Info struct { - Hash common.HexBytes `json:"hash"` - Height int64 `json:"height"` - Tx sdk.Tx `json:"tx"` - Result abci.ResponseDeliverTx `json:"result"` + return sdk.NewResponseResultTx(res, tx), nil } func parseTx(cdc *codec.Codec, txBytes []byte) (sdk.Tx, error) { diff --git a/client/tx/search.go b/client/tx/search.go index 97beee075..91c3b32c4 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -106,7 +106,7 @@ $ gaiacli query txs --tags ':&:' --page 1 --limit 30 // SearchTxs performs a search for transactions for a given set of tags via // Tendermint RPC. It returns a slice of Info object containing txs and metadata. // An error is returned if the query fails. -func SearchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string, page, limit int) ([]Info, error) { +func SearchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string, page, limit int) ([]sdk.TxResponse, error) { if len(tags) == 0 { return nil, errors.New("must declare at least one tag to search") } @@ -153,9 +153,9 @@ func SearchTxs(cliCtx context.CLIContext, cdc *codec.Codec, tags []string, page, } // parse the indexed txs into an array of Info -func FormatTxResults(cdc *codec.Codec, res []*ctypes.ResultTx) ([]Info, error) { +func FormatTxResults(cdc *codec.Codec, res []*ctypes.ResultTx) ([]sdk.TxResponse, error) { var err error - out := make([]Info, len(res)) + out := make([]sdk.TxResponse, len(res)) for i := range res { out[i], err = formatTxResult(cdc, res[i]) if err != nil { @@ -173,7 +173,7 @@ func SearchTxRequestHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http. return func(w http.ResponseWriter, r *http.Request) { var tags []string var page, limit int - var txs []Info + var txs []sdk.TxResponse err := r.ParseForm() if err != nil { rest.WriteErrorResponse(w, http.StatusBadRequest, sdk.AppendMsgToErr("could not parse query parameters", err.Error())) diff --git a/client/utils/utils.go b/client/utils/utils.go index b5ae87eab..0123f0952 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -69,7 +69,8 @@ func CompleteAndBroadcastTxCLI(txBldr authtxb.TxBuilder, cliCtx context.CLIConte } // broadcast to a Tendermint node - _, err = cliCtx.BroadcastTx(txBytes) + res, err := cliCtx.BroadcastTx(txBytes) + cliCtx.PrintOutput(res) return err } diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 0b6d5790d..6c58b6bee 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -14,8 +14,6 @@ import ( "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/tests" @@ -272,14 +270,10 @@ func TestGaiaCLIGasAuto(t *testing.T) { require.NotEmpty(t, stderr) require.True(t, success) cdc := app.MakeCodec() - sendResp := struct { - Height int64 - TxHash string - Response abci.ResponseDeliverTx - }{} + sendResp := sdk.TxResponse{} err := cdc.UnmarshalJSON([]byte(stdout), &sendResp) require.Nil(t, err) - require.True(t, sendResp.Response.GasWanted >= sendResp.Response.GasUsed) + require.True(t, sendResp.GasWanted >= sendResp.GasUsed) tests.WaitForNextNBlocksTM(1, f.Port) // Check state has changed accordingly @@ -684,14 +678,12 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { success, stdout, _ = f.TxBroadcast(signedTxFile.Name()) require.True(t, success) - var result struct { - Response abci.ResponseDeliverTx - } + var result sdk.TxResponse // Unmarshal the response and ensure that gas was properly used require.Nil(t, app.MakeCodec().UnmarshalJSON([]byte(stdout), &result)) - require.True(t, msg.Fee.Gas >= uint64(result.Response.GasUsed)) - require.Equal(t, msg.Fee.Gas, uint64(result.Response.GasWanted)) + require.Equal(t, msg.Fee.Gas, uint64(result.GasUsed)) + require.Equal(t, msg.Fee.Gas, uint64(result.GasWanted)) tests.WaitForNextNBlocksTM(1, f.Port) // Ensure account state diff --git a/cmd/gaia/cli_test/test_helpers.go b/cmd/gaia/cli_test/test_helpers.go index b2288382f..93fae3689 100644 --- a/cmd/gaia/cli_test/test_helpers.go +++ b/cmd/gaia/cli_test/test_helpers.go @@ -14,7 +14,6 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" "github.com/cosmos/cosmos-sdk/client/keys" - "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" appInit "github.com/cosmos/cosmos-sdk/cmd/gaia/init" "github.com/cosmos/cosmos-sdk/codec" @@ -352,10 +351,10 @@ func (f *Fixtures) QueryAccount(address sdk.AccAddress, flags ...string) auth.Ba // gaiacli query txs // QueryTxs is gaiacli query txs -func (f *Fixtures) QueryTxs(page, limit int, tags ...string) []tx.Info { +func (f *Fixtures) QueryTxs(page, limit int, tags ...string) []sdk.TxResponse { cmd := fmt.Sprintf("gaiacli query txs --page=%d --limit=%d --tags='%s' %v", page, limit, queryTags(tags), f.Flags()) out, _ := tests.ExecuteT(f.T, cmd, "") - var txs []tx.Info + var txs []sdk.TxResponse cdc := app.MakeCodec() err := cdc.UnmarshalJSON([]byte(out), &txs) require.NoError(f.T, err, "out %v\n, err %v", out, err) diff --git a/types/result.go b/types/result.go index 924b73b49..f51de775b 100644 --- a/types/result.go +++ b/types/result.go @@ -1,6 +1,13 @@ package types -// Result is the union of ResponseDeliverTx and ResponseCheckTx. +import ( + "fmt" + "strings" + + ctypes "github.com/tendermint/tendermint/rpc/core/types" +) + +// Result is the union of ResponseFormat and ResponseCheckTx. type Result struct { // Code is the response code, is stored back on the chain. @@ -33,3 +40,104 @@ type Result struct { func (res Result) IsOK() bool { return res.Code.IsOK() } + +// Is a version of TxResponse where the tags are StringTags rather than []byte tags +type TxResponse struct { + Height int64 `json:"height"` + TxHash string `json:"txhash"` + Code uint32 `json:"code,omitempty"` + Data []byte `json:"data,omitempty"` + Log string `json:"log,omitempty"` + Info string `json:"info,omitempty"` + GasWanted int64 `json:"gas_wanted,omitempty"` + GasUsed int64 `json:"gas_used,omitempty"` + Tags StringTags `json:"tags,omitempty"` + Codespace string `json:"codespace,omitempty"` + Tx Tx `json:"tx,omitempty"` +} + +func NewResponseResultTx(res *ctypes.ResultTx, tx Tx) TxResponse { + return TxResponse{ + TxHash: res.Hash.String(), + Height: res.Height, + Code: res.TxResult.Code, + Data: res.TxResult.Data, + Log: res.TxResult.Log, + Info: res.TxResult.Info, + GasWanted: res.TxResult.GasWanted, + GasUsed: res.TxResult.GasUsed, + Tags: TagsToStringTags(res.TxResult.Tags), + Tx: tx, + } +} + +func NewResponseFormatBroadcastTxCommit(res *ctypes.ResultBroadcastTxCommit) TxResponse { + return TxResponse{ + Height: res.Height, + TxHash: res.Hash.String(), + Code: res.DeliverTx.Code, + Data: res.DeliverTx.Data, + Log: res.DeliverTx.Log, + Info: res.DeliverTx.Info, + GasWanted: res.DeliverTx.GasWanted, + GasUsed: res.DeliverTx.GasUsed, + Tags: TagsToStringTags(res.DeliverTx.Tags), + Codespace: res.DeliverTx.Codespace, + } +} + +func NewResponseFormatBroadcastTx(res *ctypes.ResultBroadcastTx) TxResponse { + return TxResponse{ + Code: res.Code, + Data: res.Data.Bytes(), + Log: res.Log, + TxHash: res.Hash.String(), + } +} + +func (r TxResponse) String() string { + var sb strings.Builder + sb.WriteString("Response:\n") + + if r.Height > 0 { + sb.WriteString(fmt.Sprintf(" Height: %d\n", r.Height)) + } + + if r.TxHash != "" { + sb.WriteString(fmt.Sprintf(" TxHash: %s\n", r.TxHash)) + } + + if r.Code > 0 { + sb.WriteString(fmt.Sprintf(" Code: %d\n", r.Code)) + } + + if r.Data != nil { + sb.WriteString(fmt.Sprintf(" Data: %s\n", string(r.Data))) + } + + if r.Log != "" { + sb.WriteString(fmt.Sprintf(" Log: %s\n", r.Log)) + } + + if r.Info != "" { + sb.WriteString(fmt.Sprintf(" Info: %s\n", r.Info)) + } + + if r.GasWanted != 0 { + sb.WriteString(fmt.Sprintf(" GasWanted: %d\n", r.GasWanted)) + } + + if r.GasUsed != 0 { + sb.WriteString(fmt.Sprintf(" GasUsed: %d\n", r.GasUsed)) + } + + if len(r.Tags) > 0 { + sb.WriteString(fmt.Sprintf(" Tags: \n%s\n", r.Tags.String())) + } + + if r.Codespace != "" { + sb.WriteString(fmt.Sprintf(" Codespace: %s\n", r.Codespace)) + } + + return strings.TrimSpace(sb.String()) +} diff --git a/types/tags.go b/types/tags.go index add0c0ad5..befabc36a 100644 --- a/types/tags.go +++ b/types/tags.go @@ -1,6 +1,9 @@ package types import ( + "fmt" + "strings" + cmn "github.com/tendermint/tendermint/libs/common" ) @@ -16,7 +19,7 @@ func EmptyTags() Tags { } // Append a single tag -func (t Tags) AppendTag(k string, v []byte) Tags { +func (t Tags) AppendTag(k string, v string) Tags { return append(t, MakeTag(k, v)) } @@ -41,15 +44,26 @@ func NewTags(tags ...interface{}) Tags { if i == len(tags) { break } - ret = append(ret, Tag{Key: []byte(tags[i].(string)), Value: tags[i+1].([]byte)}) + ret = append(ret, Tag{Key: toBytes(tags[i]), Value: toBytes(tags[i+1])}) i += 2 } return ret } +func toBytes(i interface{}) []byte { + switch x := i.(type) { + case []uint8: + return x + case string: + return []byte(x) + default: + panic(i) + } +} + // Make a tag from a key and a value -func MakeTag(k string, v []byte) Tag { - return Tag{Key: []byte(k), Value: v} +func MakeTag(k string, v string) Tag { + return Tag{Key: []byte(k), Value: []byte(v)} } //__________________________________________________ @@ -61,3 +75,41 @@ var ( TagDstValidator = "destination-validator" TagDelegator = "delegator" ) + +// A KVPair where the Key and Value are both strings, rather than []byte +type StringTag struct { + Key string `json:"key"` + Value string `json:"value,omitempty"` +} + +func (st StringTag) String() string { + return fmt.Sprintf("%s = %s", st.Key, st.Value) +} + +// A slice of StringTag +type StringTags []StringTag + +func (st StringTags) String() string { + var sb strings.Builder + for _, t := range st { + sb.WriteString(fmt.Sprintf(" - %s\n", t.String())) + } + return strings.TrimSpace(sb.String()) +} + +// Conversion function from a []byte tag to a string tag +func TagToStringTag(tag Tag) StringTag { + return StringTag{ + Key: string(tag.Key), + Value: string(tag.Value), + } +} + +// Conversion function from Tags to a StringTags +func TagsToStringTags(tags Tags) StringTags { + var stringTags StringTags + for _, tag := range tags { + stringTags = append(stringTags, TagToStringTag(tag)) + } + return stringTags +} diff --git a/types/tags_test.go b/types/tags_test.go index 77bb4041c..9ce59cf7f 100644 --- a/types/tags_test.go +++ b/types/tags_test.go @@ -7,11 +7,11 @@ import ( ) func TestAppendTags(t *testing.T) { - a := NewTags("a", []byte("1")) - b := NewTags("b", []byte("2")) + a := NewTags("a", "1") + b := NewTags("b", "2") c := a.AppendTags(b) - require.Equal(t, c, Tags{MakeTag("a", []byte("1")), MakeTag("b", []byte("2"))}) - require.Equal(t, c, Tags{MakeTag("a", []byte("1"))}.AppendTag("b", []byte("2"))) + require.Equal(t, c, Tags{MakeTag("a", "1"), MakeTag("b", "2")}) + require.Equal(t, c, Tags{MakeTag("a", "1")}.AppendTag("b", "2")) } func TestEmptyTags(t *testing.T) { @@ -20,16 +20,16 @@ func TestEmptyTags(t *testing.T) { } func TestNewTags(t *testing.T) { - b := NewTags("a", []byte("1")) - require.Equal(t, b, Tags{MakeTag("a", []byte("1"))}) + b := NewTags("a", "1") + require.Equal(t, b, Tags{MakeTag("a", "1")}) - require.Panics(t, func() { NewTags("a", []byte("1"), "b") }) + require.Panics(t, func() { NewTags("a", "1", "b") }) require.Panics(t, func() { NewTags("a", 1) }) require.Panics(t, func() { NewTags(1, 1) }) require.Panics(t, func() { NewTags(true, false) }) } func TestKVPairTags(t *testing.T) { - a := NewTags("a", []byte("1")) + a := NewTags("a", "1") require.Equal(t, a, Tags(a.ToKVPairs())) } diff --git a/x/bank/client/cli/broadcast.go b/x/bank/client/cli/broadcast.go index 1bcd811cd..48734e392 100644 --- a/x/bank/client/cli/broadcast.go +++ b/x/bank/client/cli/broadcast.go @@ -33,7 +33,8 @@ in place of an input filename, the command reads from standard input.`, return } - _, err = cliCtx.BroadcastTx(txBytes) + res, err := cliCtx.BroadcastTx(txBytes) + cliCtx.PrintOutput(res) return err }, } diff --git a/x/bank/keeper.go b/x/bank/keeper.go index 978507e5c..534facec5 100644 --- a/x/bank/keeper.go +++ b/x/bank/keeper.go @@ -248,7 +248,7 @@ func subtractCoins(ctx sdk.Context, ak auth.AccountKeeper, addr sdk.AccAddress, newCoins := oldCoins.Minus(amt) // should not panic as spendable coins was already checked err := setCoins(ctx, ak, addr, newCoins) - tags := sdk.NewTags(TagKeySender, []byte(addr.String())) + tags := sdk.NewTags(TagKeySender, addr.String()) return newCoins, tags, err } @@ -263,7 +263,7 @@ func addCoins(ctx sdk.Context, am auth.AccountKeeper, addr sdk.AccAddress, amt s } err := setCoins(ctx, am, addr, newCoins) - tags := sdk.NewTags(TagKeyRecipient, []byte(addr.String())) + tags := sdk.NewTags(TagKeyRecipient, addr.String()) return newCoins, tags, err } diff --git a/x/gov/handler.go b/x/gov/handler.go index 947ce5407..aee576dc2 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -27,7 +27,7 @@ func NewHandler(keeper Keeper) sdk.Handler { func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitProposal) sdk.Result { proposal := keeper.NewTextProposal(ctx, msg.Title, msg.Description, msg.ProposalType) proposalID := proposal.GetProposalID() - proposalIDBytes := []byte(fmt.Sprintf("%d", proposalID)) + proposalIDStr := fmt.Sprintf("%d", proposalID) err, votingStarted := keeper.AddDeposit(ctx, proposalID, msg.Proposer, msg.InitialDeposit) if err != nil { @@ -36,11 +36,11 @@ func handleMsgSubmitProposal(ctx sdk.Context, keeper Keeper, msg MsgSubmitPropos resTags := sdk.NewTags( tags.Proposer, []byte(msg.Proposer.String()), - tags.ProposalID, proposalIDBytes, + tags.ProposalID, proposalIDStr, ) if votingStarted { - resTags = resTags.AppendTag(tags.VotingPeriodStart, proposalIDBytes) + resTags = resTags.AppendTag(tags.VotingPeriodStart, proposalIDStr) } return sdk.Result{ @@ -55,14 +55,14 @@ func handleMsgDeposit(ctx sdk.Context, keeper Keeper, msg MsgDeposit) sdk.Result return err.Result() } - proposalIDBytes := []byte(fmt.Sprintf("%d", msg.ProposalID)) + proposalIDStr := fmt.Sprintf("%d", msg.ProposalID) resTags := sdk.NewTags( tags.Depositor, []byte(msg.Depositor.String()), - tags.ProposalID, proposalIDBytes, + tags.ProposalID, proposalIDStr, ) if votingStarted { - resTags = resTags.AppendTag(tags.VotingPeriodStart, proposalIDBytes) + resTags = resTags.AppendTag(tags.VotingPeriodStart, proposalIDStr) } return sdk.Result{ @@ -78,8 +78,8 @@ func handleMsgVote(ctx sdk.Context, keeper Keeper, msg MsgVote) sdk.Result { return sdk.Result{ Tags: sdk.NewTags( - tags.Voter, []byte(msg.Voter.String()), - tags.ProposalID, []byte(fmt.Sprintf("%d", msg.ProposalID)), + tags.Voter, msg.Voter.String(), + tags.ProposalID, fmt.Sprintf("%d", msg.ProposalID), ), } } @@ -99,7 +99,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) sdk.Tags { keeper.DeleteProposal(ctx, proposalID) keeper.DeleteDeposits(ctx, proposalID) // delete any associated deposits (burned) - resTags = resTags.AppendTag(tags.ProposalID, []byte(fmt.Sprintf("%d", proposalID))) + resTags = resTags.AppendTag(tags.ProposalID, fmt.Sprintf("%d", proposalID)) resTags = resTags.AppendTag(tags.ProposalResult, tags.ActionProposalDropped) logger.Info( @@ -122,7 +122,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) sdk.Tags { activeProposal := keeper.GetProposal(ctx, proposalID) passes, tallyResults := tally(ctx, keeper, activeProposal) - var tagValue []byte + var tagValue string if passes { keeper.RefundDeposits(ctx, activeProposal.GetProposalID()) activeProposal.SetStatus(StatusPassed) @@ -144,7 +144,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) sdk.Tags { ), ) - resTags = resTags.AppendTag(tags.ProposalID, []byte(fmt.Sprintf("%d", proposalID))) + resTags = resTags.AppendTag(tags.ProposalID, fmt.Sprintf("%d", proposalID)) resTags = resTags.AppendTag(tags.ProposalResult, tagValue) } diff --git a/x/gov/tags/tags.go b/x/gov/tags/tags.go index a4b58d8b2..6f18b2596 100644 --- a/x/gov/tags/tags.go +++ b/x/gov/tags/tags.go @@ -6,9 +6,9 @@ import ( // Governance tags var ( - ActionProposalDropped = []byte("proposal-dropped") - ActionProposalPassed = []byte("proposal-passed") - ActionProposalRejected = []byte("proposal-rejected") + ActionProposalDropped = "proposal-dropped" + ActionProposalPassed = "proposal-passed" + ActionProposalRejected = "proposal-rejected" Action = sdk.TagAction Proposer = "proposer" diff --git a/x/slashing/handler.go b/x/slashing/handler.go index 2bed8db94..d0a26ff76 100644 --- a/x/slashing/handler.go +++ b/x/slashing/handler.go @@ -56,7 +56,7 @@ func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result { tags := sdk.NewTags( tags.Action, tags.ActionValidatorUnjailed, - tags.Validator, []byte(msg.ValidatorAddr.String()), + tags.Validator, msg.ValidatorAddr.String(), ) return sdk.Result{ diff --git a/x/slashing/tags/tags.go b/x/slashing/tags/tags.go index 9ab5a48bd..12c1d616f 100644 --- a/x/slashing/tags/tags.go +++ b/x/slashing/tags/tags.go @@ -6,7 +6,7 @@ import ( // Slashing tags var ( - ActionValidatorUnjailed = []byte("validator-unjailed") + ActionValidatorUnjailed = "validator-unjailed" Action = sdk.TagAction Validator = "validator" diff --git a/x/staking/client/rest/query.go b/x/staking/client/rest/query.go index 2ffefbda1..88e625194 100644 --- a/x/staking/client/rest/query.go +++ b/x/staking/client/rest/query.go @@ -7,7 +7,6 @@ import ( "github.com/cosmos/cosmos-sdk/client/rest" "github.com/cosmos/cosmos-sdk/client/context" - "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking" @@ -145,7 +144,7 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han isBondTx := contains(typesQuerySlice, "bond") isUnbondTx := contains(typesQuerySlice, "unbond") isRedTx := contains(typesQuerySlice, "redelegate") - var txs = []tx.Info{} + var txs = []sdk.TxResponse{} var actions []string switch { @@ -153,16 +152,16 @@ func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Han actions = append(actions, staking.MsgDelegate{}.Type()) case isUnbondTx: actions = append(actions, staking.MsgUndelegate{}.Type()) - actions = append(actions, string(tags.ActionCompleteUnbonding)) + actions = append(actions, tags.ActionCompleteUnbonding) case isRedTx: actions = append(actions, staking.MsgBeginRedelegate{}.Type()) - actions = append(actions, string(tags.ActionCompleteRedelegation)) + actions = append(actions, tags.ActionCompleteRedelegation) case noQuery: actions = append(actions, staking.MsgDelegate{}.Type()) actions = append(actions, staking.MsgUndelegate{}.Type()) - actions = append(actions, string(tags.ActionCompleteUnbonding)) + actions = append(actions, tags.ActionCompleteUnbonding) actions = append(actions, staking.MsgBeginRedelegate{}.Type()) - actions = append(actions, string(tags.ActionCompleteRedelegation)) + actions = append(actions, tags.ActionCompleteRedelegation) default: w.WriteHeader(http.StatusNoContent) return diff --git a/x/staking/client/rest/utils.go b/x/staking/client/rest/utils.go index c9e2ed9d9..3efd05d54 100644 --- a/x/staking/client/rest/utils.go +++ b/x/staking/client/rest/utils.go @@ -29,7 +29,7 @@ func contains(stringSlice []string, txType string) bool { } // queries staking txs -func queryTxs(node rpcclient.Client, cliCtx context.CLIContext, cdc *codec.Codec, tag string, delegatorAddr string) ([]tx.Info, error) { +func queryTxs(node rpcclient.Client, cliCtx context.CLIContext, cdc *codec.Codec, tag string, delegatorAddr string) ([]sdk.TxResponse, error) { page := 0 perPage := 100 prove := !cliCtx.TrustNode diff --git a/x/staking/handler.go b/x/staking/handler.go index 1b8813a19..3bf5ddf05 100644 --- a/x/staking/handler.go +++ b/x/staking/handler.go @@ -62,8 +62,8 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, sdk.T resTags.AppendTags(sdk.NewTags( tags.Action, ActionCompleteUnbonding, - tags.Delegator, []byte(dvPair.DelegatorAddr.String()), - tags.SrcValidator, []byte(dvPair.ValidatorAddr.String()), + tags.Delegator, dvPair.DelegatorAddr.String(), + tags.SrcValidator, dvPair.ValidatorAddr.String(), )) } @@ -78,9 +78,9 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) ([]abci.ValidatorUpdate, sdk.T resTags.AppendTags(sdk.NewTags( tags.Action, tags.ActionCompleteRedelegation, - tags.Delegator, []byte(dvvTriplet.DelegatorAddr.String()), - tags.SrcValidator, []byte(dvvTriplet.ValidatorSrcAddr.String()), - tags.DstValidator, []byte(dvvTriplet.ValidatorDstAddr.String()), + tags.Delegator, dvvTriplet.DelegatorAddr.String(), + tags.SrcValidator, dvvTriplet.ValidatorSrcAddr.String(), + tags.DstValidator, dvvTriplet.ValidatorDstAddr.String(), )) } @@ -145,9 +145,9 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k } tags := sdk.NewTags( - tags.DstValidator, []byte(msg.ValidatorAddr.String()), - tags.Moniker, []byte(msg.Description.Moniker), - tags.Identity, []byte(msg.Description.Identity), + tags.DstValidator, msg.ValidatorAddr.String(), + tags.Moniker, msg.Description.Moniker, + tags.Identity, msg.Description.Identity, ) return sdk.Result{ @@ -183,9 +183,9 @@ func handleMsgEditValidator(ctx sdk.Context, msg types.MsgEditValidator, k keepe k.SetValidator(ctx, validator) tags := sdk.NewTags( - tags.DstValidator, []byte(msg.ValidatorAddr.String()), - tags.Moniker, []byte(description.Moniker), - tags.Identity, []byte(description.Identity), + tags.DstValidator, msg.ValidatorAddr.String(), + tags.Moniker, description.Moniker, + tags.Identity, description.Identity, ) return sdk.Result{ @@ -213,8 +213,8 @@ func handleMsgDelegate(ctx sdk.Context, msg types.MsgDelegate, k keeper.Keeper) } tags := sdk.NewTags( - tags.Delegator, []byte(msg.DelegatorAddr.String()), - tags.DstValidator, []byte(msg.ValidatorAddr.String()), + tags.Delegator, msg.DelegatorAddr.String(), + tags.DstValidator, msg.ValidatorAddr.String(), ) return sdk.Result{ @@ -230,9 +230,9 @@ func handleMsgUndelegate(ctx sdk.Context, msg types.MsgUndelegate, k keeper.Keep finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(completionTime) tags := sdk.NewTags( - tags.Delegator, []byte(msg.DelegatorAddr.String()), - tags.SrcValidator, []byte(msg.ValidatorAddr.String()), - tags.EndTime, []byte(completionTime.Format(time.RFC3339)), + tags.Delegator, msg.DelegatorAddr.String(), + tags.SrcValidator, msg.ValidatorAddr.String(), + tags.EndTime, completionTime.Format(time.RFC3339), ) return sdk.Result{Data: finishTime, Tags: tags} @@ -247,10 +247,10 @@ func handleMsgBeginRedelegate(ctx sdk.Context, msg types.MsgBeginRedelegate, k k finishTime := types.MsgCdc.MustMarshalBinaryLengthPrefixed(completionTime) resTags := sdk.NewTags( - tags.Delegator, []byte(msg.DelegatorAddr.String()), - tags.SrcValidator, []byte(msg.ValidatorSrcAddr.String()), - tags.DstValidator, []byte(msg.ValidatorDstAddr.String()), - tags.EndTime, []byte(completionTime.Format(time.RFC3339)), + tags.Delegator, msg.DelegatorAddr.String(), + tags.SrcValidator, msg.ValidatorSrcAddr.String(), + tags.DstValidator, msg.ValidatorDstAddr.String(), + tags.EndTime, completionTime.Format(time.RFC3339), ) return sdk.Result{Data: finishTime, Tags: resTags} diff --git a/x/staking/tags/tags.go b/x/staking/tags/tags.go index e8f42d9e0..a99135171 100644 --- a/x/staking/tags/tags.go +++ b/x/staking/tags/tags.go @@ -6,8 +6,8 @@ import ( ) var ( - ActionCompleteUnbonding = []byte("complete-unbonding") - ActionCompleteRedelegation = []byte("complete-redelegation") + ActionCompleteUnbonding = "complete-unbonding" + ActionCompleteRedelegation = "complete-redelegation" Action = sdk.TagAction SrcValidator = sdk.TagSrcValidator From f5ada58780213458b48d89d75a6256d26afc6446 Mon Sep 17 00:00:00 2001 From: Juan Leni Date: Tue, 5 Feb 2019 17:22:56 +0100 Subject: [PATCH 53/99] Merge PR #3461: GaiaCLI - refactor/fix --account and --index --- client/input.go | 6 - client/keys/add.go | 373 ++++++++++++++---------------- client/keys/delete.go | 4 +- client/keys/errors.go | 18 +- client/keys/mnemonic.go | 7 +- client/keys/types.go | 38 +++ client/keys/utils.go | 9 - client/lcd/lcd_test.go | 96 ++++++-- client/lcd/test_helpers.go | 103 +++------ client/rest/types.go | 59 +++++ cmd/gaia/cli_test/cli_test.go | 19 +- cmd/gaia/cli_test/test_helpers.go | 8 +- crypto/keys/hd/hdpath.go | 40 ++-- crypto/keys/hd/hdpath_test.go | 70 +++++- crypto/keys/keybase.go | 59 ++--- crypto/keys/keybase_test.go | 2 +- crypto/keys/types.go | 34 ++- crypto/ledger_secp256k1.go | 12 +- crypto/ledger_test.go | 10 +- 19 files changed, 559 insertions(+), 408 deletions(-) create mode 100644 client/keys/types.go diff --git a/client/input.go b/client/input.go index 46c838e2e..631e13c69 100644 --- a/client/input.go +++ b/client/input.go @@ -42,12 +42,6 @@ func GetPassword(prompt string, buf *bufio.Reader) (pass string, err error) { return pass, nil } -// GetSeed will request a seed phrase from stdin and trims off -// leading/trailing spaces -func GetSeed(prompt string, buf *bufio.Reader) (string, error) { - return GetString(prompt, buf) -} - // GetCheckPassword will prompt for a password twice to verify they // match (for creating a new password). // It enforces the password length. Only parses password once if diff --git a/client/keys/add.go b/client/keys/add.go index b3af588dc..ca5b58da4 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -9,26 +9,24 @@ import ( "os" "sort" - "github.com/tendermint/tendermint/crypto/multisig" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "github.com/cosmos/cosmos-sdk/crypto/keys" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/go-bip39" "github.com/gorilla/mux" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/libs/cli" - "github.com/cosmos/cosmos-sdk/client" - ccrypto "github.com/cosmos/cosmos-sdk/crypto" - "github.com/cosmos/cosmos-sdk/crypto/keys" - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/multisig" + "github.com/tendermint/tendermint/libs/cli" ) const ( flagInteractive = "interactive" - flagBIP44Path = "bip44-path" flagRecover = "recover" flagNoBackup = "no-backup" flagDryRun = "dry-run" @@ -38,6 +36,11 @@ const ( flagNoSort = "nosort" ) +const ( + maxValidAccountValue = int(0x80000000 - 1) + maxValidIndexalue = int(0x80000000 - 1) +) + func addKeyCommand() *cobra.Command { cmd := &cobra.Command{ Use: "add ", @@ -68,7 +71,6 @@ the flag --nosort is set. cmd.Flags().String(FlagPublicKey, "", "Parse a public key in bech32 format and save it to disk") cmd.Flags().BoolP(flagInteractive, "i", false, "Interactively prompt user for BIP39 passphrase and mnemonic") cmd.Flags().Bool(client.FlagUseLedger, false, "Store a local reference to a private key on a Ledger device") - cmd.Flags().String(flagBIP44Path, "44'/118'/0'/0/0", "BIP44 path from which to derive a private key") cmd.Flags().Bool(flagRecover, false, "Provide seed phrase to recover existing key instead of creating") cmd.Flags().Bool(flagNoBackup, false, "Don't print out seed phrase (if others are watching the terminal)") cmd.Flags().Bool(flagDryRun, false, "Perform action, but don't add key to local keystore") @@ -95,24 +97,25 @@ func runAddCmd(cmd *cobra.Command, args []string) error { name := args[0] interactive := viper.GetBool(flagInteractive) + showMnemonic := viper.GetBool(flagNoBackup) if viper.GetBool(flagDryRun) { // we throw this away, so don't enforce args, // we want to get a new random seed phrase quickly kb = client.MockKeyBase() - encryptPassword = "throwing-this-key-away" + encryptPassword = app.DefaultKeyPass } else { kb, err = GetKeyBaseWithWritePerm() if err != nil { return err } - _, err := kb.Get(name) + _, err = kb.Get(name) if err == nil { // account exists, ask for user confirmation - if response, err := client.GetConfirmation( - fmt.Sprintf("override the existing name %s", name), buf); err != nil || !response { - return err + if response, err2 := client.GetConfirmation( + fmt.Sprintf("override the existing name %s", name), buf); err2 != nil || !response { + return err2 } } @@ -144,6 +147,7 @@ func runAddCmd(cmd *cobra.Command, args []string) error { if _, err := kb.CreateOffline(name, pk); err != nil { return err } + fmt.Fprintf(os.Stderr, "Key %q saved to disk.", name) return nil } @@ -164,51 +168,37 @@ func runAddCmd(cmd *cobra.Command, args []string) error { if err != nil { return err } - kb.CreateOffline(name, pk) + _, err = kb.CreateOffline(name, pk) + if err != nil { + return err + } return nil } - bipFlag := cmd.Flags().Lookup(flagBIP44Path) - bip44Params, err := getBIP44ParamsAndPath(bipFlag.Value.String(), bipFlag.Changed || !interactive) - if err != nil { - return err - } + account := uint32(viper.GetInt(flagAccount)) + index := uint32(viper.GetInt(flagIndex)) - // If we're using ledger, only thing we need is the path. So generate key and - // we're done. + // If we're using ledger, only thing we need is the path. So generate key and we're done. if viper.GetBool(client.FlagUseLedger) { - account := uint32(viper.GetInt(flagAccount)) - index := uint32(viper.GetInt(flagIndex)) - path := ccrypto.DerivationPath{44, 118, account, 0, index} - info, err := kb.CreateLedger(name, path, keys.Secp256k1) + info, err := kb.CreateLedger(name, keys.Secp256k1, account, index) if err != nil { return err } - printCreate(info, "") - return nil - } - - // Recover key from seed passphrase - if viper.GetBool(flagRecover) { - seed, err := client.GetSeed( - "Enter your recovery seed phrase:", buf) - if err != nil { - return err - } - info, err := kb.CreateKey(name, seed, encryptPassword) - if err != nil { - return err - } - // print out results without the seed phrase - viper.Set(flagNoBackup, true) - printCreate(info, "") - return nil + return printCreate(info, false, "") } + // Get bip39 mnemonic var mnemonic string - if interactive { - mnemonic, err = client.GetString("Enter your bip39 mnemonic, or hit enter to generate one.", buf) + var bip39Passphrase string + + if interactive || viper.GetBool(flagRecover) { + bip39Message := "Enter your bip39 mnemonic" + if !viper.GetBool(flagRecover) { + bip39Message = "Enter your bip39 mnemonic, or hit enter to generate one." + } + + mnemonic, err = client.GetString(bip39Message, buf) if err != nil { return err } @@ -227,8 +217,12 @@ func runAddCmd(cmd *cobra.Command, args []string) error { } } - // get bip39 passphrase - var bip39Passphrase string + if !bip39.IsMnemonicValid(mnemonic) { + fmt.Fprintf(os.Stderr, "Error: Mnemonic is not valid") + return nil + } + + // override bip39 passphrase if interactive { bip39Passphrase, err = client.GetString( "Enter your bip39 passphrase. This is combined with the mnemonic to derive the seed. "+ @@ -250,173 +244,158 @@ func runAddCmd(cmd *cobra.Command, args []string) error { } } - info, err := kb.Derive(name, mnemonic, bip39Passphrase, encryptPassword, *bip44Params) + info, err := kb.CreateAccount(name, mnemonic, keys.DefaultBIP39Passphrase, encryptPassword, account, index) if err != nil { return err } - printCreate(info, mnemonic) - return nil -} -func getBIP44ParamsAndPath(path string, flagSet bool) (*hd.BIP44Params, error) { - buf := client.BufferStdin() - bip44Path := path - - // if it wasn't set in the flag, give it a chance to overide interactively - if !flagSet { - var err error - - bip44Path, err = client.GetString(fmt.Sprintf("Enter your bip44 path. Default is %s\n", path), buf) - if err != nil { - return nil, err - } - - if len(bip44Path) == 0 { - bip44Path = path - } + // Recover key from seed passphrase + if viper.GetBool(flagRecover) { + // Hide mnemonic from output + showMnemonic = false + mnemonic = "" } - bip44params, err := hd.NewParamsFromPath(bip44Path) - if err != nil { - return nil, err - } - - return bip44params, nil + return printCreate(info, showMnemonic, mnemonic) } -func printCreate(info keys.Info, seed string) { +func printCreate(info keys.Info, showMnemonic bool, mnemonic string) error { output := viper.Get(cli.OutputFlag) + switch output { case "text": - fmt.Fprintln(os.Stderr, "") + fmt.Fprintln(os.Stderr) printKeyInfo(info, Bech32KeyOutput) - // print seed unless requested not to. - if !viper.GetBool(client.FlagUseLedger) && !viper.GetBool(flagNoBackup) { - fmt.Fprintln(os.Stderr, "\n**Important** write this seed phrase in a safe place.") + // print mnemonic unless requested not to. + if showMnemonic { + fmt.Fprintln(os.Stderr, "\n**Important** write this mnemonic phrase in a safe place.") fmt.Fprintln(os.Stderr, "It is the only way to recover your account if you ever forget your password.") - fmt.Fprintln(os.Stderr) - fmt.Fprintln(os.Stderr, seed) + fmt.Fprintln(os.Stderr, "") + fmt.Fprintln(os.Stderr, mnemonic) } case "json": out, err := Bech32KeyOutput(info) if err != nil { - panic(err) + return err } - if !viper.GetBool(flagNoBackup) { - out.Seed = seed + + if showMnemonic { + out.Mnemonic = mnemonic } + var jsonString []byte if viper.GetBool(client.FlagIndentResponse) { jsonString, err = cdc.MarshalJSONIndent(out, "", " ") } else { jsonString, err = cdc.MarshalJSON(out) } + if err != nil { - panic(err) // really shouldn't happen... + return err } fmt.Fprintln(os.Stderr, string(jsonString)) default: - panic(fmt.Sprintf("I can't speak: %s", output)) + return errors.Errorf("I can't speak: %s", output) } -} -// function to just a new seed to display in the UI before actually persisting it in the keybase -func getSeed(algo keys.SigningAlgo) string { - kb := client.MockKeyBase() - pass := "throwing-this-key-away" - name := "inmemorykey" - _, seed, _ := kb.CreateMnemonic(name, keys.English, pass, algo) - return seed -} - -func printPrefixed(msg string) { - fmt.Fprintln(os.Stderr, msg) -} - -func printStep() { - printPrefixed("-------------------------------------") + return nil } ///////////////////////////// // REST -// new key request REST body -type NewKeyBody struct { - Name string `json:"name"` - Password string `json:"password"` - Seed string `json:"seed"` +// function to just create a new seed to display in the UI before actually persisting it in the keybase +func generateMnemonic(algo keys.SigningAlgo) string { + kb := client.MockKeyBase() + pass := app.DefaultKeyPass + name := "inmemorykey" + _, seed, _ := kb.CreateMnemonic(name, keys.English, pass, algo) + return seed +} + +// CheckAndWriteErrorResponse will check for errors and return +// a given error message when corresponding +//TODO: Move to utils/rest or similar +func CheckAndWriteErrorResponse(w http.ResponseWriter, httpErr int, err error) bool { + if err != nil { + w.WriteHeader(httpErr) + _, _ = w.Write([]byte(err.Error())) + return true + } + return false } // add new key REST handler func AddNewKeyRequestHandler(indent bool) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var kb keys.Keybase - var m NewKeyBody + var m AddNewKey kb, err := GetKeyBaseWithWritePerm() - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) { return } body, err := ioutil.ReadAll(r.Body) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) { return } + err = json.Unmarshal(body, &m) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) { return } + + // Check parameters if m.Name == "" { - w.WriteHeader(http.StatusBadRequest) - err = errMissingName() - w.Write([]byte(err.Error())) + CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingName()) return } if m.Password == "" { - w.WriteHeader(http.StatusBadRequest) - err = errMissingPassword() - w.Write([]byte(err.Error())) + CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingPassword()) return } - // check if already exists - infos, err := kb.List() - for _, info := range infos { - if info.GetName() == m.Name { - w.WriteHeader(http.StatusConflict) - err = errKeyNameConflict(m.Name) - w.Write([]byte(err.Error())) - return - } + mnemonic := m.Mnemonic + // if mnemonic is empty, generate one + if mnemonic == "" { + mnemonic = generateMnemonic(keys.Secp256k1) + } + if !bip39.IsMnemonicValid(mnemonic) { + CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidMnemonic()) + } + + if m.Account < 0 || m.Account > maxValidAccountValue { + CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidAccountNumber()) + return + } + + if m.Index < 0 || m.Index > maxValidIndexalue { + CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidIndexNumber()) + return + } + + _, err = kb.Get(m.Name) + if err == nil { + CheckAndWriteErrorResponse(w, http.StatusConflict, errKeyNameConflict(m.Name)) + return } // create account - seed := m.Seed - if seed == "" { - seed = getSeed(keys.Secp256k1) - } - info, err := kb.CreateKey(m.Name, seed, m.Password) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + account := uint32(m.Account) + index := uint32(m.Index) + info, err := kb.CreateAccount(m.Name, mnemonic, keys.DefaultBIP39Passphrase, m.Password, account, index) + if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) { return } keyOutput, err := Bech32KeyOutput(info) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) { return } - keyOutput.Seed = seed + keyOutput.Mnemonic = mnemonic PostProcessResponse(w, cdc, keyOutput, indent) } @@ -426,22 +405,17 @@ func AddNewKeyRequestHandler(indent bool) http.HandlerFunc { func SeedRequestHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) algoType := vars["type"] + // algo type defaults to secp256k1 if algoType == "" { algoType = "secp256k1" } - algo := keys.SigningAlgo(algoType) - seed := getSeed(algo) + algo := keys.SigningAlgo(algoType) + seed := generateMnemonic(algo) w.Header().Set("Content-Type", "application/json") - w.Write([]byte(seed)) -} - -// RecoverKeyBody is recover key request REST body -type RecoverKeyBody struct { - Password string `json:"password"` - Seed string `json:"seed"` + _, _ = w.Write([]byte(seed)) } // RecoverRequestHandler performs key recover request @@ -449,67 +423,66 @@ func RecoverRequestHandler(indent bool) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) name := vars["name"] - var m RecoverKeyBody + var m RecoverKey + body, err := ioutil.ReadAll(r.Body) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) - return - } - err = cdc.UnmarshalJSON(body, &m) - if err != nil { - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte(err.Error())) + if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) { return } - if name == "" { - w.WriteHeader(http.StatusBadRequest) - err = errMissingName() - w.Write([]byte(err.Error())) - return - } - if m.Password == "" { - w.WriteHeader(http.StatusBadRequest) - err = errMissingPassword() - w.Write([]byte(err.Error())) - return - } - if m.Seed == "" { - w.WriteHeader(http.StatusBadRequest) - err = errMissingSeed() - w.Write([]byte(err.Error())) + err = cdc.UnmarshalJSON(body, &m) + if CheckAndWriteErrorResponse(w, http.StatusBadRequest, err) { return } kb, err := GetKeyBaseWithWritePerm() - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) + + if name == "" { + CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingName()) return } - // check if already exists - infos, err := kb.List() - for _, info := range infos { - if info.GetName() == name { - w.WriteHeader(http.StatusConflict) - err = errKeyNameConflict(name) - w.Write([]byte(err.Error())) - return - } + if m.Password == "" { + CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingPassword()) + return } - info, err := kb.CreateKey(name, m.Seed, m.Password) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + mnemonic := m.Mnemonic + if !bip39.IsMnemonicValid(mnemonic) { + CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidMnemonic()) + } + + if m.Mnemonic == "" { + CheckAndWriteErrorResponse(w, http.StatusBadRequest, errMissingMnemonic()) + return + } + + if m.Account < 0 || m.Account > maxValidAccountValue { + CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidAccountNumber()) + return + } + + if m.Index < 0 || m.Index > maxValidIndexalue { + CheckAndWriteErrorResponse(w, http.StatusBadRequest, errInvalidIndexNumber()) + return + } + + _, err = kb.Get(name) + if err == nil { + CheckAndWriteErrorResponse(w, http.StatusConflict, errKeyNameConflict(name)) + return + } + + account := uint32(m.Account) + index := uint32(m.Index) + + info, err := kb.CreateAccount(name, mnemonic, keys.DefaultBIP39Passphrase, m.Password, account, index) + if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) { return } keyOutput, err := Bech32KeyOutput(info) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - w.Write([]byte(err.Error())) + if CheckAndWriteErrorResponse(w, http.StatusInternalServerError, err) { return } diff --git a/client/keys/delete.go b/client/keys/delete.go index 5f3ff4f09..0f8392270 100644 --- a/client/keys/delete.go +++ b/client/keys/delete.go @@ -13,8 +13,8 @@ import ( "github.com/gorilla/mux" "github.com/cosmos/cosmos-sdk/client" - keys "github.com/cosmos/cosmos-sdk/crypto/keys" - keyerror "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" + "github.com/cosmos/cosmos-sdk/crypto/keys" + "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" "github.com/spf13/cobra" ) diff --git a/client/keys/errors.go b/client/keys/errors.go index 9c6139d7a..a603b1d1a 100644 --- a/client/keys/errors.go +++ b/client/keys/errors.go @@ -3,7 +3,7 @@ package keys import "fmt" func errKeyNameConflict(name string) error { - return fmt.Errorf("acount with name %s already exists", name) + return fmt.Errorf("account with name %s already exists", name) } func errMissingName() error { @@ -14,6 +14,18 @@ func errMissingPassword() error { return fmt.Errorf("you have to specify a password for the locally stored account") } -func errMissingSeed() error { - return fmt.Errorf("you have to specify seed for key recover") +func errMissingMnemonic() error { + return fmt.Errorf("you have to specify a mnemonic for key recovery") +} + +func errInvalidMnemonic() error { + return fmt.Errorf("the mnemonic is invalid") +} + +func errInvalidAccountNumber() error { + return fmt.Errorf("the account number is invalid") +} + +func errInvalidIndexNumber() error { + return fmt.Errorf("the index number is invalid") } diff --git a/client/keys/mnemonic.go b/client/keys/mnemonic.go index 32a6856bb..ffa72fff4 100644 --- a/client/keys/mnemonic.go +++ b/client/keys/mnemonic.go @@ -4,11 +4,9 @@ import ( "crypto/sha256" "fmt" - "github.com/spf13/cobra" - - "github.com/cosmos/cosmos-sdk/client" - bip39 "github.com/bartekn/go-bip39" + "github.com/cosmos/cosmos-sdk/client" + "github.com/spf13/cobra" ) const ( @@ -58,7 +56,6 @@ func runMnemonicCmd(cmd *cobra.Command, args []string) error { // hash input entropy to get entropy seed hashedEntropy := sha256.Sum256([]byte(inputEntropy)) entropySeed = hashedEntropy[:] - printStep() } else { // read entropy seed straight from crypto.Rand var err error diff --git a/client/keys/types.go b/client/keys/types.go new file mode 100644 index 000000000..d4a1032c2 --- /dev/null +++ b/client/keys/types.go @@ -0,0 +1,38 @@ +package keys + +// used for outputting keys.Info over REST +type KeyOutput struct { + Name string `json:"name"` + Type string `json:"type"` + Address string `json:"address"` + PubKey string `json:"pub_key"` + Mnemonic string `json:"mnemonic,omitempty"` +} + +// AddNewKey request a new key +type AddNewKey struct { + Name string `json:"name"` + Password string `json:"password"` + Mnemonic string `json:"mnemonic"` + Account int `json:"account,string,omitempty"` + Index int `json:"index,string,omitempty"` +} + +// RecoverKeyBody recovers a key +type RecoverKey struct { + Password string `json:"password"` + Mnemonic string `json:"mnemonic"` + Account int `json:"account,string,omitempty"` + Index int `json:"index,string,omitempty"` +} + +// UpdateKeyReq requests updating a key +type UpdateKeyReq struct { + OldPassword string `json:"old_password"` + NewPassword string `json:"new_password"` +} + +// DeleteKeyReq requests deleting a key +type DeleteKeyReq struct { + Password string `json:"password"` +} diff --git a/client/keys/utils.go b/client/keys/utils.go index 78899f5f8..5f9bcfa59 100644 --- a/client/keys/utils.go +++ b/client/keys/utils.go @@ -118,15 +118,6 @@ func SetKeyBase(kb keys.Keybase) { keybase = kb } -// used for outputting keys.Info over REST -type KeyOutput struct { - Name string `json:"name"` - Type string `json:"type"` - Address string `json:"address"` - PubKey string `json:"pub_key"` - Seed string `json:"seed,omitempty"` -} - // create a list of KeyOutput in bech32 format func Bech32KeysOutput(infos []keys.Info) ([]KeyOutput, error) { kos := make([]KeyOutput, len(infos)) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index c9842c7eb..25860f79e 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -12,10 +12,9 @@ import ( "github.com/spf13/viper" "github.com/stretchr/testify/require" - client "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/rest" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" - "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" @@ -47,41 +46,100 @@ func init() { version.Version = os.Getenv("VERSION") } -func TestKeys(t *testing.T) { +func TestSeedsAreDifferent(t *testing.T) { addr, _ := CreateAddr(t, name1, pw, GetKeyBase(t)) cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() - // get new seed - seed := getKeysSeed(t, port) + mnemonic1 := getKeysSeed(t, port) + mnemonic2 := getKeysSeed(t, port) + + require.NotEqual(t, mnemonic1, mnemonic2) +} + +func TestKeyRecover(t *testing.T) { + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + + myName1 := "TestKeyRecover_1" + myName2 := "TestKeyRecover_2" + + mnemonic := getKeysSeed(t, port) + expectedInfo, _ := GetKeyBase(t).CreateAccount(myName1, mnemonic, "", pw, 0, 0) + expectedAddress := expectedInfo.GetAddress().String() + expectedPubKey := sdk.MustBech32ifyAccPub(expectedInfo.GetPubKey()) // recover key - doRecoverKey(t, port, name2, pw, seed) + doRecoverKey(t, port, myName2, pw, mnemonic, 0, 0) + + keys := getKeys(t, port) + + require.Equal(t, expectedAddress, keys[0].Address) + require.Equal(t, expectedPubKey, keys[0].PubKey) +} + +func TestKeyRecoverHDPath(t *testing.T) { + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + defer cleanup() + + mnemonic := getKeysSeed(t, port) + + for account := uint32(0); account < 50; account += 13 { + for index := uint32(0); index < 50; index += 15 { + name1Idx := fmt.Sprintf("name1_%d_%d", account, index) + name2Idx := fmt.Sprintf("name2_%d_%d", account, index) + + expectedInfo, _ := GetKeyBase(t).CreateAccount(name1Idx, mnemonic, "", pw, account, index) + expectedAddress := expectedInfo.GetAddress().String() + expectedPubKey := sdk.MustBech32ifyAccPub(expectedInfo.GetPubKey()) + + // recover key + doRecoverKey(t, port, name2Idx, pw, mnemonic, account, index) + + keysName2Idx := getKey(t, port, name2Idx) + + require.Equal(t, expectedAddress, keysName2Idx.Address) + require.Equal(t, expectedPubKey, keysName2Idx.PubKey) + } + } +} + +func TestKeys(t *testing.T) { + addr1, _ := CreateAddr(t, name1, pw, GetKeyBase(t)) + addr1Bech32 := addr1.String() + + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr1}) + defer cleanup() + + // get new seed & recover key + mnemonic2 := getKeysSeed(t, port) + doRecoverKey(t, port, name2, pw, mnemonic2, 0, 0) // add key - resp := doKeysPost(t, port, name3, pw, seed) + mnemonic3 := mnemonic2 + resp := doKeysPost(t, port, name3, pw, mnemonic3, 0, 0) - addrBech32 := addr.String() - addr2Bech32 := resp.Address - _, err := sdk.AccAddressFromBech32(addr2Bech32) + addr3Bech32 := resp.Address + _, err := sdk.AccAddressFromBech32(addr3Bech32) require.NoError(t, err, "Failed to return a correct bech32 address") // test if created account is the correct account - expectedInfo, _ := GetKeyBase(t).CreateKey(name3, seed, pw) - expectedAccount := sdk.AccAddress(expectedInfo.GetPubKey().Address().Bytes()) - require.Equal(t, expectedAccount.String(), addr2Bech32) + expectedInfo3, _ := GetKeyBase(t).CreateAccount(name3, mnemonic3, "", pw, 0, 0) + expectedAddress3 := sdk.AccAddress(expectedInfo3.GetPubKey().Address()).String() + require.Equal(t, expectedAddress3, addr3Bech32) // existing keys - keys := getKeys(t, port) - require.Equal(t, name1, keys[0].Name, "Did not serve keys name correctly") - require.Equal(t, addrBech32, keys[0].Address, "Did not serve keys Address correctly") - require.Equal(t, name2, keys[1].Name, "Did not serve keys name correctly") - require.Equal(t, addr2Bech32, keys[1].Address, "Did not serve keys Address correctly") + require.Equal(t, name1, getKey(t, port, name1).Name, "Did not serve keys name correctly") + require.Equal(t, addr1Bech32, getKey(t, port, name1).Address, "Did not serve keys Address correctly") + require.Equal(t, name2, getKey(t, port, name2).Name, "Did not serve keys name correctly") + require.Equal(t, addr3Bech32, getKey(t, port, name2).Address, "Did not serve keys Address correctly") + require.Equal(t, name3, getKey(t, port, name3).Name, "Did not serve keys name correctly") + require.Equal(t, addr3Bech32, getKey(t, port, name3).Address, "Did not serve keys Address correctly") // select key key := getKey(t, port, name3) require.Equal(t, name3, key.Name, "Did not serve keys name correctly") - require.Equal(t, addr2Bech32, key.Address, "Did not serve keys Address correctly") + require.Equal(t, addr3Bech32, key.Address, "Did not serve keys Address correctly") // update key updateKey(t, port, name3, pw, altPw, false) diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 58b6319a6..2c6223de3 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -15,15 +15,6 @@ import ( "strings" "testing" - "github.com/tendermint/tendermint/crypto/secp256k1" - ctypes "github.com/tendermint/tendermint/rpc/core/types" - - cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys" - authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" - "github.com/cosmos/cosmos-sdk/x/gov" - "github.com/cosmos/cosmos-sdk/x/slashing" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rest" @@ -32,12 +23,23 @@ import ( gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" + cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" + authRest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" + txbuilder "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" + bankRest "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + "github.com/cosmos/cosmos-sdk/x/gov" + govRest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" + "github.com/cosmos/cosmos-sdk/x/slashing" + slashingRest "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" "github.com/cosmos/cosmos-sdk/x/staking" + stakingRest "github.com/cosmos/cosmos-sdk/x/staking/client/rest" + stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/spf13/viper" "github.com/stretchr/testify/require" @@ -46,6 +48,7 @@ import ( tmcfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/tendermint/tendermint/libs/cli" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" @@ -53,18 +56,12 @@ import ( "github.com/tendermint/tendermint/p2p" pvm "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" + ctypes "github.com/tendermint/tendermint/rpc/core/types" tmrpc "github.com/tendermint/tendermint/rpc/lib/server" tmtypes "github.com/tendermint/tendermint/types" - txbuilder "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - - authRest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" - bankRest "github.com/cosmos/cosmos-sdk/x/bank/client/rest" distr "github.com/cosmos/cosmos-sdk/x/distribution" distrRest "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" - govRest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" - slashingRest "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" - stakingRest "github.com/cosmos/cosmos-sdk/x/staking/client/rest" ) // makePathname creates a unique pathname for each test. It will panic if it @@ -145,14 +142,6 @@ func CreateAddr(t *testing.T, name, password string, kb crkeys.Keybase) (sdk.Acc return sdk.AccAddress(info.GetPubKey().Address()), seed } -// Type that combines an Address with the pnemonic of the private key to that address -type AddrSeed struct { - Address sdk.AccAddress - Seed string - Name string - Password string -} - // CreateAddr adds multiple address to the key store and returns the addresses and associated seeds in lexographical order by address. // It also requires that the keys could be created. func CreateAddrs(t *testing.T, kb crkeys.Keybase, numAddrs int) (addrs []sdk.AccAddress, seeds, names, passwords []string) { @@ -169,7 +158,7 @@ func CreateAddrs(t *testing.T, kb crkeys.Keybase, numAddrs int) (addrs []sdk.Acc password := "1234567890" info, seed, err = kb.CreateMnemonic(name, crkeys.English, password, crkeys.Secp256k1) require.NoError(t, err) - addrSeeds = append(addrSeeds, AddrSeed{Address: sdk.AccAddress(info.GetPubKey().Address()), Seed: seed, Name: name, Password: password}) + addrSeeds = append(addrSeeds, rest.AddrSeed{Address: sdk.AccAddress(info.GetPubKey().Address()), Seed: seed, Name: name, Password: password}) } sort.Sort(addrSeeds) @@ -184,14 +173,14 @@ func CreateAddrs(t *testing.T, kb crkeys.Keybase, numAddrs int) (addrs []sdk.Acc return addrs, seeds, names, passwords } -// implement `Interface` in sort package. -type AddrSeedSlice []AddrSeed +// AddrSeedSlice implements `Interface` in sort package. +type AddrSeedSlice []rest.AddrSeed func (b AddrSeedSlice) Len() int { return len(b) } -// Sorts lexographically by Address +// Less sorts lexicographically by Address func (b AddrSeedSlice) Less(i, j int) bool { // bytes package already implements Comparable for []byte. switch bytes.Compare(b[i].Address.Bytes(), b[j].Address.Bytes()) { @@ -547,10 +536,11 @@ func getKeys(t *testing.T, port string) []keys.KeyOutput { } // POST /keys Create a new account locally -func doKeysPost(t *testing.T, port, name, password, seed string) keys.KeyOutput { - pk := postKeys{name, password, seed} +func doKeysPost(t *testing.T, port, name, password, mnemonic string, account int, index int) keys.KeyOutput { + pk := keys.AddNewKey{name, password, mnemonic, account, index} req, err := cdc.MarshalJSON(pk) require.NoError(t, err) + res, body := Request(t, port, "POST", "/keys", req) require.Equal(t, http.StatusOK, res.StatusCode, body) @@ -560,12 +550,6 @@ func doKeysPost(t *testing.T, port, name, password, seed string) keys.KeyOutput return resp } -type postKeys struct { - Name string `json:"name"` - Password string `json:"password"` - Seed string `json:"seed"` -} - // GET /keys/seed Create a new seed to create a new account defaultValidFor func getKeysSeed(t *testing.T, port string) string { res, body := Request(t, port, "GET", "/keys/seed", nil) @@ -577,14 +561,17 @@ func getKeysSeed(t *testing.T, port string) string { return body } -// POST /keys/{name}/recover Recover a account from a seed -func doRecoverKey(t *testing.T, port, recoverName, recoverPassword, seed string) { - jsonStr := []byte(fmt.Sprintf(`{"password":"%s", "seed":"%s"}`, recoverPassword, seed)) - res, body := Request(t, port, "POST", fmt.Sprintf("/keys/%s/recover", recoverName), jsonStr) +// POST /keys/{name}/recove Recover a account from a seed +func doRecoverKey(t *testing.T, port, recoverName, recoverPassword, mnemonic string, account uint32, index uint32) { + pk := keys.RecoverKey{recoverPassword, mnemonic, int(account), int(index)} + req, err := cdc.MarshalJSON(pk) + require.NoError(t, err) + + res, body := Request(t, port, "POST", fmt.Sprintf("/keys/%s/recover", recoverName), req) require.Equal(t, http.StatusOK, res.StatusCode, body) var resp keys.KeyOutput - err := codec.Cdc.UnmarshalJSON([]byte(body), &resp) + err = codec.Cdc.UnmarshalJSON([]byte(body), &resp) require.Nil(t, err, body) addr1Bech32 := resp.Address @@ -604,7 +591,7 @@ func getKey(t *testing.T, port, name string) keys.KeyOutput { // PUT /keys/{name} Update the password for this account in the KMS func updateKey(t *testing.T, port, name, oldPassword, newPassword string, fail bool) { - kr := updateKeyReq{oldPassword, newPassword} + kr := keys.UpdateKeyReq{oldPassword, newPassword} req, err := cdc.MarshalJSON(kr) require.NoError(t, err) keyEndpoint := fmt.Sprintf("/keys/%s", name) @@ -616,14 +603,9 @@ func updateKey(t *testing.T, port, name, oldPassword, newPassword string, fail b require.Equal(t, http.StatusOK, res.StatusCode, body) } -type updateKeyReq struct { - OldPassword string `json:"old_password"` - NewPassword string `json:"new_password"` -} - // DELETE /keys/{name} Remove an account func deleteKey(t *testing.T, port, name, password string) { - dk := deleteKeyReq{password} + dk := keys.DeleteKeyReq{password} req, err := cdc.MarshalJSON(dk) require.NoError(t, err) keyEndpoint := fmt.Sprintf("/keys/%s", name) @@ -631,10 +613,6 @@ func deleteKey(t *testing.T, port, name, password string) { require.Equal(t, http.StatusOK, res.StatusCode, body) } -type deleteKeyReq struct { - Password string `json:"password"` -} - // GET /auth/accounts/{address} Get the account information on blockchain func getAccount(t *testing.T, port string, addr sdk.AccAddress) auth.Account { res, body := Request(t, port, "GET", fmt.Sprintf("/auth/accounts/%s", addr.String()), nil) @@ -668,7 +646,7 @@ func doSign(t *testing.T, port, name, password, chainID string, accnum, sequence // POST /tx/broadcast Send a signed Tx func doBroadcast(t *testing.T, port string, msg auth.StdTx) sdk.TxResponse { - tx := broadcastReq{Tx: msg, Return: "block"} + tx := rest.BroadcastReq{Tx: msg, Return: "block"} req, err := cdc.MarshalJSON(tx) require.Nil(t, err) res, body := Request(t, port, "POST", "/tx/broadcast", req) @@ -678,11 +656,6 @@ func doBroadcast(t *testing.T, port string, msg auth.StdTx) sdk.TxResponse { return resultTx } -type broadcastReq struct { - Tx auth.StdTx `json:"tx"` - Return string `json:"return"` -} - // GET /bank/balances/{address} Get the account balances // POST /bank/accounts/{address}/transfers Send coins (build -> sign -> send) @@ -726,7 +699,7 @@ func doTransferWithGas( generateOnly, simulate, ) - sr := sendReq{ + sr := rest.SendReq{ Amount: sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 1)}, BaseReq: baseReq, } @@ -759,7 +732,7 @@ func doTransferWithGasAccAuto( fmt.Sprintf("%f", gasAdjustment), 0, 0, fees, nil, generateOnly, simulate, ) - sr := sendReq{ + sr := rest.SendReq{ Amount: sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 1)}, BaseReq: baseReq, } @@ -859,7 +832,7 @@ func doBeginRedelegation(t *testing.T, port, name, password string, chainID := viper.GetString(client.FlagChainID) baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) - msg := msgBeginRedelegateInput{ + msg := rest.MsgBeginRedelegateInput{ BaseReq: baseReq, DelegatorAddr: delAddr, ValidatorSrcAddr: valSrcAddr, @@ -1090,7 +1063,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA chainID := viper.GetString(client.FlagChainID) baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) - pr := postProposalReq{ + pr := rest.PostProposalReq{ Title: "Test", Description: "test", ProposalType: "Text", @@ -1186,7 +1159,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk chainID := viper.GetString(client.FlagChainID) baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) - dr := depositReq{ + dr := rest.DepositReq{ Depositor: proposerAddr, Amount: sdk.Coins{sdk.NewCoin(stakingTypes.DefaultBondDenom, sdk.NewInt(amount))}, BaseReq: baseReq, @@ -1240,7 +1213,7 @@ func doVote(t *testing.T, port, seed, name, password string, proposerAddr sdk.Ac chainID := viper.GetString(client.FlagChainID) baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", accnum, sequence, fees, nil, false, false) - vr := voteReq{ + vr := rest.VoteReq{ Voter: proposerAddr, Option: option, BaseReq: baseReq, @@ -1372,7 +1345,7 @@ func doUnjail(t *testing.T, port, seed, name, password string, chainID := viper.GetString(client.FlagChainID) baseReq := rest.NewBaseReq(name, password, "", chainID, "", "", 1, 1, fees, nil, false, false) - ur := unjailReq{ + ur := rest.UnjailReq{ BaseReq: baseReq, } req, err := cdc.MarshalJSON(ur) diff --git a/client/rest/types.go b/client/rest/types.go index cb409627d..0595042ba 100644 --- a/client/rest/types.go +++ b/client/rest/types.go @@ -8,6 +8,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" ) // GasEstimateResponse defines a response definition for tx gas estimation. @@ -126,3 +127,61 @@ func ReadRESTReq(w http.ResponseWriter, r *http.Request, cdc *codec.Codec, req i return true } + +// AddrSeed combines an Address with the mnemonic of the private key to that address +type AddrSeed struct { + Address sdk.AccAddress + Seed string + Name string + Password string +} + +// SendReq requests sending an amount of coins +type SendReq struct { + Amount sdk.Coins `json:"amount"` + BaseReq BaseReq `json:"base_req"` +} + +// MsgBeginRedelegateInput request to begin a redelegation +type MsgBeginRedelegateInput struct { + BaseReq BaseReq `json:"base_req"` + DelegatorAddr sdk.AccAddress `json:"delegator_addr"` // in bech32 + ValidatorSrcAddr sdk.ValAddress `json:"validator_src_addr"` // in bech32 + ValidatorDstAddr sdk.ValAddress `json:"validator_dst_addr"` // in bech32 + SharesAmount sdk.Dec `json:"shares"` +} + +// PostProposalReq requests a proposals +type PostProposalReq struct { + BaseReq BaseReq `json:"base_req"` + Title string `json:"title"` // Title of the proposal + Description string `json:"description"` // Description of the proposal + ProposalType string `json:"proposal_type"` // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} + Proposer sdk.AccAddress `json:"proposer"` // Address of the proposer + InitialDeposit sdk.Coins `json:"initial_deposit"` // Coins to add to the proposal's deposit +} + +// BroadcastReq requests broadcasting a transaction +type BroadcastReq struct { + Tx auth.StdTx `json:"tx"` + Return string `json:"return"` +} + +// DepositReq requests a deposit of an amount of coins +type DepositReq struct { + BaseReq BaseReq `json:"base_req"` + Depositor sdk.AccAddress `json:"depositor"` // Address of the depositor + Amount sdk.Coins `json:"amount"` // Coins to add to the proposal's deposit +} + +// VoteReq requests sending a vote +type VoteReq struct { + BaseReq BaseReq `json:"base_req"` + Voter sdk.AccAddress `json:"voter"` // address of the voter + Option string `json:"option"` // option from OptionSet chosen by the voter +} + +// UnjailReq request unjailing +type UnjailReq struct { + BaseReq BaseReq `json:"base_req"` +} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 6c58b6bee..2139f1fd0 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -48,7 +48,24 @@ func TestGaiaCLIKeysAddRecover(t *testing.T) { f := InitFixtures(t) f.KeysAddRecover("test-recover", "dentist task convince chimney quality leave banana trade firm crawl eternal easily") - require.Equal(t, f.KeyAddress("test-recover").String(), "cosmos1qcfdf69js922qrdr4yaww3ax7gjml6pdds46f4") + require.Equal(t, "cosmos1qcfdf69js922qrdr4yaww3ax7gjml6pdds46f4", f.KeyAddress("test-recover").String()) +} + +func TestGaiaCLIKeysAddRecoverHDPath(t *testing.T) { + t.Parallel() + f := InitFixtures(t) + + f.KeysAddRecoverHDPath("test-recoverHD1", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 0, 0) + require.Equal(t, "cosmos1qcfdf69js922qrdr4yaww3ax7gjml6pdds46f4", f.KeyAddress("test-recoverHD1").String()) + + f.KeysAddRecoverHDPath("test-recoverH2", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 1, 5) + require.Equal(t, "cosmos1pdfav2cjhry9k79nu6r8kgknnjtq6a7rykmafy", f.KeyAddress("test-recoverH2").String()) + + f.KeysAddRecoverHDPath("test-recoverH3", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 1, 17) + require.Equal(t, "cosmos1909k354n6wl8ujzu6kmh49w4d02ax7qvlkv4sn", f.KeyAddress("test-recoverH3").String()) + + f.KeysAddRecoverHDPath("test-recoverH4", "dentist task convince chimney quality leave banana trade firm crawl eternal easily", 2, 17) + require.Equal(t, "cosmos1v9plmhvyhgxk3th9ydacm7j4z357s3nhtwsjat", f.KeyAddress("test-recoverH4").String()) } func TestGaiaCLIMinimumFees(t *testing.T) { diff --git a/cmd/gaia/cli_test/test_helpers.go b/cmd/gaia/cli_test/test_helpers.go index 93fae3689..5a206a997 100644 --- a/cmd/gaia/cli_test/test_helpers.go +++ b/cmd/gaia/cli_test/test_helpers.go @@ -225,12 +225,18 @@ func (f *Fixtures) KeysAdd(name string, flags ...string) { executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass) } -// KeysAdd is gaiacli keys add --recover +// KeysAddRecover prepares gaiacli keys add --recover func (f *Fixtures) KeysAddRecover(name, mnemonic string, flags ...string) { cmd := fmt.Sprintf("gaiacli keys add --home=%s --recover %s", f.GCLIHome, name) executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass, mnemonic) } +// KeysAddRecoverHDPath prepares gaiacli keys add --recover --account --index +func (f *Fixtures) KeysAddRecoverHDPath(name, mnemonic string, account uint32, index uint32, flags ...string) { + cmd := fmt.Sprintf("gaiacli keys add --home=%s --recover %s --account %d --index %d", f.GCLIHome, name, account, index) + executeWriteCheckErr(f.T, addFlags(cmd, flags), app.DefaultKeyPass, mnemonic) +} + // KeysShow is gaiacli keys show func (f *Fixtures) KeysShow(name string, flags ...string) keys.KeyOutput { cmd := fmt.Sprintf("gaiacli keys show --home=%s %s", f.GCLIHome, name) diff --git a/crypto/keys/hd/hdpath.go b/crypto/keys/hd/hdpath.go index 112abe0b6..050b0a39e 100644 --- a/crypto/keys/hd/hdpath.go +++ b/crypto/keys/hd/hdpath.go @@ -14,6 +14,7 @@ package hd import ( "crypto/hmac" "crypto/sha512" + "encoding/binary" "errors" "fmt" @@ -62,19 +63,7 @@ func NewParamsFromPath(path string) (*BIP44Params, error) { return nil, fmt.Errorf("path length is wrong. Expected 5, got %d", len(spl)) } - if spl[0] != "44'" { - return nil, fmt.Errorf("first field in path must be 44', got %v", spl[0]) - } - - if !isHardened(spl[1]) || !isHardened(spl[2]) { - return nil, - fmt.Errorf("second and third field in path must be hardened (ie. contain the suffix ', got %v and %v", spl[1], spl[2]) - } - if isHardened(spl[3]) || isHardened(spl[4]) { - return nil, - fmt.Errorf("fourth and fifth field in path must not be hardened (ie. not contain the suffix ', got %v and %v", spl[3], spl[4]) - } - + // Check items can be parsed purpose, err := hardenedInt(spl[0]) if err != nil { return nil, err @@ -91,15 +80,30 @@ func NewParamsFromPath(path string) (*BIP44Params, error) { if err != nil { return nil, err } - if !(change == 0 || change == 1) { - return nil, fmt.Errorf("change field can only be 0 or 1") - } addressIdx, err := hardenedInt(spl[4]) if err != nil { return nil, err } + // Confirm valid values + if spl[0] != "44'" { + return nil, fmt.Errorf("first field in path must be 44', got %v", spl[0]) + } + + if !isHardened(spl[1]) || !isHardened(spl[2]) { + return nil, + fmt.Errorf("second and third field in path must be hardened (ie. contain the suffix ', got %v and %v", spl[1], spl[2]) + } + if isHardened(spl[3]) || isHardened(spl[4]) { + return nil, + fmt.Errorf("fourth and fifth field in path must not be hardened (ie. not contain the suffix ', got %v and %v", spl[3], spl[4]) + } + + if !(change == 0 || change == 1) { + return nil, fmt.Errorf("change field can only be 0 or 1") + } + return &BIP44Params{ purpose: purpose, coinType: coinType, @@ -132,7 +136,7 @@ func NewFundraiserParams(account uint32, addressIdx uint32) *BIP44Params { return NewParams(44, 118, account, false, addressIdx) } -// Return the BIP44 fields as an array. +// DerivationPath returns the BIP44 fields as an array. func (p BIP44Params) DerivationPath() []uint32 { change := uint32(0) if p.change { @@ -251,8 +255,10 @@ func i64(key []byte, data []byte) (IL [32]byte, IR [32]byte) { mac := hmac.New(sha512.New, key) // sha512 does not err _, _ = mac.Write(data) + I := mac.Sum(nil) copy(IL[:], I[:32]) copy(IR[:], I[32:]) + return } diff --git a/crypto/keys/hd/hdpath_test.go b/crypto/keys/hd/hdpath_test.go index f310fc355..275b714ce 100644 --- a/crypto/keys/hd/hdpath_test.go +++ b/crypto/keys/hd/hdpath_test.go @@ -5,9 +5,9 @@ import ( "fmt" "testing" - "github.com/stretchr/testify/assert" - "github.com/cosmos/go-bip39" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var defaultBIP39Passphrase = "" @@ -21,7 +21,27 @@ func mnemonicToSeed(mnemonic string) []byte { func ExampleStringifyPathParams() { path := NewParams(44, 0, 0, false, 0) fmt.Println(path.String()) - // Output: 44'/0'/0'/0/0 + path = NewParams(44, 33, 7, true, 9) + fmt.Println(path.String()) + // Output: + // 44'/0'/0'/0/0 + // 44'/33'/7'/1/9 +} + +func TestStringifyFundraiserPathParams(t *testing.T) { + path := NewFundraiserParams(4, 22) + require.Equal(t, "44'/118'/4'/0/22", path.String()) + + path = NewFundraiserParams(4, 57) + require.Equal(t, "44'/118'/4'/0/57", path.String()) +} + +func TestPathToArray(t *testing.T) { + path := NewParams(44, 118, 1, false, 4) + require.Equal(t, "[44 118 1 0 4]", fmt.Sprintf("%v", path.DerivationPath())) + + path = NewParams(44, 118, 2, true, 15) + require.Equal(t, "[44 118 2 1 15]", fmt.Sprintf("%v", path.DerivationPath())) } func TestParamsFromPath(t *testing.T) { @@ -60,6 +80,11 @@ func TestParamsFromPath(t *testing.T) { {"44'/0'/0'/0/0'"}, // fifth field must not have ' {"44'/-1'/0'/0/0"}, // no negatives {"44'/0'/0'/-1/0"}, // no negatives + {"a'/0'/0'/-1/0"}, // valid values + {"0/X/0'/-1/0"}, // valid values + {"44'/0'/X/-1/0"}, // valid values + {"44'/0'/0'/%/0"}, // valid values + {"44'/0'/0'/0/%"}, // valid values } for i, c := range badCases { @@ -80,14 +105,39 @@ func ExampleSomeBIP32TestVecs() { fmt.Println("keys from fundraiser test-vector (cosmos, bitcoin, ether)") fmt.Println() // cosmos - priv, _ := DerivePrivateKeyForPath(master, ch, FullFundraiserPath) - fmt.Println(hex.EncodeToString(priv[:])) + priv, err := DerivePrivateKeyForPath(master, ch, FullFundraiserPath) + if err != nil { + fmt.Println("INVALID") + } else { + fmt.Println(hex.EncodeToString(priv[:])) + } // bitcoin - priv, _ = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/0") - fmt.Println(hex.EncodeToString(priv[:])) + priv, err = DerivePrivateKeyForPath(master, ch, "44'/0'/0'/0/0") + if err != nil { + fmt.Println("INVALID") + } else { + fmt.Println(hex.EncodeToString(priv[:])) + } // ether - priv, _ = DerivePrivateKeyForPath(master, ch, "44'/60'/0'/0/0") - fmt.Println(hex.EncodeToString(priv[:])) + priv, err = DerivePrivateKeyForPath(master, ch, "44'/60'/0'/0/0") + if err != nil { + fmt.Println("INVALID") + } else { + fmt.Println(hex.EncodeToString(priv[:])) + } + // INVALID + priv, err = DerivePrivateKeyForPath(master, ch, "X/0'/0'/0/0") + if err != nil { + fmt.Println("INVALID") + } else { + fmt.Println(hex.EncodeToString(priv[:])) + } + priv, err = DerivePrivateKeyForPath(master, ch, "-44/0'/0'/0/0") + if err != nil { + fmt.Println("INVALID") + } else { + fmt.Println(hex.EncodeToString(priv[:])) + } fmt.Println() fmt.Println("keys generated via https://coinomi.com/recovery-phrase-tool.html") @@ -121,6 +171,8 @@ func ExampleSomeBIP32TestVecs() { // bfcb217c058d8bbafd5e186eae936106ca3e943889b0b4a093ae13822fd3170c // e77c3de76965ad89997451de97b95bb65ede23a6bf185a55d80363d92ee37c3d // 7fc4d8a8146dea344ba04c593517d3f377fa6cded36cd55aee0a0bb968e651bc + // INVALID + // INVALID // // keys generated via https://coinomi.com/recovery-phrase-tool.html // diff --git a/crypto/keys/keybase.go b/crypto/keys/keybase.go index e202cd6d8..635a62f77 100644 --- a/crypto/keys/keybase.go +++ b/crypto/keys/keybase.go @@ -8,19 +8,18 @@ import ( "github.com/pkg/errors" - "github.com/cosmos/go-bip39" - "github.com/cosmos/cosmos-sdk/crypto" "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" "github.com/cosmos/cosmos-sdk/crypto/keys/mintkey" "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/go-bip39" + tmcrypto "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/encoding/amino" "github.com/tendermint/tendermint/crypto/secp256k1" dbm "github.com/tendermint/tendermint/libs/db" - - "github.com/cosmos/cosmos-sdk/crypto/keys/keyerror" ) var _ Keybase = dbKeybase{} @@ -30,6 +29,7 @@ var _ Keybase = dbKeybase{} // Find a list of all supported languages in the BIP 39 spec (word lists). type Language int +//noinspection ALL const ( // English is the default language to create a mnemonic. // It is the only supported language by this package. @@ -54,7 +54,7 @@ const ( const ( // used for deriving seed from mnemonic - defaultBIP39Passphrase = "" + DefaultBIP39Passphrase = "" // bits of entropy to draw when creating a mnemonic defaultEntropySize = 256 @@ -109,41 +109,15 @@ func (kb dbKeybase) CreateMnemonic(name string, language Language, passwd string return } - seed := bip39.NewSeed(mnemonic, defaultBIP39Passphrase) + seed := bip39.NewSeed(mnemonic, DefaultBIP39Passphrase) info, err = kb.persistDerivedKey(seed, passwd, name, hd.FullFundraiserPath) return } -// TEMPORARY METHOD UNTIL WE FIGURE OUT USER FACING HD DERIVATION API -func (kb dbKeybase) CreateKey(name, mnemonic, passwd string) (info Info, err error) { - words := strings.Split(mnemonic, " ") - if len(words) != 12 && len(words) != 24 { - err = fmt.Errorf("recovering only works with 12 word (fundraiser) or 24 word mnemonics, got: %v words", len(words)) - return - } - seed, err := bip39.NewSeedWithErrorChecking(mnemonic, defaultBIP39Passphrase) - if err != nil { - return - } - info, err = kb.persistDerivedKey(seed, passwd, name, hd.FullFundraiserPath) - return -} - -// CreateFundraiserKey converts a mnemonic to a private key and persists it, -// encrypted with the given password. -// TODO(ismail) -func (kb dbKeybase) CreateFundraiserKey(name, mnemonic, passwd string) (info Info, err error) { - words := strings.Split(mnemonic, " ") - if len(words) != 12 { - err = fmt.Errorf("recovering only works with 12 word (fundraiser), got: %v words", len(words)) - return - } - seed, err := bip39.NewSeedWithErrorChecking(mnemonic, defaultBIP39Passphrase) - if err != nil { - return - } - info, err = kb.persistDerivedKey(seed, passwd, name, hd.FullFundraiserPath) - return +// CreateAccount converts a mnemonic to a private key and persists it, encrypted with the given password. +func (kb dbKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error) { + hdPath := hd.NewFundraiserParams(account, index) + return kb.Derive(name, mnemonic, bip39Passwd, encryptPasswd, *hdPath) } func (kb dbKeybase) Derive(name, mnemonic, bip39Passphrase, encryptPasswd string, params hd.BIP44Params) (info Info, err error) { @@ -151,23 +125,26 @@ func (kb dbKeybase) Derive(name, mnemonic, bip39Passphrase, encryptPasswd string if err != nil { return } - info, err = kb.persistDerivedKey(seed, encryptPasswd, name, params.String()) + info, err = kb.persistDerivedKey(seed, encryptPasswd, name, params.String()) return } // CreateLedger creates a new locally-stored reference to a Ledger keypair // It returns the created key info and an error if the Ledger could not be queried -func (kb dbKeybase) CreateLedger(name string, path crypto.DerivationPath, algo SigningAlgo) (Info, error) { +func (kb dbKeybase) CreateLedger(name string, algo SigningAlgo, account uint32, index uint32) (Info, error) { if algo != Secp256k1 { return nil, ErrUnsupportedSigningAlgo } - priv, err := crypto.NewPrivKeyLedgerSecp256k1(path) + + hdPath := hd.NewFundraiserParams(account, index) + priv, err := crypto.NewPrivKeyLedgerSecp256k1(*hdPath) if err != nil { return nil, err } pub := priv.PubKey() - return kb.writeLedgerKey(pub, path, name), nil + + return kb.writeLedgerKey(pub, *hdPath, name), nil } // CreateOffline creates a new reference to an offline keypair @@ -432,7 +409,7 @@ func (kb dbKeybase) writeLocalKey(priv tmcrypto.PrivKey, name, passphrase string return info } -func (kb dbKeybase) writeLedgerKey(pub tmcrypto.PubKey, path crypto.DerivationPath, name string) Info { +func (kb dbKeybase) writeLedgerKey(pub tmcrypto.PubKey, path hd.BIP44Params, name string) Info { info := newLedgerInfo(name, pub, path) kb.writeInfo(info, name) return info diff --git a/crypto/keys/keybase_test.go b/crypto/keys/keybase_test.go index bc7783b67..36a05d374 100644 --- a/crypto/keys/keybase_test.go +++ b/crypto/keys/keybase_test.go @@ -344,7 +344,7 @@ func TestSeedPhrase(t *testing.T) { // let us re-create it from the mnemonic-phrase params := *hd.NewFundraiserParams(0, 0) - newInfo, err := cstore.Derive(n2, mnemonic, defaultBIP39Passphrase, p2, params) + newInfo, err := cstore.Derive(n2, mnemonic, DefaultBIP39Passphrase, p2, params) require.NoError(t, err) require.Equal(t, n2, newInfo.GetName()) require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address()) diff --git a/crypto/keys/types.go b/crypto/keys/types.go index 14d050961..52ef88b4b 100644 --- a/crypto/keys/types.go +++ b/crypto/keys/types.go @@ -3,8 +3,6 @@ package keys import ( "github.com/tendermint/tendermint/crypto" - ccrypto "github.com/cosmos/cosmos-sdk/crypto" - "github.com/cosmos/cosmos-sdk/crypto/keys/hd" "github.com/cosmos/cosmos-sdk/types" ) @@ -23,20 +21,20 @@ type Keybase interface { // CreateMnemonic creates a new mnemonic, and derives a hierarchical deterministic // key from that. CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error) - // CreateKey takes a mnemonic and derives, a password. This method is temporary - CreateKey(name, mnemonic, passwd string) (info Info, err error) - // CreateFundraiserKey takes a mnemonic and derives, a password - CreateFundraiserKey(name, mnemonic, passwd string) (info Info, err error) - // Compute a BIP39 seed from th mnemonic and bip39Passwd. + + // CreateAccount creates an account based using the BIP44 path (44'/118'/{account}'/0/{index} + CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error) + + // Derive computes a BIP39 seed from th mnemonic and bip39Passwd. // Derive private key from the seed using the BIP44 params. // Encrypt the key to disk using encryptPasswd. // See https://github.com/cosmos/cosmos-sdk/issues/2095 - Derive(name, mnemonic, bip39Passwd, - encryptPasswd string, params hd.BIP44Params) (Info, error) - // Create, store, and return a new Ledger key reference - CreateLedger(name string, path ccrypto.DerivationPath, algo SigningAlgo) (info Info, err error) + Derive(name, mnemonic, bip39Passwd, encryptPasswd string, params hd.BIP44Params) (Info, error) - // Create, store, and return a new offline key reference + // CreateLedger creates, stores, and returns a new Ledger key reference + CreateLedger(name string, algo SigningAlgo, account uint32, index uint32) (info Info, err error) + + // CreateOffline creates, stores, and returns a new offline key reference CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error) // The following operations will *only* work on locally-stored keys @@ -46,10 +44,10 @@ type Keybase interface { Export(name string) (armor string, err error) ExportPubKey(name string) (armor string, err error) - // *only* works on locally-stored keys. Temporary method until we redo the exporting API + // ExportPrivateKeyObject *only* works on locally-stored keys. Temporary method until we redo the exporting API ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error) - // Close closes the database. + // CloseDB closes the database. CloseDB() } @@ -123,12 +121,12 @@ func (i localInfo) GetAddress() types.AccAddress { // ledgerInfo is the public information about a Ledger key type ledgerInfo struct { - Name string `json:"name"` - PubKey crypto.PubKey `json:"pubkey"` - Path ccrypto.DerivationPath `json:"path"` + Name string `json:"name"` + PubKey crypto.PubKey `json:"pubkey"` + Path hd.BIP44Params `json:"path"` } -func newLedgerInfo(name string, pub crypto.PubKey, path ccrypto.DerivationPath) Info { +func newLedgerInfo(name string, pub crypto.PubKey, path hd.BIP44Params) Info { return &ledgerInfo{ Name: name, PubKey: pub, diff --git a/crypto/ledger_secp256k1.go b/crypto/ledger_secp256k1.go index ff05d31ba..8f5c9ab81 100644 --- a/crypto/ledger_secp256k1.go +++ b/crypto/ledger_secp256k1.go @@ -6,6 +6,7 @@ import ( "github.com/pkg/errors" secp256k1 "github.com/btcsuite/btcd/btcec" + "github.com/cosmos/cosmos-sdk/crypto/keys/hd" tmcrypto "github.com/tendermint/tendermint/crypto" tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1" ) @@ -22,9 +23,6 @@ type ( // dependencies when Ledger support is potentially not enabled. discoverLedgerFn func() (LedgerSECP256K1, error) - // DerivationPath represents a Ledger derivation path. - DerivationPath []uint32 - // LedgerSECP256K1 reflects an interface a Ledger API must implement for // the SECP256K1 scheme. LedgerSECP256K1 interface { @@ -39,7 +37,7 @@ type ( // go-amino so we can view the address later, even without having the // ledger attached. CachedPubKey tmcrypto.PubKey - Path DerivationPath + Path hd.BIP44Params ledger LedgerSECP256K1 } ) @@ -49,7 +47,7 @@ type ( // // CONTRACT: The ledger device, ledgerDevice, must be loaded and set prior to // any creation of a PrivKeyLedgerSecp256k1. -func NewPrivKeyLedgerSecp256k1(path DerivationPath) (tmcrypto.PrivKey, error) { +func NewPrivKeyLedgerSecp256k1(path hd.BIP44Params) (tmcrypto.PrivKey, error) { if discoverLedger == nil { return nil, errors.New("no Ledger discovery function defined") } @@ -138,11 +136,11 @@ func (pkl PrivKeyLedgerSecp256k1) getPubKey() (key tmcrypto.PubKey, err error) { } func (pkl PrivKeyLedgerSecp256k1) signLedgerSecp256k1(msg []byte) ([]byte, error) { - return pkl.ledger.SignSECP256K1(pkl.Path, msg) + return pkl.ledger.SignSECP256K1(pkl.Path.DerivationPath(), msg) } func (pkl PrivKeyLedgerSecp256k1) pubkeyLedgerSecp256k1() (pub tmcrypto.PubKey, err error) { - key, err := pkl.ledger.GetPublicKeySECP256K1(pkl.Path) + key, err := pkl.ledger.GetPublicKeySECP256K1(pkl.Path.DerivationPath()) if err != nil { return nil, fmt.Errorf("error fetching public key: %v", err) } diff --git a/crypto/ledger_test.go b/crypto/ledger_test.go index 1aae158ef..cf14db74b 100644 --- a/crypto/ledger_test.go +++ b/crypto/ledger_test.go @@ -5,6 +5,8 @@ import ( "os" "testing" + "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/encoding/amino" ) @@ -16,9 +18,9 @@ func TestRealLedgerSecp256k1(t *testing.T) { t.Skip(fmt.Sprintf("Set '%s' to run code on a real ledger", ledgerEnabledEnv)) } msg := []byte("{\"account_number\":\"3\",\"chain_id\":\"1234\",\"fee\":{\"amount\":[{\"amount\":\"150\",\"denom\":\"atom\"}],\"gas\":\"5000\"},\"memo\":\"memo\",\"msgs\":[[\"%s\"]],\"sequence\":\"6\"}") - path := DerivationPath{44, 60, 0, 0, 0} + path := hd.NewParams(44, 60, 0, false, 0) - priv, err := NewPrivKeyLedgerSecp256k1(path) + priv, err := NewPrivKeyLedgerSecp256k1(*path) require.Nil(t, err, "%s", err) pub := priv.PubKey() @@ -58,7 +60,7 @@ func TestRealLedgerErrorHandling(t *testing.T) { // first, try to generate a key, must return an error // (no panic) - path := DerivationPath{44, 60, 0, 0, 0} - _, err := NewPrivKeyLedgerSecp256k1(path) + path := hd.NewParams(44, 60, 0, false, 0) + _, err := NewPrivKeyLedgerSecp256k1(*path) require.Error(t, err) } From ac1d2c73b2b2518ace01ce652b60ca4b25fd8c1f Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Tue, 5 Feb 2019 10:09:25 -0800 Subject: [PATCH 54/99] Merge PR #3500: Update RELEASE_PROCESS.md --- docs/RELEASE_PROCESS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/RELEASE_PROCESS.md b/docs/RELEASE_PROCESS.md index c8968486a..8ae483531 100644 --- a/docs/RELEASE_PROCESS.md +++ b/docs/RELEASE_PROCESS.md @@ -4,9 +4,9 @@ - [ ] 2. Add commits/PRs that are desired for this release **that haven’t already been added to develop** - [ ] 3. Merge items in `PENDING.md` into the `CHANGELOG.md`. While doing this make sure that each entry contains links to issues/PRs for each item - [ ] 4. Summarize breaking API changes section under “Breaking Changes” section to the `CHANGELOG.md` to bring attention to any breaking API changes that affect RPC consumers. -- [ ] 5. Tag the commit `{ .Release.Name }-rcN` +- [ ] 5. Tag the commit `git tag -a { .Release.Name }-rcN -m 'Release { .Release.Name }'` - [ ] 6. Open a branch & PR to merge the pending release back into `develop`. If any changes are made to the release branch, they must also be made to the pending develop merge, and both must pass tests. - [ ] 7. Kick off 1 day of automated fuzz testing - [ ] 8. Release Lead assigns 2 people to perform [buddy testing script](/docs/RELEASE_TEST_SCRIPT.md) and update the relevant documentation - [ ] 9. If errors are found in either #6 or #7 go back to #2 (*NOTE*: be sure to increment the `rcN`) -- [ ] 10. After #6 and #7 have successfully completed then merge the release PR and push the final release tag +- [ ] 10. After #6 and #7 have successfully completed then merge the release PR and push the final annotated release tag (created with `git tag -a`) From 174ec0c809e19c16e053273dab377b1d717c1a9f Mon Sep 17 00:00:00 2001 From: Juan Leni Date: Tue, 5 Feb 2019 19:21:04 +0100 Subject: [PATCH 55/99] Merge PR #3431: PrivKeyLedgerSecp256k1: lost connection --- Gopkg.lock | 39 ++++---- Gopkg.toml | 5 +- Makefile | 3 + PENDING.md | 1 + crypto/ledger_integration_test.go | 157 ++++++++++++++++++++++++++++++ crypto/ledger_secp256k1.go | 132 ++++++++++++++++--------- crypto/ledger_test.go | 57 +---------- 7 files changed, 273 insertions(+), 121 deletions(-) create mode 100644 crypto/ledger_integration_test.go diff --git a/Gopkg.lock b/Gopkg.lock index 97fcf6734..9711a8447 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1,22 +1,6 @@ # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. -[[projects]] - digest = "1:7736fc6da04620727f8f3aa2ced8d77be8e074a302820937aa5993848c769b27" - name = "github.com/ZondaX/hid-go" - packages = ["."] - pruneopts = "UT" - revision = "48b08affede2cea076a3cf13b2e3f72ed262b743" - version = "v0.4.0" - -[[projects]] - digest = "1:1ba351898f7efc68c7c9ff3145b920e478f716b077fdaaf06b967c5d883fa988" - name = "github.com/ZondaX/ledger-go" - packages = ["."] - pruneopts = "UT" - revision = "c3225ab10c2f53397d4aa419a588466493572b22" - version = "v0.4.0" - [[projects]] branch = "master" digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0" @@ -522,12 +506,28 @@ revision = "v0.29.0" [[projects]] - digest = "1:29b886f00694ae7c18c4559a2901f2a057d5a62308ed5eb6cd52b9a31016fb14" + digest = "1:b73f5e117bc7c6e8fc47128f20db48a873324ad5cfeeebfc505e85c58682b5e4" + name = "github.com/zondax/hid" + packages = ["."] + pruneopts = "T" + revision = "302fd402163c34626286195dfa9adac758334acc" + version = "v0.9.0" + +[[projects]] + digest = "1:fca24169988a61ea725d1326de30910d8049fe68bcbc194d28803f9a76dda380" name = "github.com/zondax/ledger-cosmos-go" packages = ["."] pruneopts = "UT" - revision = "71aa0ab6e03d2d320c82bbe13678a36584a5b813" - version = "v0.9.3" + revision = "69fdb8ce5e5b9d9c3b22b9248e117b231d4f06dd" + version = "v0.9.7" + +[[projects]] + digest = "1:f8e4c0b959174a1fa5946b12f1f2ac7ea5651bef20a9e4a8dac55dbffcaa6cd6" + name = "github.com/zondax/ledger-go" + packages = ["."] + pruneopts = "UT" + revision = "69c15f1333a9b6866e5f66096561c7d138894bc5" + version = "v0.8.0" [[projects]] digest = "1:6f6dc6060c4e9ba73cf28aa88f12a69a030d3d19d518ef8e931879eaa099628d" @@ -681,6 +681,7 @@ "github.com/stretchr/testify/assert", "github.com/stretchr/testify/require", "github.com/syndtr/goleveldb/leveldb/opt", + "github.com/tendermint/btcd/btcec", "github.com/tendermint/go-amino", "github.com/tendermint/iavl", "github.com/tendermint/tendermint/abci/server", diff --git a/Gopkg.toml b/Gopkg.toml index 1bf115d61..bb3b016c0 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -44,7 +44,7 @@ [[constraint]] name = "github.com/zondax/ledger-cosmos-go" - version = "=v0.9.3" + version = "=v0.9.7" ## deps without releases: @@ -84,3 +84,6 @@ [prune] go-tests = true unused-packages = true + [[prune.project]] + name = "github.com/zondax/hid" + unused-packages = false diff --git a/Makefile b/Makefile index d583e0c76..0b2c2c76b 100644 --- a/Makefile +++ b/Makefile @@ -144,6 +144,9 @@ test: test_unit test_cli: @go test -p 4 `go list github.com/cosmos/cosmos-sdk/cmd/gaia/cli_test` -tags=cli_test +test_ledger: + @go test `go list github.com/cosmos/cosmos-sdk/crypto` -tags='cgo ledger test_real_ledger' + test_unit: @VERSION=$(VERSION) go test $(PACKAGES_NOSIMULATION) diff --git a/PENDING.md b/PENDING.md index 109e18ab5..296d30d1c 100644 --- a/PENDING.md +++ b/PENDING.md @@ -90,6 +90,7 @@ BUG FIXES - [\#3419](https://github.com/cosmos/cosmos-sdk/pull/3419) Fix `q distr slashes` panic - [\#3453](https://github.com/cosmos/cosmos-sdk/pull/3453) The `rest-server` command didn't respect persistent flags such as `--chain-id` and `--trust-node` if they were passed on the command line. + - [\#3441](https://github.com/cosmos/cosmos-sdk/pull/3431) Improved resource management and connection handling (ledger devices). Fixes issue with DER vs BER signatures. * Gaia * [\#3486](https://github.com/cosmos/cosmos-sdk/pull/3486) Use AmountOf in diff --git a/crypto/ledger_integration_test.go b/crypto/ledger_integration_test.go new file mode 100644 index 000000000..30c66c765 --- /dev/null +++ b/crypto/ledger_integration_test.go @@ -0,0 +1,157 @@ +// +build cgo,ledger,test_real_ledger + +package crypto + +import ( + "fmt" + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/encoding/amino" + ledger "github.com/zondax/ledger-cosmos-go" +) + +const ( + // These tests expect a ledger device initialized to the following mnemonic + testMnemonic = "equip will roof matter pink blind book anxiety banner elbow sun young" +) + +func TestDiscoverDevice(t *testing.T) { + device, err := discoverLedger() + require.NoError(t, err) + require.NotNil(t, device) + defer device.Close() +} + +func TestDiscoverDeviceTwice(t *testing.T) { + // We expect the second call not to find a device + device, err := discoverLedger() + require.NoError(t, err) + require.NotNil(t, device) + defer device.Close() + + device2, err := discoverLedger() + require.Error(t, err) + require.Equal(t, "no ledger connected", err.Error()) + require.Nil(t, device2) +} + +func TestDiscoverDeviceTwiceClosing(t *testing.T) { + { + device, err := ledger.FindLedgerCosmosUserApp() + require.NoError(t, err) + require.NotNil(t, device) + require.NoError(t, device.Close()) + } + + device2, err := discoverLedger() + require.NoError(t, err) + require.NotNil(t, device2) + require.NoError(t, device2.Close()) +} + +func TestPublicKey(t *testing.T) { + path := *hd.NewFundraiserParams(0, 0) + priv, err := NewPrivKeyLedgerSecp256k1(path) + require.Nil(t, err, "%s", err) + require.NotNil(t, priv) + + pubKeyAddr, err := sdk.Bech32ifyAccPub(priv.PubKey()) + require.NoError(t, err) + require.Equal(t, "cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0", pubKeyAddr, "Is your device using test mnemonic: %s ?", testMnemonic) + require.Equal(t, "5075624b6579536563703235366b317b303334464546394344374334433633353838443342303"+ + "3464542353238314239443233324342413334443646334437314145453539323131464642464531464538377d", + fmt.Sprintf("%x", priv.PubKey())) +} + +func TestPublicKeyHDPath(t *testing.T) { + expectedAnswers := []string{ + "cosmospub1addwnpepqd87l8xhcnrrtzxnkql7k55ph8fr9jarf4hn6udwukfprlalu8lgw0urza0", + "cosmospub1addwnpepqfsdqjr68h7wjg5wacksmqaypasnra232fkgu5sxdlnlu8j22ztxvlqvd65", + "cosmospub1addwnpepqw3xwqun6q43vtgw6p4qspq7srvxhcmvq4jrx5j5ma6xy3r7k6dtxmrkh3d", + "cosmospub1addwnpepqvez9lrp09g8w7gkv42y4yr5p6826cu28ydrhrujv862yf4njmqyyjr4pjs", + "cosmospub1addwnpepq06hw3enfrtmq8n67teytcmtnrgcr0yntmyt25kdukfjkerdc7lqg32rcz7", + "cosmospub1addwnpepqg3trf2gd0s2940nckrxherwqhgmm6xd5h4pcnrh4x7y35h6yafmcpk5qns", + "cosmospub1addwnpepqdm6rjpx6wsref8wjn7ym6ntejet430j4szpngfgc20caz83lu545vuv8hp", + "cosmospub1addwnpepqvdhtjzy2wf44dm03jxsketxc07vzqwvt3vawqqtljgsr9s7jvydjmt66ew", + "cosmospub1addwnpepqwystfpyxwcava7v3t7ndps5xzu6s553wxcxzmmnxevlzvwrlqpzz695nw9", + "cosmospub1addwnpepqw970u6gjqkccg9u3rfj99857wupj2z9fqfzy2w7e5dd7xn7kzzgkgqch0r", + } + + // Check with device + for i := uint32(0); i < 10; i++ { + path := *hd.NewFundraiserParams(0, i) + fmt.Printf("Checking keys at %v\n", path) + + priv, err := NewPrivKeyLedgerSecp256k1(path) + require.Nil(t, err, "%s", err) + require.NotNil(t, priv) + + pubKeyAddr, err := sdk.Bech32ifyAccPub(priv.PubKey()) + require.NoError(t, err) + require.Equal(t, expectedAnswers[i], pubKeyAddr, "Is your device using test mnemonic: %s ?", testMnemonic) + } +} + +func getFakeTx(accountNumber uint32) []byte { + tmp := fmt.Sprintf( + `{"account_number":"%d","chain_id":"1234","fee":{"amount":[{"amount":"150","denom":"atom"}],"gas":"5000"},"memo":"memo","msgs":[[""]],"sequence":"6"}`, + accountNumber) + + return []byte(tmp) +} + +func TestSignaturesHD(t *testing.T) { + for account := uint32(0); account < 100; account += 30 { + msg := getFakeTx(account) + + path := *hd.NewFundraiserParams(account, account/5) + fmt.Printf("Checking signature at %v\n", path) + + priv, err := NewPrivKeyLedgerSecp256k1(path) + require.Nil(t, err, "%s", err) + + pub := priv.PubKey() + sig, err := priv.Sign(msg) + require.Nil(t, err) + + valid := pub.VerifyBytes(msg, sig) + require.True(t, valid, "Is your device using test mnemonic: %s ?", testMnemonic) + } +} + +func TestRealLedgerSecp256k1(t *testing.T) { + msg := getFakeTx(50) + path := *hd.NewFundraiserParams(0, 0) + priv, err := NewPrivKeyLedgerSecp256k1(path) + require.Nil(t, err, "%s", err) + + pub := priv.PubKey() + sig, err := priv.Sign(msg) + require.Nil(t, err) + + valid := pub.VerifyBytes(msg, sig) + require.True(t, valid) + + // now, let's serialize the public key and make sure it still works + bs := priv.PubKey().Bytes() + pub2, err := cryptoAmino.PubKeyFromBytes(bs) + require.Nil(t, err, "%+v", err) + + // make sure we get the same pubkey when we load from disk + require.Equal(t, pub, pub2) + + // signing with the loaded key should match the original pubkey + sig, err = priv.Sign(msg) + require.Nil(t, err) + valid = pub.VerifyBytes(msg, sig) + require.True(t, valid) + + // make sure pubkeys serialize properly as well + bs = pub.Bytes() + bpub, err := cryptoAmino.PubKeyFromBytes(bs) + require.NoError(t, err) + require.Equal(t, pub, bpub) +} diff --git a/crypto/ledger_secp256k1.go b/crypto/ledger_secp256k1.go index 8f5c9ab81..5be8405f5 100644 --- a/crypto/ledger_secp256k1.go +++ b/crypto/ledger_secp256k1.go @@ -2,11 +2,12 @@ package crypto import ( "fmt" + "os" - "github.com/pkg/errors" - - secp256k1 "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/btcec" "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + "github.com/pkg/errors" + tmbtcec "github.com/tendermint/btcd/btcec" tmcrypto "github.com/tendermint/tendermint/crypto" tmsecp256k1 "github.com/tendermint/tendermint/crypto/secp256k1" ) @@ -26,6 +27,7 @@ type ( // LedgerSECP256K1 reflects an interface a Ledger API must implement for // the SECP256K1 scheme. LedgerSECP256K1 interface { + Close() error GetPublicKeySECP256K1([]uint32) ([]byte, error) SignSECP256K1([]uint32, []byte) ([]byte, error) } @@ -38,7 +40,6 @@ type ( // ledger attached. CachedPubKey tmcrypto.PubKey Path hd.BIP44Params - ledger LedgerSECP256K1 } ) @@ -48,24 +49,18 @@ type ( // CONTRACT: The ledger device, ledgerDevice, must be loaded and set prior to // any creation of a PrivKeyLedgerSecp256k1. func NewPrivKeyLedgerSecp256k1(path hd.BIP44Params) (tmcrypto.PrivKey, error) { - if discoverLedger == nil { - return nil, errors.New("no Ledger discovery function defined") - } - - device, err := discoverLedger() + device, err := getLedgerDevice() if err != nil { - return nil, errors.Wrap(err, "failed to create PrivKeyLedgerSecp256k1") + return nil, err } + defer warnIfErrors(device.Close) - pkl := &PrivKeyLedgerSecp256k1{Path: path, ledger: device} - - pubKey, err := pkl.getPubKey() + pubKey, err := getPubKey(device, path) if err != nil { return nil, err } - pkl.CachedPubKey = pubKey - return pkl, err + return PrivKeyLedgerSecp256k1{pubKey, path}, nil } // PubKey returns the cached public key. @@ -73,21 +68,27 @@ func (pkl PrivKeyLedgerSecp256k1) PubKey() tmcrypto.PubKey { return pkl.CachedPubKey } +// Sign returns a secp256k1 signature for the corresponding message +func (pkl PrivKeyLedgerSecp256k1) Sign(message []byte) ([]byte, error) { + device, err := getLedgerDevice() + if err != nil { + return nil, err + } + defer warnIfErrors(device.Close) + + return sign(device, pkl, message) +} + // ValidateKey allows us to verify the sanity of a public key after loading it // from disk. func (pkl PrivKeyLedgerSecp256k1) ValidateKey() error { - // getPubKey will return an error if the ledger is not - pub, err := pkl.getPubKey() + device, err := getLedgerDevice() if err != nil { return err } + defer warnIfErrors(device.Close) - // verify this matches cached address - if !pub.Equals(pkl.CachedPubKey) { - return fmt.Errorf("cached key does not match retrieved key") - } - - return nil + return validateKey(device, pkl) } // AssertIsPrivKeyInner implements the PrivKey interface. It performs a no-op. @@ -105,54 +106,89 @@ func (pkl PrivKeyLedgerSecp256k1) Equals(other tmcrypto.PrivKey) bool { if ledger, ok := other.(*PrivKeyLedgerSecp256k1); ok { return pkl.CachedPubKey.Equals(ledger.CachedPubKey) } - return false } +// warnIfErrors wraps a function and writes a warning to stderr. This is required +// to avoid ignoring errors when defer is used. Using defer may result in linter warnings. +func warnIfErrors(f func() error) { + if err := f(); err != nil { + _, _ = fmt.Fprint(os.Stderr, "received error when closing ledger connection", err) + } +} + +func convertDERtoBER(signatureDER []byte) ([]byte, error) { + sigDER, err := btcec.ParseDERSignature(signatureDER[:], btcec.S256()) + if err != nil { + return nil, err + } + sigBER := tmbtcec.Signature{R: sigDER.R, S: sigDER.S} + return sigBER.Serialize(), nil +} + +func getLedgerDevice() (LedgerSECP256K1, error) { + if discoverLedger == nil { + return nil, errors.New("no Ledger discovery function defined") + } + + device, err := discoverLedger() + if err != nil { + return nil, errors.Wrap(err, "ledger nano S") + } + + return device, nil +} + +func validateKey(device LedgerSECP256K1, pkl PrivKeyLedgerSecp256k1) error { + pub, err := getPubKey(device, pkl.Path) + if err != nil { + return err + } + + // verify this matches cached address + if !pub.Equals(pkl.CachedPubKey) { + return fmt.Errorf("cached key does not match retrieved key") + } + + return nil +} + // Sign calls the ledger and stores the PubKey for future use. // // Communication is checked on NewPrivKeyLedger and PrivKeyFromBytes, returning // an error, so this should only trigger if the private key is held in memory // for a while before use. -func (pkl PrivKeyLedgerSecp256k1) Sign(msg []byte) ([]byte, error) { - sig, err := pkl.signLedgerSecp256k1(msg) +func sign(device LedgerSECP256K1, pkl PrivKeyLedgerSecp256k1, msg []byte) ([]byte, error) { + err := validateKey(device, pkl) if err != nil { return nil, err } - return sig, nil + sig, err := device.SignSECP256K1(pkl.Path.DerivationPath(), msg) + if err != nil { + return nil, err + } + + return convertDERtoBER(sig) } // getPubKey reads the pubkey the ledger itself // since this involves IO, it may return an error, which is not exposed // in the PubKey interface, so this function allows better error handling -func (pkl PrivKeyLedgerSecp256k1) getPubKey() (key tmcrypto.PubKey, err error) { - key, err = pkl.pubkeyLedgerSecp256k1() +func getPubKey(device LedgerSECP256K1, path hd.BIP44Params) (tmcrypto.PubKey, error) { + publicKey, err := device.GetPublicKeySECP256K1(path.DerivationPath()) if err != nil { - return key, fmt.Errorf("please open Cosmos app on the Ledger device - error: %v", err) + return nil, fmt.Errorf("please open Cosmos app on the Ledger device - error: %v", err) } - return key, err -} - -func (pkl PrivKeyLedgerSecp256k1) signLedgerSecp256k1(msg []byte) ([]byte, error) { - return pkl.ledger.SignSECP256K1(pkl.Path.DerivationPath(), msg) -} - -func (pkl PrivKeyLedgerSecp256k1) pubkeyLedgerSecp256k1() (pub tmcrypto.PubKey, err error) { - key, err := pkl.ledger.GetPublicKeySECP256K1(pkl.Path.DerivationPath()) - if err != nil { - return nil, fmt.Errorf("error fetching public key: %v", err) - } - - var pk tmsecp256k1.PubKeySecp256k1 - // re-serialize in the 33-byte compressed format - cmp, err := secp256k1.ParsePubKey(key[:], secp256k1.S256()) + cmp, err := btcec.ParsePubKey(publicKey[:], btcec.S256()) if err != nil { return nil, fmt.Errorf("error parsing public key: %v", err) } - copy(pk[:], cmp.SerializeCompressed()) - return pk, nil + var compressedPublicKey tmsecp256k1.PubKeySecp256k1 + copy(compressedPublicKey[:], cmp.SerializeCompressed()) + + return compressedPublicKey, nil } diff --git a/crypto/ledger_test.go b/crypto/ledger_test.go index cf14db74b..cb2c7b325 100644 --- a/crypto/ledger_test.go +++ b/crypto/ledger_test.go @@ -1,66 +1,17 @@ package crypto import ( - "fmt" - "os" "testing" "github.com/cosmos/cosmos-sdk/crypto/keys/hd" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/encoding/amino" ) -var ledgerEnabledEnv = "TEST_WITH_LEDGER" - -func TestRealLedgerSecp256k1(t *testing.T) { - if os.Getenv(ledgerEnabledEnv) == "" { - t.Skip(fmt.Sprintf("Set '%s' to run code on a real ledger", ledgerEnabledEnv)) - } - msg := []byte("{\"account_number\":\"3\",\"chain_id\":\"1234\",\"fee\":{\"amount\":[{\"amount\":\"150\",\"denom\":\"atom\"}],\"gas\":\"5000\"},\"memo\":\"memo\",\"msgs\":[[\"%s\"]],\"sequence\":\"6\"}") - path := hd.NewParams(44, 60, 0, false, 0) - - priv, err := NewPrivKeyLedgerSecp256k1(*path) - require.Nil(t, err, "%s", err) - - pub := priv.PubKey() - sig, err := priv.Sign(msg) - require.Nil(t, err) - - valid := pub.VerifyBytes(msg, sig) - require.True(t, valid) - - // now, let's serialize the public key and make sure it still works - bs := priv.PubKey().Bytes() - pub2, err := cryptoAmino.PubKeyFromBytes(bs) - require.Nil(t, err, "%+v", err) - - // make sure we get the same pubkey when we load from disk - require.Equal(t, pub, pub2) - - // signing with the loaded key should match the original pubkey - sig, err = priv.Sign(msg) - require.Nil(t, err) - valid = pub.VerifyBytes(msg, sig) - require.True(t, valid) - - // make sure pubkeys serialize properly as well - bs = pub.Bytes() - bpub, err := cryptoAmino.PubKeyFromBytes(bs) - require.NoError(t, err) - require.Equal(t, pub, bpub) -} - -// TestRealLedgerErrorHandling calls. These tests assume -// the ledger is not plugged in.... -func TestRealLedgerErrorHandling(t *testing.T) { - if os.Getenv(ledgerEnabledEnv) != "" { - t.Skip(fmt.Sprintf("Unset '%s' to run code as if without a real Ledger", ledgerEnabledEnv)) - } - +// This tests assume a ledger is not plugged in +func TestLedgerErrorHandling(t *testing.T) { // first, try to generate a key, must return an error // (no panic) - path := hd.NewParams(44, 60, 0, false, 0) - _, err := NewPrivKeyLedgerSecp256k1(*path) + path := *hd.NewParams(44, 555, 0, false, 0) + _, err := NewPrivKeyLedgerSecp256k1(path) require.Error(t, err) } From d50f3ee4fba80114b49bef2442d6ed8d62094ec1 Mon Sep 17 00:00:00 2001 From: Joon Date: Tue, 5 Feb 2019 10:39:22 -0800 Subject: [PATCH 56/99] Merge PR #3395: Store Refactor 2 --- store/errors/errors.go | 29 +++++++++++++++++++++++++++++ store/gaskv/store_test.go | 28 ++++++++++++++-------------- store/iavl/store.go | 17 ++++++++--------- store/iavl/store_test.go | 23 ++++++++++++----------- store/rootmulti/proof_test.go | 5 ++--- store/rootmulti/store.go | 15 ++++++++------- store/rootmulti/store_test.go | 24 ++++++++++++------------ 7 files changed, 85 insertions(+), 56 deletions(-) create mode 100644 store/errors/errors.go diff --git a/store/errors/errors.go b/store/errors/errors.go new file mode 100644 index 000000000..361a10fdb --- /dev/null +++ b/store/errors/errors.go @@ -0,0 +1,29 @@ +package errors + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// nolint - reexport +const ( + CodeOK = sdk.CodeOK + CodeInternal = sdk.CodeInternal + CodeTxDecode = sdk.CodeTxDecode + CodeUnknownRequest = sdk.CodeUnknownRequest + + CodespaceRoot = sdk.CodespaceRoot +) + +// nolint - reexport +type Error = sdk.Error + +// nolint - reexport +func ErrInternal(msg string) Error { + return sdk.ErrInternal(msg) +} +func ErrTxDecode(msg string) Error { + return sdk.ErrTxDecode(msg) +} +func ErrUnknownRequest(msg string) Error { + return sdk.ErrUnknownRequest(msg) +} diff --git a/store/gaskv/store_test.go b/store/gaskv/store_test.go index 8d6acd86e..a5b8f8907 100644 --- a/store/gaskv/store_test.go +++ b/store/gaskv/store_test.go @@ -8,15 +8,15 @@ import ( "github.com/cosmos/cosmos-sdk/store/dbadapter" "github.com/cosmos/cosmos-sdk/store/gaskv" - stypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/store/types" "github.com/stretchr/testify/require" ) -func newGasKVStore() stypes.KVStore { - meter := stypes.NewGasMeter(10000) +func newGasKVStore() types.KVStore { + meter := types.NewGasMeter(10000) mem := dbadapter.Store{dbm.NewMemDB()} - return gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + return gaskv.NewStore(mem, meter, types.KVGasConfig()) } func bz(s string) []byte { return []byte(s) } @@ -26,20 +26,20 @@ func valFmt(i int) []byte { return bz(fmt.Sprintf("value%0.8d", i)) } func TestGasKVStoreBasic(t *testing.T) { mem := dbadapter.Store{dbm.NewMemDB()} - meter := stypes.NewGasMeter(10000) - st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + meter := types.NewGasMeter(10000) + st := gaskv.NewStore(mem, meter, types.KVGasConfig()) require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") st.Set(keyFmt(1), valFmt(1)) require.Equal(t, valFmt(1), st.Get(keyFmt(1))) st.Delete(keyFmt(1)) require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") - require.Equal(t, meter.GasConsumed(), stypes.Gas(6429)) + require.Equal(t, meter.GasConsumed(), types.Gas(6429)) } func TestGasKVStoreIterator(t *testing.T) { mem := dbadapter.Store{dbm.NewMemDB()} - meter := stypes.NewGasMeter(10000) - st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + meter := types.NewGasMeter(10000) + st := gaskv.NewStore(mem, meter, types.KVGasConfig()) require.Empty(t, st.Get(keyFmt(1)), "Expected `key1` to be empty") require.Empty(t, st.Get(keyFmt(2)), "Expected `key2` to be empty") st.Set(keyFmt(1), valFmt(1)) @@ -57,20 +57,20 @@ func TestGasKVStoreIterator(t *testing.T) { iterator.Next() require.False(t, iterator.Valid()) require.Panics(t, iterator.Next) - require.Equal(t, meter.GasConsumed(), stypes.Gas(6987)) + require.Equal(t, meter.GasConsumed(), types.Gas(6987)) } func TestGasKVStoreOutOfGasSet(t *testing.T) { mem := dbadapter.Store{dbm.NewMemDB()} - meter := stypes.NewGasMeter(0) - st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + meter := types.NewGasMeter(0) + st := gaskv.NewStore(mem, meter, types.KVGasConfig()) require.Panics(t, func() { st.Set(keyFmt(1), valFmt(1)) }, "Expected out-of-gas") } func TestGasKVStoreOutOfGasIterator(t *testing.T) { mem := dbadapter.Store{dbm.NewMemDB()} - meter := stypes.NewGasMeter(20000) - st := gaskv.NewStore(mem, meter, stypes.KVGasConfig()) + meter := types.NewGasMeter(20000) + st := gaskv.NewStore(mem, meter, types.KVGasConfig()) st.Set(keyFmt(1), valFmt(1)) iterator := st.Iterator(nil, nil) iterator.Next() diff --git a/store/iavl/store.go b/store/iavl/store.go index 29e375b55..49299301e 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -11,11 +11,10 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" - stypes "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/store/cachekv" + "github.com/cosmos/cosmos-sdk/store/errors" "github.com/cosmos/cosmos-sdk/store/tracekv" + "github.com/cosmos/cosmos-sdk/store/types" ) const ( @@ -132,7 +131,7 @@ func (st *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.Ca // Implements types.KVStore. func (st *Store) Set(key, value []byte) { - stypes.AssertValidValue(value) + types.AssertValidValue(value) st.tree.Set(key, value) } @@ -186,7 +185,7 @@ func getHeight(tree *iavl.MutableTree, req abci.RequestQuery) int64 { func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { if len(req.Data) == 0 { msg := "Query cannot be zero length" - return types.ErrTxDecode(msg).QueryResult() + return errors.ErrTxDecode(msg).QueryResult() } tree := st.tree @@ -236,7 +235,7 @@ func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { subspace := req.Data res.Key = subspace - iterator := stypes.KVStorePrefixIterator(st, subspace) + iterator := types.KVStorePrefixIterator(st, subspace) for ; iterator.Valid(); iterator.Next() { KVs = append(KVs, types.KVPair{Key: iterator.Key(), Value: iterator.Value()}) } @@ -246,7 +245,7 @@ func (st *Store) Query(req abci.RequestQuery) (res abci.ResponseQuery) { default: msg := fmt.Sprintf("Unexpected Query path: %v", req.Path) - return types.ErrUnknownRequest(msg).QueryResult() + return errors.ErrUnknownRequest(msg).QueryResult() } return @@ -291,8 +290,8 @@ var _ types.Iterator = (*iavlIterator)(nil) func newIAVLIterator(tree *iavl.ImmutableTree, start, end []byte, ascending bool) *iavlIterator { iter := &iavlIterator{ tree: tree, - start: stypes.Cp(start), - end: stypes.Cp(end), + start: types.Cp(start), + end: types.Cp(end), ascending: ascending, iterCh: make(chan cmn.KVPair, 0), // Set capacity > 0? quitCh: make(chan struct{}), diff --git a/store/iavl/store_test.go b/store/iavl/store_test.go index 360fa712c..b46c202a5 100644 --- a/store/iavl/store_test.go +++ b/store/iavl/store_test.go @@ -11,7 +11,8 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" - "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/errors" + "github.com/cosmos/cosmos-sdk/store/types" ) var ( @@ -443,7 +444,7 @@ func TestIAVLStoreQuery(t *testing.T) { // query subspace before anything set qres := iavlStore.Query(querySub) - require.Equal(t, uint32(types.CodeOK), qres.Code) + require.Equal(t, uint32(errors.CodeOK), qres.Code) require.Equal(t, valExpSubEmpty, qres.Value) // set data @@ -452,24 +453,24 @@ func TestIAVLStoreQuery(t *testing.T) { // set data without commit, doesn't show up qres = iavlStore.Query(query) - require.Equal(t, uint32(types.CodeOK), qres.Code) + require.Equal(t, uint32(errors.CodeOK), qres.Code) require.Nil(t, qres.Value) // commit it, but still don't see on old version cid = iavlStore.Commit() qres = iavlStore.Query(query) - require.Equal(t, uint32(types.CodeOK), qres.Code) + require.Equal(t, uint32(errors.CodeOK), qres.Code) require.Nil(t, qres.Value) // but yes on the new version query.Height = cid.Version qres = iavlStore.Query(query) - require.Equal(t, uint32(types.CodeOK), qres.Code) + require.Equal(t, uint32(errors.CodeOK), qres.Code) require.Equal(t, v1, qres.Value) // and for the subspace qres = iavlStore.Query(querySub) - require.Equal(t, uint32(types.CodeOK), qres.Code) + require.Equal(t, uint32(errors.CodeOK), qres.Code) require.Equal(t, valExpSub1, qres.Value) // modify @@ -478,28 +479,28 @@ func TestIAVLStoreQuery(t *testing.T) { // query will return old values, as height is fixed qres = iavlStore.Query(query) - require.Equal(t, uint32(types.CodeOK), qres.Code) + require.Equal(t, uint32(errors.CodeOK), qres.Code) require.Equal(t, v1, qres.Value) // update to latest in the query and we are happy query.Height = cid.Version qres = iavlStore.Query(query) - require.Equal(t, uint32(types.CodeOK), qres.Code) + require.Equal(t, uint32(errors.CodeOK), qres.Code) require.Equal(t, v3, qres.Value) query2 := abci.RequestQuery{Path: "/key", Data: k2, Height: cid.Version} qres = iavlStore.Query(query2) - require.Equal(t, uint32(types.CodeOK), qres.Code) + require.Equal(t, uint32(errors.CodeOK), qres.Code) require.Equal(t, v2, qres.Value) // and for the subspace qres = iavlStore.Query(querySub) - require.Equal(t, uint32(types.CodeOK), qres.Code) + require.Equal(t, uint32(errors.CodeOK), qres.Code) require.Equal(t, valExpSub2, qres.Value) // default (height 0) will show latest -1 query0 := abci.RequestQuery{Path: "/key", Data: k1} qres = iavlStore.Query(query0) - require.Equal(t, uint32(types.CodeOK), qres.Code) + require.Equal(t, uint32(errors.CodeOK), qres.Code) require.Equal(t, v1, qres.Value) } diff --git a/store/rootmulti/proof_test.go b/store/rootmulti/proof_test.go index 885d0ddb4..5498fba1c 100644 --- a/store/rootmulti/proof_test.go +++ b/store/rootmulti/proof_test.go @@ -3,19 +3,18 @@ package rootmulti import ( "testing" - "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" "github.com/cosmos/cosmos-sdk/store/iavl" - stypes "github.com/cosmos/cosmos-sdk/store/types" + "github.com/cosmos/cosmos-sdk/store/types" ) func TestVerifyIAVLStoreQueryProof(t *testing.T) { // Create main tree for testing. db := dbm.NewMemDB() - iStore, err := iavl.LoadStore(db, types.CommitID{}, stypes.PruneNothing) + iStore, err := iavl.LoadStore(db, types.CommitID{}, types.PruneNothing) store := iStore.(*iavl.Store) require.Nil(t, err) store.Set([]byte("MYKEY"), []byte("MYVALUE")) diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 8be98a1d8..85d9b5b20 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -12,10 +12,11 @@ import ( "github.com/cosmos/cosmos-sdk/store/cachemulti" "github.com/cosmos/cosmos-sdk/store/dbadapter" + "github.com/cosmos/cosmos-sdk/store/errors" "github.com/cosmos/cosmos-sdk/store/iavl" "github.com/cosmos/cosmos-sdk/store/tracekv" "github.com/cosmos/cosmos-sdk/store/transient" - "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/types" ) const ( @@ -288,12 +289,12 @@ func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery { store := rs.getStoreByName(storeName) if store == nil { msg := fmt.Sprintf("no such store: %s", storeName) - return types.ErrUnknownRequest(msg).QueryResult() + return errors.ErrUnknownRequest(msg).QueryResult() } queryable, ok := store.(types.Queryable) if !ok { msg := fmt.Sprintf("store %s doesn't support queries", storeName) - return types.ErrUnknownRequest(msg).QueryResult() + return errors.ErrUnknownRequest(msg).QueryResult() } // trim the path and make the query @@ -305,12 +306,12 @@ func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery { } if res.Proof == nil || len(res.Proof.Ops) == 0 { - return types.ErrInternal("substore proof was nil/empty when it should never be").QueryResult() + return errors.ErrInternal("substore proof was nil/empty when it should never be").QueryResult() } commitInfo, errMsg := getCommitInfo(rs.db, res.Height) if errMsg != nil { - return types.ErrInternal(errMsg.Error()).QueryResult() + return errors.ErrInternal(errMsg.Error()).QueryResult() } // Restore origin path and append proof op. @@ -327,9 +328,9 @@ func (rs *Store) Query(req abci.RequestQuery) abci.ResponseQuery { // parsePath expects a format like /[/] // Must start with /, subpath may be empty // Returns error if it doesn't start with / -func parsePath(path string) (storeName string, subpath string, err types.Error) { +func parsePath(path string) (storeName string, subpath string, err errors.Error) { if !strings.HasPrefix(path, "/") { - err = types.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path)) + err = errors.ErrUnknownRequest(fmt.Sprintf("invalid path: %s", path)) return } diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index 75aed91b6..85ada5587 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -8,8 +8,8 @@ import ( "github.com/tendermint/tendermint/crypto/merkle" dbm "github.com/tendermint/tendermint/libs/db" - stypes "github.com/cosmos/cosmos-sdk/store/types" - "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/store/errors" + "github.com/cosmos/cosmos-sdk/store/types" ) const useDebugDB = false @@ -157,37 +157,37 @@ func TestMultiStoreQuery(t *testing.T) { // Test bad path. query := abci.RequestQuery{Path: "/key", Data: k, Height: ver} qres := multi.Query(query) - require.EqualValues(t, types.CodeUnknownRequest, qres.Code) - require.EqualValues(t, types.CodespaceRoot, qres.Codespace) + require.EqualValues(t, errors.CodeUnknownRequest, qres.Code) + require.EqualValues(t, errors.CodespaceRoot, qres.Codespace) query.Path = "h897fy32890rf63296r92" qres = multi.Query(query) - require.EqualValues(t, types.CodeUnknownRequest, qres.Code) - require.EqualValues(t, types.CodespaceRoot, qres.Codespace) + require.EqualValues(t, errors.CodeUnknownRequest, qres.Code) + require.EqualValues(t, errors.CodespaceRoot, qres.Codespace) // Test invalid store name. query.Path = "/garbage/key" qres = multi.Query(query) - require.EqualValues(t, types.CodeUnknownRequest, qres.Code) - require.EqualValues(t, types.CodespaceRoot, qres.Codespace) + require.EqualValues(t, errors.CodeUnknownRequest, qres.Code) + require.EqualValues(t, errors.CodespaceRoot, qres.Codespace) // Test valid query with data. query.Path = "/store1/key" qres = multi.Query(query) - require.EqualValues(t, types.CodeOK, qres.Code) + require.EqualValues(t, errors.CodeOK, qres.Code) require.Equal(t, v, qres.Value) // Test valid but empty query. query.Path = "/store2/key" query.Prove = true qres = multi.Query(query) - require.EqualValues(t, types.CodeOK, qres.Code) + require.EqualValues(t, errors.CodeOK, qres.Code) require.Nil(t, qres.Value) // Test store2 data. query.Data = k2 qres = multi.Query(query) - require.EqualValues(t, types.CodeOK, qres.Code) + require.EqualValues(t, errors.CodeOK, qres.Code) require.Equal(t, v2, qres.Value) } @@ -196,7 +196,7 @@ func TestMultiStoreQuery(t *testing.T) { func newMultiStoreWithMounts(db dbm.DB) *Store { store := NewStore(db) - store.pruningOpts = stypes.PruneSyncable + store.pruningOpts = types.PruneSyncable store.MountStoreWithDB( types.NewKVStoreKey("store1"), types.StoreTypeIAVL, nil) store.MountStoreWithDB( From 9f30bfdbd9394e7e16023e8198d62e2063a999f9 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 5 Feb 2019 16:36:10 -0800 Subject: [PATCH 57/99] Merge PR #3505: add json tags to auth params and fix tags in slashing --- x/auth/params.go | 10 +++++----- x/slashing/params.go | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/x/auth/params.go b/x/auth/params.go index bf0607cf2..11374bd8b 100644 --- a/x/auth/params.go +++ b/x/auth/params.go @@ -34,11 +34,11 @@ var _ params.ParamSet = &Params{} // Params defines the parameters for the auth module. type Params struct { - MaxMemoCharacters uint64 - TxSigLimit uint64 // max total number of signatures per tx - TxSizeCostPerByte uint64 - SigVerifyCostED25519 uint64 - SigVerifyCostSecp256k1 uint64 + MaxMemoCharacters uint64 `json:"max_memo_characters"` + TxSigLimit uint64 `json:"tx_sig_limit"` + TxSizeCostPerByte uint64 `json:"tx_size_cost_per_byte"` + SigVerifyCostED25519 uint64 `json:"sig_verify_cost_ed25519"` + SigVerifyCostSecp256k1 uint64 `json:"sig_verify_cost_secp256k1"` } // ParamTable for staking module diff --git a/x/slashing/params.go b/x/slashing/params.go index 87b3edddd..1b500882c 100644 --- a/x/slashing/params.go +++ b/x/slashing/params.go @@ -35,12 +35,12 @@ func ParamKeyTable() params.KeyTable { // Params - used for initializing default parameter for slashing at genesis type Params struct { - MaxEvidenceAge time.Duration `json:"max-evidence-age"` - SignedBlocksWindow int64 `json:"signed-blocks-window"` - MinSignedPerWindow sdk.Dec `json:"min-signed-per-window"` - DowntimeJailDuration time.Duration `json:"downtime-jail-duration"` - SlashFractionDoubleSign sdk.Dec `json:"slash-fraction-double-sign"` - SlashFractionDowntime sdk.Dec `json:"slash-fraction-downtime"` + MaxEvidenceAge time.Duration `json:"max_evidence_age"` + SignedBlocksWindow int64 `json:"signed_blocks_window"` + MinSignedPerWindow sdk.Dec `json:"min_signed_per_window"` + DowntimeJailDuration time.Duration `json:"downtime_jail_duration"` + SlashFractionDoubleSign sdk.Dec `json:"slash_fraction_double_sign"` + SlashFractionDowntime sdk.Dec `json:"slash_fraction_downtime"` } func (p Params) String() string { From 1766a73a4c247e4ddc7bfbc985d193747ef24430 Mon Sep 17 00:00:00 2001 From: Juan Leni Date: Wed, 6 Feb 2019 02:52:18 +0100 Subject: [PATCH 58/99] Merge PR #3502: Fixing bug in genesis Equal --- PENDING.md | 1 + x/gov/genesis.go | 4 ++-- x/gov/genesis_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/PENDING.md b/PENDING.md index 296d30d1c..00c16cdb3 100644 --- a/PENDING.md +++ b/PENDING.md @@ -22,6 +22,7 @@ BREAKING CHANGES * SDK * [\#3487](https://github.com/cosmos/cosmos-sdk/pull/3487) Move HTTP/REST utilities out of client/utils into a new dedicated client/rest package. * [\#3490](https://github.com/cosmos/cosmos-sdk/issues/3490) ReadRESTReq() returns bool to avoid callers to write error responses twice. + * [\#3502](https://github.com/cosmos/cosmos-sdk/pull/3502) Fixes issue when comparing genesis states * Tendermint diff --git a/x/gov/genesis.go b/x/gov/genesis.go index 9c44b2f00..a86085b22 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -62,7 +62,7 @@ func DefaultGenesisState() GenesisState { // Checks whether 2 GenesisState structs are equivalent. func (data GenesisState) Equal(data2 GenesisState) bool { - if data.StartingProposalID != data.StartingProposalID || + if data.StartingProposalID != data2.StartingProposalID || !data.DepositParams.Equal(data2.DepositParams) || data.VotingParams != data2.VotingParams || data.TallyParams != data2.TallyParams { @@ -97,7 +97,7 @@ func (data GenesisState) Equal(data2 GenesisState) bool { return false } for i := range data.Proposals { - if data.Proposals[i] != data.Proposals[i] { + if !ProposalEqual(data.Proposals[i], data2.Proposals[i]) { return false } } diff --git a/x/gov/genesis_test.go b/x/gov/genesis_test.go index 87d466b5f..61f2c299a 100644 --- a/x/gov/genesis_test.go +++ b/x/gov/genesis_test.go @@ -10,6 +10,53 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ) +func TestEqualProposalID(t *testing.T) { + state1 := GenesisState{} + state2 := GenesisState{} + require.Equal(t, state1, state2) + + // Proposals + state1.StartingProposalID = 1 + require.NotEqual(t, state1, state2) + require.False(t, state1.Equal(state2)) + + state2.StartingProposalID = 1 + require.Equal(t, state1, state2) + require.True(t, state1.Equal(state2)) +} + +func TestEqualProposals(t *testing.T) { + // Generate mock app and keepers + mapp, keeper, _, addrs, _, _ := getMockApp(t, 2, GenesisState{}, nil) + SortAddresses(addrs) + mapp.BeginBlock(abci.RequestBeginBlock{}) + ctx := mapp.BaseApp.NewContext(false, abci.Header{}) + + // Create two proposals + proposal1 := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) + proposal2 := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) + + // They are similar but their IDs should be different + require.NotEqual(t, proposal1, proposal2) + require.False(t, ProposalEqual(proposal1, proposal2)) + + // Now create two genesis blocks + state1 := GenesisState{Proposals: []Proposal{proposal1}} + state2 := GenesisState{Proposals: []Proposal{proposal2}} + require.NotEqual(t, state1, state2) + require.False(t, state1.Equal(state2)) + + // Now make proposals identical by setting both IDs to 55 + proposal1.SetProposalID(55) + proposal2.SetProposalID(55) + require.Equal(t, proposal1, proposal1) + require.True(t, ProposalEqual(proposal1, proposal2)) + + // State should be identical now.. + require.Equal(t, state1, state2) + require.True(t, state1.Equal(state2)) +} + func TestImportExportQueues(t *testing.T) { // Generate mock app and keepers From 52350c592b0b2319a471ac9f5ff9c58bf9f453b3 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 5 Feb 2019 19:11:57 -0800 Subject: [PATCH 59/99] Merge PR #3468: Increase BaseApp Test Coverage --- PENDING.md | 2 + baseapp/baseapp.go | 244 ++++++++++++++----------- baseapp/baseapp_test.go | 307 +++++++++++++++++++++++++------- baseapp/helpers.go | 31 +--- baseapp/options.go | 24 +-- baseapp/query.go | 54 ------ baseapp/query_test.go | 97 ---------- baseapp/queryrouter.go | 36 ++-- baseapp/queryrouter_test.go | 33 ++++ baseapp/router.go | 48 +++-- baseapp/router_test.go | 31 ++++ baseapp/testdata/genesis.json | 22 --- baseapp/testdata/genesis2.json | 39 ---- baseapp/testdata/genesis2b.json | 52 ------ baseapp/testdata/genesis3.json | 3 - 15 files changed, 497 insertions(+), 526 deletions(-) delete mode 100644 baseapp/query.go delete mode 100644 baseapp/query_test.go create mode 100644 baseapp/queryrouter_test.go create mode 100644 baseapp/router_test.go delete mode 100644 baseapp/testdata/genesis.json delete mode 100644 baseapp/testdata/genesis2.json delete mode 100644 baseapp/testdata/genesis2b.json delete mode 100644 baseapp/testdata/genesis3.json diff --git a/PENDING.md b/PENDING.md index 00c16cdb3..8aa9ca3c5 100644 --- a/PENDING.md +++ b/PENDING.md @@ -75,6 +75,8 @@ IMPROVEMENTS * [\#2605] x/params add subkey accessing * [\#2986](https://github.com/cosmos/cosmos-sdk/pull/2986) Store Refactor * \#3435 Test that store implementations do not allow nil values + * [\#556](https://github.com/cosmos/cosmos-sdk/issues/556) Increase `BaseApp` + test coverage. * Tendermint diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 2c24a57b1..d22df484a 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -52,7 +52,6 @@ type BaseApp struct { // set upon LoadVersion or LoadLatestVersion. mainKey *sdk.KVStoreKey // Main KVStore in cms - // may be nil anteHandler sdk.AnteHandler // ante handler for fee and auth initChainer sdk.InitChainer // initialize state with validators and state blob beginBlocker sdk.BeginBlocker // logic to run before any txs @@ -61,8 +60,8 @@ type BaseApp struct { pubkeyPeerFilter sdk.PeerFilter // filter peers by public key fauxMerkleMode bool // if true, IAVL MountStores uses MountStoresDB for simulation speed. - //-------------------- - // Volatile + // -------------------- + // Volatile state // checkState is set on initialization and reset on Commit. // deliverState is set in InitChain and BeginBlock and cleared on Commit. // See methods setCheckState and setDeliverState. @@ -71,25 +70,28 @@ type BaseApp struct { voteInfos []abci.VoteInfo // absent validators from begin block // consensus params - // TODO move this in the future to baseapp param store on main store. + // TODO: Move this in the future to baseapp param store on main store. consensusParams *abci.ConsensusParams // The minimum gas prices a validator is willing to accept for processing a // transaction. This is mainly used for DoS and spam prevention. minGasPrices sdk.DecCoins - // flag for sealing + // flag for sealing options and parameters to a BaseApp sealed bool } var _ abci.Application = (*BaseApp)(nil) -// NewBaseApp returns a reference to an initialized BaseApp. +// NewBaseApp returns a reference to an initialized BaseApp. It accepts a +// variadic number of option functions, which act on the BaseApp to set +// configuration choices. // // NOTE: The db is used to store the version number for now. -// Accepts a user-defined txDecoder -// 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 { +func NewBaseApp( + name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecoder, options ...func(*BaseApp), +) *BaseApp { + app := &BaseApp{ Logger: logger, name: name, @@ -103,10 +105,11 @@ func NewBaseApp(name string, logger log.Logger, db dbm.DB, txDecoder sdk.TxDecod for _, option := range options { option(app) } + return app } -// BaseApp Name +// Name returns the name of the BaseApp. func (app *BaseApp) Name() string { return app.name } @@ -117,7 +120,8 @@ func (app *BaseApp) SetCommitMultiStoreTracer(w io.Writer) { app.cms.SetTracer(w) } -// Mount IAVL or DB stores to the provided keys in the BaseApp multistore +// MountStores mounts all IAVL or DB stores to the provided keys in the BaseApp +// multistore. func (app *BaseApp) MountStores(keys ...*sdk.KVStoreKey) { for _, key := range keys { if !app.fauxMerkleMode { @@ -130,25 +134,28 @@ func (app *BaseApp) MountStores(keys ...*sdk.KVStoreKey) { } } -// Mount stores to the provided keys in the BaseApp multistore +// MountStoresTransient mounts transient stores to the provided keys in the +// BaseApp multistore. func (app *BaseApp) MountStoresTransient(keys ...*sdk.TransientStoreKey) { for _, key := range keys { app.MountStore(key, sdk.StoreTypeTransient) } } -// Mount a store to the provided key in the BaseApp multistore, using a specified DB +// MountStoreWithDB mounts a store to the provided key in the BaseApp +// multistore, using a specified DB. func (app *BaseApp) MountStoreWithDB(key sdk.StoreKey, typ sdk.StoreType, db dbm.DB) { app.cms.MountStoreWithDB(key, typ, db) } -// Mount a store to the provided key in the BaseApp multistore, using the default DB +// MountStore mounts a store to the provided key in the BaseApp multistore, +// using the default DB. func (app *BaseApp) MountStore(key sdk.StoreKey, typ sdk.StoreType) { app.cms.MountStoreWithDB(key, typ, nil) } -// load latest application version -// panics if called more than once on a running baseapp +// LoadLatestVersion loads the latest application version. It will panic if +// called more than once on a running BaseApp. func (app *BaseApp) LoadLatestVersion(mainKey *sdk.KVStoreKey) error { err := app.cms.LoadLatestVersion() if err != nil { @@ -157,8 +164,8 @@ func (app *BaseApp) LoadLatestVersion(mainKey *sdk.KVStoreKey) error { return app.initFromMainStore(mainKey) } -// load application version -// panics if called more than once on a running baseapp +// LoadVersion loads the BaseApp application version. It will panic if called +// more than once on a running baseapp. func (app *BaseApp) LoadVersion(version int64, mainKey *sdk.KVStoreKey) error { err := app.cms.LoadVersion(version) if err != nil { @@ -167,20 +174,18 @@ func (app *BaseApp) LoadVersion(version int64, mainKey *sdk.KVStoreKey) error { return app.initFromMainStore(mainKey) } -// the last CommitID of the multistore +// LastCommitID returns the last CommitID of the multistore. func (app *BaseApp) LastCommitID() sdk.CommitID { return app.cms.LastCommitID() } -// the last committed block height +// LastBlockHeight returns the last committed block height. func (app *BaseApp) LastBlockHeight() int64 { return app.cms.LastCommitID().Version } // initializes the remaining logic from app.cms func (app *BaseApp) initFromMainStore(mainKey *sdk.KVStoreKey) error { - - // main store should exist. mainStore := app.cms.GetKVStore(mainKey) if mainStore == nil { return errors.New("baseapp expects MultiStore with 'main' KVStore") @@ -192,23 +197,24 @@ func (app *BaseApp) initFromMainStore(mainKey *sdk.KVStoreKey) error { } app.mainKey = mainKey - // load consensus params from the main store + // Load the consensus params from the main store. If the consensus params are + // nil, it will be saved later during InitChain. + // + // TODO: assert that InitChain hasn't yet been called. consensusParamsBz := mainStore.Get(mainConsensusParamsKey) if consensusParamsBz != nil { var consensusParams = &abci.ConsensusParams{} + err := proto.Unmarshal(consensusParamsBz, consensusParams) if err != nil { panic(err) } + app.setConsensusParams(consensusParams) - } else { - // It will get saved later during InitChain. - // TODO assert that InitChain hasn't yet been called. } - // Needed for `gaiad export`, which inits from store but never calls initchain + // needed for `gaiad export`, which inits from store but never calls initchain app.setCheckState(abci.Header{}) - app.Seal() return nil @@ -218,7 +224,8 @@ func (app *BaseApp) setMinGasPrices(gasPrices sdk.DecCoins) { app.minGasPrices = gasPrices } -// NewContext returns a new Context with the correct store, the given header, and nil txBytes. +// NewContext returns a new Context with the correct store, the given header, +// and nil txBytes. func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { if isCheckTx { return sdk.NewContext(app.checkState.ms, header, true, app.Logger). @@ -228,18 +235,24 @@ func (app *BaseApp) NewContext(isCheckTx bool, header abci.Header) sdk.Context { return sdk.NewContext(app.deliverState.ms, header, false, app.Logger) } -type state struct { - ms sdk.CacheMultiStore - ctx sdk.Context +// Router returns the router of the BaseApp. +func (app *BaseApp) Router() Router { + if app.sealed { + // We cannot return a router when the app is sealed because we can't have + // any routes modified which would cause unexpected routing behavior. + panic("Router() on sealed BaseApp") + } + return app.router } -func (st *state) CacheMultiStore() sdk.CacheMultiStore { - return st.ms.CacheMultiStore() -} +// QueryRouter returns the QueryRouter of a BaseApp. +func (app *BaseApp) QueryRouter() QueryRouter { return app.queryRouter } -func (st *state) Context() sdk.Context { - return st.ctx -} +// Seal seals a BaseApp. It prohibits any further modifications to a BaseApp. +func (app *BaseApp) Seal() { app.sealed = true } + +// IsSealed returns true if the BaseApp is sealed and false otherwise. +func (app *BaseApp) IsSealed() bool { return app.sealed } func (app *BaseApp) setCheckState(header abci.Header) { ms := app.cms.CacheMultiStore() @@ -280,11 +293,10 @@ func (app *BaseApp) getMaximumBlockGas() (maxGas uint64) { return uint64(app.consensusParams.BlockSize.MaxGas) } -//______________________________________________________________________________ - +// ---------------------------------------------------------------------------- // ABCI -// Implements ABCI +// Info implements the ABCI interface. func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { lastCommitID := app.cms.LastCommitID() @@ -295,23 +307,23 @@ func (app *BaseApp) Info(req abci.RequestInfo) abci.ResponseInfo { } } -// Implements ABCI +// SetOption implements the ABCI interface. func (app *BaseApp) SetOption(req abci.RequestSetOption) (res abci.ResponseSetOption) { - // TODO: Implement + // TODO: Implement! return } -// Implements ABCI -// InitChain runs the initialization logic directly on the CommitMultiStore. +// InitChain implements the ABCI interface. It runs the initialization logic +// directly on the CommitMultiStore. func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitChain) { - // Stash the consensus params in the cms main store and memoize. + // stash the consensus params in the cms main store and memoize if req.ConsensusParams != nil { app.setConsensusParams(req.ConsensusParams) app.storeConsensusParams(req.ConsensusParams) } - // Initialize the deliver state and check state with ChainID and run initChain + // initialize the deliver state and check state with ChainID and run initChain app.setDeliverState(abci.Header{ChainID: req.ChainId}) app.setCheckState(abci.Header{ChainID: req.ChainId}) @@ -325,12 +337,12 @@ func (app *BaseApp) InitChain(req abci.RequestInitChain) (res abci.ResponseInitC res = app.initChainer(app.deliverState.ctx, req) - // NOTE: we don't commit, but BeginBlock for block 1 - // starts from this deliverState + // NOTE: We don't commit, but BeginBlock for block 1 starts from this + // deliverState. return } -// Filter peers by address / port +// FilterPeerByAddrPort filters peers by address/port. func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery { if app.addrPeerFilter != nil { return app.addrPeerFilter(info) @@ -338,7 +350,7 @@ func (app *BaseApp) FilterPeerByAddrPort(info string) abci.ResponseQuery { return abci.ResponseQuery{} } -// Filter peers by public key +// FilterPeerByPubKey filters peers by a public key. func (app *BaseApp) FilterPeerByPubKey(info string) abci.ResponseQuery { if app.pubkeyPeerFilter != nil { return app.pubkeyPeerFilter(info) @@ -346,7 +358,8 @@ 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"} +// Splits a string path using the delimiter '/'. +// e.g. "this/is/funny" becomes []string{"this", "is", "funny"} func splitPath(requestPath string) (path []string) { path = strings.Split(requestPath, "/") // first element is empty string @@ -356,22 +369,26 @@ func splitPath(requestPath string) (path []string) { return path } -// Implements ABCI. -// Delegates to CommitMultiStore if it implements Queryable +// Query implements the ABCI interface. It delegates to CommitMultiStore if it +// implements Queryable. func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { path := splitPath(req.Path) if len(path) == 0 { msg := "no query path provided" return sdk.ErrUnknownRequest(msg).QueryResult() } + switch path[0] { // "/app" prefix for special application queries case "app": return handleQueryApp(app, path, req) + case "store": return handleQueryStore(app, path, req) + case "p2p": return handleQueryP2P(app, path, req) + case "custom": return handleQueryCustom(app, path, req) } @@ -383,6 +400,7 @@ func (app *BaseApp) Query(req abci.RequestQuery) (res abci.ResponseQuery) { func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { if len(path) >= 2 { var result sdk.Result + switch path[1] { case "simulate": txBytes := req.Data @@ -392,17 +410,18 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc } else { result = app.Simulate(txBytes, tx) } + case "version": return abci.ResponseQuery{ Code: uint32(sdk.CodeOK), Codespace: string(sdk.CodespaceRoot), Value: []byte(version.Version), } + default: result = sdk.ErrUnknownRequest(fmt.Sprintf("Unknown query: %s", path)).Result() } - // Encode with json value := codec.Cdc.MustMarshalBinaryLengthPrefixed(result) return abci.ResponseQuery{ Code: uint32(sdk.CodeOK), @@ -410,6 +429,7 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) (res abc Value: value, } } + msg := "Expected second parameter to be either simulate or version, neither was present" return sdk.ErrUnknownRequest(msg).QueryResult() } @@ -421,12 +441,12 @@ func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) (res a msg := "multistore doesn't support queries" return sdk.ErrUnknownRequest(msg).QueryResult() } + req.Path = "/" + strings.Join(path[1:], "/") return queryable.Query(req) } -// nolint: unparam -func handleQueryP2P(app *BaseApp, path []string, req abci.RequestQuery) (res abci.ResponseQuery) { +func handleQueryP2P(app *BaseApp, path []string, _ abci.RequestQuery) (res abci.ResponseQuery) { // "/p2p" prefix for p2p queries if len(path) >= 4 { if path[1] == "filter" { @@ -449,23 +469,29 @@ 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" + // 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 len(path) < 2 || path[1] == "" { return sdk.ErrUnknownRequest("No route for custom query specified").QueryResult() } + querier := app.queryRouter.Route(path[1]) if querier == nil { return sdk.ErrUnknownRequest(fmt.Sprintf("no custom querier found for route %s", path[1])).QueryResult() } - // Cache wrap the commit-multistore for safety. + // cache wrap the commit-multistore for safety ctx := sdk.NewContext( app.cms.CacheMultiStore(), app.checkState.ctx.BlockHeader(), true, app.Logger, ).WithMinGasPrices(app.minGasPrices) // 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 + // + // 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{ @@ -474,6 +500,7 @@ func handleQueryCustom(app *BaseApp, path []string, req abci.RequestQuery) (res Log: err.ABCILog(), } } + return abci.ResponseQuery{ Code: uint32(sdk.CodeOK), Value: resBytes, @@ -521,15 +548,16 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg return } -// CheckTx implements ABCI -// CheckTx runs the "basic checks" to see whether or not a transaction can possibly be executed, -// first decoding, then the ante handler (which checks signatures/fees/ValidateBasic), -// then finally the route match to see whether a handler exists. CheckTx does not run the actual -// Msg handler function(s). +// CheckTx implements the ABCI interface. It runs the "basic checks" to see +// whether or not a transaction can possibly be executed, first decoding, then +// the ante handler (which checks signatures/fees/ValidateBasic), then finally +// the route match to see whether a handler exists. +// +// NOTE:CheckTx does not run the actual Msg handler function(s). func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { - // Decode the Tx. var result sdk.Result - var tx, err = app.txDecoder(txBytes) + + tx, err := app.txDecoder(txBytes) if err != nil { result = err.Result() } else { @@ -546,22 +574,17 @@ func (app *BaseApp) CheckTx(txBytes []byte) (res abci.ResponseCheckTx) { } } -// Implements ABCI +// DeliverTx implements the ABCI interface. func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { - - // Decode the Tx. - var tx, err = app.txDecoder(txBytes) var result sdk.Result + + tx, err := app.txDecoder(txBytes) if err != nil { result = err.Result() } else { result = app.runTx(runTxModeDeliver, txBytes, tx) } - // Even though the Result.Code is not OK, there are still effects, - // namely fee deductions and sequence incrementing. - - // Tell the blockchain engine (i.e. Tendermint). return abci.ResponseDeliverTx{ Code: uint32(result.Code), Codespace: string(result.Codespace), @@ -573,10 +596,10 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) { } } -// Basic validator for msgs +// validateBasicTxMsgs executes basic validator calls for messages. func validateBasicTxMsgs(msgs []sdk.Msg) sdk.Error { if msgs == nil || len(msgs) == 0 { - // TODO: probably shouldn't be ErrInternal. Maybe new ErrInvalidMessage, or ? + // TODO: Probably shouldn't be ErrInternal. Maybe ErrInvalidMessage? return sdk.ErrInternal("Tx.GetMsgs() must return at least one message in list") } @@ -597,22 +620,25 @@ func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) (ctx sdk.Con WithTxBytes(txBytes). WithVoteInfos(app.voteInfos). WithConsensusParams(app.consensusParams) + if mode == runTxModeSimulate { ctx, _ = ctx.CacheContext() } + return } -// Iterates through msgs and executes them +// runMsgs iterates through all the messages and executes them. func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (result sdk.Result) { - // accumulate results logs := make([]string, 0, len(msgs)) + var data []byte // NOTE: we just append them all (?!) var tags sdk.Tags // also just append them all var code sdk.CodeType var codespace sdk.CodespaceType + for msgIdx, msg := range msgs { - // Match route. + // match message route msgRoute := msg.Route() handler := app.router.Route(msgRoute) if handler == nil { @@ -620,20 +646,19 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re } var msgResult sdk.Result - // Skip actual execution for CheckTx + + // skip actual execution for CheckTx mode if mode != runTxModeCheck { msgResult = handler(ctx, msg) } - // NOTE: GasWanted is determined by ante handler and - // GasUsed by the GasMeter + // NOTE: GasWanted is determined by ante handler and GasUsed by the GasMeter. - // Append Data and Tags data = append(data, msgResult.Data...) tags = append(tags, sdk.MakeTag(sdk.TagAction, msg.Type())) tags = append(tags, msgResult.Tags...) - // Stop execution and return on first failed message. + // stop execution and return on first failed message if !msgResult.IsOK() { logs = append(logs, fmt.Sprintf("Msg %d failed: %s", msgIdx, msgResult.Log)) code = msgResult.Code @@ -641,11 +666,10 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, mode runTxMode) (re break } - // Construct usable logs in multi-message transactions. + // construct usable logs in multi-message transactions logs = append(logs, fmt.Sprintf("Msg %d: %s", msgIdx, msgResult.Log)) } - // Set the final gas values. result = sdk.Result{ Code: code, Codespace: codespace, @@ -733,11 +757,11 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk result.GasUsed = ctx.GasMeter().GasConsumed() }() - // If BlockGasMeter() panics it will be caught by the above recover and - // return an error - in any case BlockGasMeter will consume gas past - // the limit. - // NOTE: this must exist in a separate defer function for the - // above recovery to recover from this one + // If BlockGasMeter() panics it will be caught by the above recover and will + // return an error - in any case BlockGasMeter will consume gas past the limit. + // + // NOTE: This must exist in a separate defer function for the above recovery + // to recover from this one. defer func() { if mode == runTxModeDeliver { ctx.BlockGasMeter().ConsumeGas( @@ -756,7 +780,6 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk return err.Result() } - // Execute the ante handler if one is defined. if app.anteHandler != nil { var anteCtx sdk.Context var msCache sdk.CacheMultiStore @@ -812,7 +835,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte, tx sdk.Tx) (result sdk return } -// EndBlock implements the ABCI application interface. +// EndBlock implements the ABCI interface. func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) { if app.deliverState.ms.TracingEnabled() { app.deliverState.ms = app.deliverState.ms.SetTracingContext(nil).(sdk.CacheMultiStore) @@ -825,27 +848,42 @@ func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBloc return } -// Implements ABCI +// Commit implements the ABCI interface. func (app *BaseApp) Commit() (res abci.ResponseCommit) { header := app.deliverState.ctx.BlockHeader() - // Write the Deliver state and commit the MultiStore + // write the Deliver state and commit the MultiStore app.deliverState.ms.Write() commitID := app.cms.Commit() - // TODO: this is missing a module identifier and dumps byte array - app.Logger.Debug("Commit synced", - "commit", fmt.Sprintf("%X", commitID), - ) - // Reset the Check state to the latest committed + app.Logger.Debug("Commit synced", "commit", fmt.Sprintf("%X", commitID)) + + // Reset the Check state to the latest committed. + // // NOTE: safe because Tendermint holds a lock on the mempool for Commit. // Use the header from this latest block. app.setCheckState(header) - // Empty the Deliver state + // empty/reset the deliver state app.deliverState = nil return abci.ResponseCommit{ Data: commitID.Hash, } } + +// ---------------------------------------------------------------------------- +// State + +type state struct { + ms sdk.CacheMultiStore + ctx sdk.Context +} + +func (st *state) CacheMultiStore() sdk.CacheMultiStore { + return st.ms.CacheMultiStore() +} + +func (st *state) Context() sdk.Context { + return st.ctx +} diff --git a/baseapp/baseapp_test.go b/baseapp/baseapp_test.go index 378249106..d48ec433e 100644 --- a/baseapp/baseapp_test.go +++ b/baseapp/baseapp_test.go @@ -21,14 +21,10 @@ import ( ) var ( - // make some cap keys capKey1 = sdk.NewKVStoreKey("key1") capKey2 = sdk.NewKVStoreKey("key2") ) -//------------------------------------------------------------------------------------------ -// Helpers for setup. Most tests should be able to use setupBaseApp - func defaultLogger() log.Logger { return log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app") } @@ -70,9 +66,6 @@ func setupBaseApp(t *testing.T, options ...func(*BaseApp)) *BaseApp { return app } -//------------------------------------------------------------------------------------------ -// test mounting and loading stores - func TestMountStores(t *testing.T) { app := setupBaseApp(t) @@ -137,6 +130,41 @@ func TestLoadVersion(t *testing.T) { testLoadVersionHelper(t, app, int64(2), commitID2) } +func TestLoadVersionInvalid(t *testing.T) { + logger := log.NewNopLogger() + pruningOpt := SetPruning(store.PruneSyncable) + db := dbm.NewMemDB() + name := t.Name() + app := NewBaseApp(name, logger, db, nil, pruningOpt) + + capKey := sdk.NewKVStoreKey(MainStoreKey) + app.MountStores(capKey) + err := app.LoadLatestVersion(capKey) + require.Nil(t, err) + + // require error when loading an invalid version + err = app.LoadVersion(-1, capKey) + require.Error(t, err) + + header := abci.Header{Height: 1} + app.BeginBlock(abci.RequestBeginBlock{Header: header}) + res := app.Commit() + commitID1 := sdk.CommitID{1, res.Data} + + // create a new app with the stores mounted under the same cap key + app = NewBaseApp(name, logger, db, nil, pruningOpt) + app.MountStores(capKey) + + // require we can load the latest version + err = app.LoadVersion(1, capKey) + require.Nil(t, err) + testLoadVersionHelper(t, app, int64(1), commitID1) + + // require error when loading an invalid version + err = app.LoadVersion(2, capKey) + require.Error(t, err) +} + func testLoadVersionHelper(t *testing.T, app *BaseApp, expectedHeight int64, expectedID sdk.CommitID) { lastHeight := app.LastBlockHeight() lastID := app.LastCommitID() @@ -157,39 +185,21 @@ func testChangeNameHelper(name string) func(*BaseApp) { } } -// Test that the app hash is static -// TODO: https://github.com/cosmos/cosmos-sdk/issues/520 -/*func TestStaticAppHash(t *testing.T) { - app := newBaseApp(t.Name()) - - // make a cap key and mount the store - capKey := sdk.NewKVStoreKey(MainStoreKey) - app.MountStores(capKey) - err := app.LoadLatestVersion(capKey) // needed to make stores non-nil - require.Nil(t, err) - - // execute some blocks - header := abci.Header{Height: 1} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res := app.Commit() - commitID1 := sdk.CommitID{1, res.Data} - - header = abci.Header{Height: 2} - app.BeginBlock(abci.RequestBeginBlock{Header: header}) - res = app.Commit() - commitID2 := sdk.CommitID{2, res.Data} - - require.Equal(t, commitID1.Hash, commitID2.Hash) -} -*/ - -//------------------------------------------------------------------------------------------ -// test some basic abci/baseapp functionality - // Test that txs can be unmarshalled and read and that // correct error codes are returned when not func TestTxDecoder(t *testing.T) { - // TODO + codec := codec.New() + registerTestCodec(codec) + + app := newBaseApp(t.Name()) + tx := newTxCounter(1, 0) + txBytes := codec.MustMarshalBinaryLengthPrefixed(tx) + + dTx, err := app.txDecoder(txBytes) + require.NoError(t, err) + + cTx := dTx.(txTest) + require.Equal(t, tx.Counter, cTx.Counter) } // Test that Info returns the latest committed state. @@ -210,8 +220,46 @@ func TestInfo(t *testing.T) { // TODO } -//------------------------------------------------------------------------------------------ -// InitChain, BeginBlock, EndBlock +func TestBaseAppOptionSeal(t *testing.T) { + app := setupBaseApp(t) + + require.Panics(t, func() { + app.SetName("") + }) + require.Panics(t, func() { + app.SetDB(nil) + }) + require.Panics(t, func() { + app.SetCMS(nil) + }) + require.Panics(t, func() { + app.SetInitChainer(nil) + }) + require.Panics(t, func() { + app.SetBeginBlocker(nil) + }) + require.Panics(t, func() { + app.SetEndBlocker(nil) + }) + require.Panics(t, func() { + app.SetAnteHandler(nil) + }) + require.Panics(t, func() { + app.SetAddrPeerFilter(nil) + }) + require.Panics(t, func() { + app.SetPubKeyPeerFilter(nil) + }) + require.Panics(t, func() { + app.SetFauxMerkleMode() + }) +} + +func TestSetMinGasPrices(t *testing.T) { + minGasPrices := sdk.DecCoins{sdk.NewDecCoin("stake", 5000)} + app := newBaseApp(t.Name(), SetMinGasPrices(minGasPrices.String())) + require.Equal(t, minGasPrices, app.minGasPrices) +} func TestInitChainer(t *testing.T) { name := t.Name() @@ -280,11 +328,6 @@ func TestInitChainer(t *testing.T) { require.Equal(t, value, res.Value) } -//------------------------------------------------------------------------------------------ -// Mock tx, msgs, and mapper for the baseapp tests. -// Self-contained, just uses counters. -// We don't care about signatures, coins, accounts, etc. in the baseapp. - // Simple tx with a list of Msgs. type txTest struct { Msgs []sdk.Msg @@ -417,9 +460,6 @@ func handlerMsgCounter(t *testing.T, capKey *sdk.KVStoreKey, deliverKey []byte) } } -//----------------------------------------------------------------- -// simple int mapper - func i2b(i int64) []byte { return []byte{byte(i)} } @@ -686,10 +726,6 @@ func TestSimulateTx(t *testing.T) { } } -//------------------------------------------------------------------------------------------- -// Tx failure cases -// TODO: add more - func TestRunInvalidTransaction(t *testing.T) { anteOpt := func(bapp *BaseApp) { bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { @@ -777,11 +813,6 @@ func TestTxGasLimits(t *testing.T) { bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted)) - // NOTE/TODO/XXX: - // AnteHandlers must have their own defer/recover in order - // for the BaseApp to know how much gas was used used! - // This is because the GasMeter is created in the AnteHandler, - // but if it panics the context won't be set properly in runTx's recover ... defer func() { if r := recover(); r != nil { switch rType := r.(type) { @@ -866,11 +897,6 @@ func TestMaxBlockGasLimits(t *testing.T) { bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasGranted)) - // NOTE/TODO/XXX: - // AnteHandlers must have their own defer/recover in order - // for the BaseApp to know how much gas was used used! - // This is because the GasMeter is created in the AnteHandler, - // but if it panics the context won't be set properly in runTx's recover ... defer func() { if r := recover(); r != nil { switch rType := r.(type) { @@ -1031,3 +1057,162 @@ func TestBaseAppAnteHandler(t *testing.T) { app.EndBlock(abci.RequestEndBlock{}) app.Commit() } + +func TestGasConsumptionBadTx(t *testing.T) { + gasWanted := uint64(5) + anteOpt := func(bapp *BaseApp) { + bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { + newCtx = ctx.WithGasMeter(sdk.NewGasMeter(gasWanted)) + + defer func() { + if r := recover(); r != nil { + switch rType := r.(type) { + case sdk.ErrorOutOfGas: + log := fmt.Sprintf("out of gas in location: %v", rType.Descriptor) + res = sdk.ErrOutOfGas(log).Result() + res.GasWanted = gasWanted + res.GasUsed = newCtx.GasMeter().GasConsumed() + default: + panic(r) + } + } + }() + + txTest := tx.(txTest) + newCtx.GasMeter().ConsumeGas(uint64(txTest.Counter), "counter-ante") + if txTest.FailOnAnte { + return newCtx, sdk.ErrInternal("ante handler failure").Result(), true + } + + res = sdk.Result{ + GasWanted: gasWanted, + } + return + }) + } + + routerOpt := func(bapp *BaseApp) { + bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + count := msg.(msgCounter).Counter + ctx.GasMeter().ConsumeGas(uint64(count), "counter-handler") + return sdk.Result{} + }) + } + + cdc := codec.New() + registerTestCodec(cdc) + + app := setupBaseApp(t, anteOpt, routerOpt) + app.InitChain(abci.RequestInitChain{ + ConsensusParams: &abci.ConsensusParams{ + BlockSize: &abci.BlockSizeParams{ + MaxGas: 9, + }, + }, + }) + + app.InitChain(abci.RequestInitChain{}) + app.BeginBlock(abci.RequestBeginBlock{}) + + tx := newTxCounter(5, 0) + tx.setFailOnAnte(true) + txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) + require.NoError(t, err) + + res := app.DeliverTx(txBytes) + require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) + + // require next tx to fail due to black gas limit + tx = newTxCounter(5, 0) + txBytes, err = cdc.MarshalBinaryLengthPrefixed(tx) + require.NoError(t, err) + + res = app.DeliverTx(txBytes) + require.False(t, res.IsOK(), fmt.Sprintf("%v", res)) +} + +// Test that we can only query from the latest committed state. +func TestQuery(t *testing.T) { + key, value := []byte("hello"), []byte("goodbye") + anteOpt := func(bapp *BaseApp) { + bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { + store := ctx.KVStore(capKey1) + store.Set(key, value) + return + }) + } + + routerOpt := func(bapp *BaseApp) { + bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + store := ctx.KVStore(capKey1) + store.Set(key, value) + return sdk.Result{} + }) + } + + app := setupBaseApp(t, anteOpt, routerOpt) + + app.InitChain(abci.RequestInitChain{}) + + // NOTE: "/store/key1" tells us KVStore + // and the final "/key" says to use the data as the + // key in the given KVStore ... + query := abci.RequestQuery{ + Path: "/store/key1/key", + Data: key, + } + tx := newTxCounter(0, 0) + + // query is empty before we do anything + res := app.Query(query) + require.Equal(t, 0, len(res.Value)) + + // query is still empty after a CheckTx + resTx := app.Check(tx) + require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx)) + res = app.Query(query) + require.Equal(t, 0, len(res.Value)) + + // query is still empty after a DeliverTx before we commit + app.BeginBlock(abci.RequestBeginBlock{}) + resTx = app.Deliver(tx) + require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx)) + res = app.Query(query) + require.Equal(t, 0, len(res.Value)) + + // query returns correct value after Commit + app.Commit() + res = app.Query(query) + require.Equal(t, value, res.Value) +} + +// Test p2p filter queries +func TestP2PQuery(t *testing.T) { + addrPeerFilterOpt := func(bapp *BaseApp) { + bapp.SetAddrPeerFilter(func(addrport string) abci.ResponseQuery { + require.Equal(t, "1.1.1.1:8000", addrport) + return abci.ResponseQuery{Code: uint32(3)} + }) + } + + pubkeyPeerFilterOpt := func(bapp *BaseApp) { + bapp.SetPubKeyPeerFilter(func(pubkey string) abci.ResponseQuery { + require.Equal(t, "testpubkey", pubkey) + return abci.ResponseQuery{Code: uint32(4)} + }) + } + + app := setupBaseApp(t, addrPeerFilterOpt, pubkeyPeerFilterOpt) + + addrQuery := abci.RequestQuery{ + Path: "/p2p/filter/addr/1.1.1.1:8000", + } + res := app.Query(addrQuery) + require.Equal(t, uint32(3), res.Code) + + pubkeyQuery := abci.RequestQuery{ + Path: "/p2p/filter/pubkey/testpubkey", + } + res = app.Query(pubkeyQuery) + require.Equal(t, uint32(4), res.Code) +} diff --git a/baseapp/helpers.go b/baseapp/helpers.go index fbc3df99d..30425053b 100644 --- a/baseapp/helpers.go +++ b/baseapp/helpers.go @@ -1,13 +1,13 @@ package baseapp import ( - "github.com/tendermint/tendermint/abci/server" - abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" + "regexp" sdk "github.com/cosmos/cosmos-sdk/types" ) +var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString + // nolint - Mostly for testing func (app *BaseApp) Check(tx sdk.Tx) (result sdk.Result) { return app.runTx(runTxModeCheck, nil, tx) @@ -22,28 +22,3 @@ func (app *BaseApp) Simulate(txBytes []byte, tx sdk.Tx) (result sdk.Result) { func (app *BaseApp) Deliver(tx sdk.Tx) (result sdk.Result) { return app.runTx(runTxModeDeliver, nil, tx) } - -// RunForever BasecoinApp execution and cleanup -func RunForever(app abci.Application) { - - // Start the ABCI server - srv, err := server.NewServer("0.0.0.0:26658", "socket", app) - if err != nil { - cmn.Exit(err.Error()) - return - } - err = srv.Start() - if err != nil { - cmn.Exit(err.Error()) - return - } - - // Wait forever - cmn.TrapSignal(func() { - // Cleanup - err := srv.Stop() - if err != nil { - cmn.Exit(err.Error()) - } - }) -} diff --git a/baseapp/options.go b/baseapp/options.go index 8e6687335..5f44bf1b5 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -18,7 +18,7 @@ func SetPruning(opts sdk.PruningOptions) func(*BaseApp) { return func(bap *BaseApp) { bap.cms.SetPruning(opts) } } -// SetMinimumGasPrices returns an option that sets the minimum gas prices on the app. +// SetMinGasPrices returns an option that sets the minimum gas prices on the app. func SetMinGasPrices(gasPricesStr string) func(*BaseApp) { gasPrices, err := sdk.ParseDecCoins(gasPricesStr) if err != nil { @@ -97,25 +97,3 @@ func (app *BaseApp) SetFauxMerkleMode() { } app.fauxMerkleMode = true } - -//---------------------------------------- -// TODO: move these out of this file? - -func (app *BaseApp) Router() Router { - if app.sealed { - panic("Router() on sealed BaseApp") - } - return app.router -} - -func (app *BaseApp) QueryRouter() QueryRouter { - return app.queryRouter -} - -func (app *BaseApp) Seal() { app.sealed = true } -func (app *BaseApp) IsSealed() bool { return app.sealed } -func (app *BaseApp) enforceSeal() { - if !app.sealed { - panic("enforceSeal() on BaseApp but not sealed") - } -} diff --git a/baseapp/query.go b/baseapp/query.go deleted file mode 100644 index 6527fda4f..000000000 --- a/baseapp/query.go +++ /dev/null @@ -1,54 +0,0 @@ -package baseapp - -/* - XXX Make this work with MultiStore. - XXX It will require some interfaces updates in store/types.go. - - if len(reqQuery.Data) == 0 { - resQuery.Log = "Query cannot be zero length" - resQuery.Code = abci.CodeType_EncodingError - return - } - - // set the query response height to current - tree := app.state.Committed() - - height := reqQuery.Height - if height == 0 { - // TODO: once the rpc actually passes in non-zero - // heights we can use to query right after a tx - // we must retrun most recent, even if apphash - // is not yet in the blockchain - - withProof := app.CommittedHeight() - 1 - if tree.Tree.VersionExists(withProof) { - height = withProof - } else { - height = app.CommittedHeight() - } - } - resQuery.Height = height - - switch reqQuery.Path { - case "/store", "/key": // Get by key - key := reqQuery.Data // Data holds the key bytes - resQuery.Key = key - if reqQuery.Prove { - value, proof, err := tree.GetVersionedWithProof(key, height) - if err != nil { - resQuery.Log = err.Error() - break - } - resQuery.Value = value - resQuery.Proof = proof.Bytes() - } else { - value := tree.Get(key) - resQuery.Value = value - } - - default: - resQuery.Code = abci.CodeType_UnknownRequest - resQuery.Log = cmn.Fmt("Unexpected Query path: %v", reqQuery.Path) - } - return -*/ diff --git a/baseapp/query_test.go b/baseapp/query_test.go deleted file mode 100644 index fed7ae477..000000000 --- a/baseapp/query_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package baseapp - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// Test that we can only query from the latest committed state. -func TestQuery(t *testing.T) { - key, value := []byte("hello"), []byte("goodbye") - anteOpt := func(bapp *BaseApp) { - bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, res sdk.Result, abort bool) { - store := ctx.KVStore(capKey1) - store.Set(key, value) - return - }) - } - - routerOpt := func(bapp *BaseApp) { - bapp.Router().AddRoute(routeMsgCounter, func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - store := ctx.KVStore(capKey1) - store.Set(key, value) - return sdk.Result{} - }) - } - - app := setupBaseApp(t, anteOpt, routerOpt) - - app.InitChain(abci.RequestInitChain{}) - - // NOTE: "/store/key1" tells us KVStore - // and the final "/key" says to use the data as the - // key in the given KVStore ... - query := abci.RequestQuery{ - Path: "/store/key1/key", - Data: key, - } - tx := newTxCounter(0, 0) - - // query is empty before we do anything - res := app.Query(query) - require.Equal(t, 0, len(res.Value)) - - // query is still empty after a CheckTx - resTx := app.Check(tx) - require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx)) - res = app.Query(query) - require.Equal(t, 0, len(res.Value)) - - // query is still empty after a DeliverTx before we commit - app.BeginBlock(abci.RequestBeginBlock{}) - resTx = app.Deliver(tx) - require.True(t, resTx.IsOK(), fmt.Sprintf("%v", resTx)) - res = app.Query(query) - require.Equal(t, 0, len(res.Value)) - - // query returns correct value after Commit - app.Commit() - res = app.Query(query) - require.Equal(t, value, res.Value) -} - -// Test p2p filter queries -func TestP2PQuery(t *testing.T) { - addrPeerFilterOpt := func(bapp *BaseApp) { - bapp.SetAddrPeerFilter(func(addrport string) abci.ResponseQuery { - require.Equal(t, "1.1.1.1:8000", addrport) - return abci.ResponseQuery{Code: uint32(3)} - }) - } - - pubkeyPeerFilterOpt := func(bapp *BaseApp) { - bapp.SetPubKeyPeerFilter(func(pubkey string) abci.ResponseQuery { - require.Equal(t, "testpubkey", pubkey) - return abci.ResponseQuery{Code: uint32(4)} - }) - } - - app := setupBaseApp(t, addrPeerFilterOpt, pubkeyPeerFilterOpt) - - addrQuery := abci.RequestQuery{ - Path: "/p2p/filter/addr/1.1.1.1:8000", - } - res := app.Query(addrQuery) - require.Equal(t, uint32(3), res.Code) - - pubkeyQuery := abci.RequestQuery{ - Path: "/p2p/filter/pubkey/testpubkey", - } - res = app.Query(pubkeyQuery) - require.Equal(t, uint32(4), res.Code) -} diff --git a/baseapp/queryrouter.go b/baseapp/queryrouter.go index 23cfad072..178646b7e 100644 --- a/baseapp/queryrouter.go +++ b/baseapp/queryrouter.go @@ -1,6 +1,8 @@ package baseapp import ( + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -10,32 +12,34 @@ type QueryRouter interface { Route(path string) (h sdk.Querier) } -type queryrouter struct { +type queryRouter struct { routes map[string]sdk.Querier } -// nolint -// NewRouter - create new router -// TODO either make Function unexported or make return type (router) Exported -func NewQueryRouter() *queryrouter { - return &queryrouter{ +// NewQueryRouter returns a reference to a new queryRouter. +// +// TODO: Either make the function private or make return type (queryRouter) public. +func NewQueryRouter() *queryRouter { // nolint: golint + return &queryRouter{ routes: map[string]sdk.Querier{}, } } -// 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) { +// AddRoute adds a query path to the router with a given Querier. It will panic +// if a duplicate route is given. The route must be alphanumeric. +func (qrt *queryRouter) AddRoute(path string, q sdk.Querier) QueryRouter { + if !isAlphaNumeric(path) { panic("route expressions can only contain alphanumeric characters") } - if rtr.routes[r] != nil { - panic("route has already been initialized") + if qrt.routes[path] != nil { + panic(fmt.Sprintf("route %s has already been initialized", path)) } - rtr.routes[r] = q - return rtr + + qrt.routes[path] = q + return qrt } -// Returns the sdk.Querier for a certain route path -func (rtr *queryrouter) Route(path string) (h sdk.Querier) { - return rtr.routes[path] +// Route returns the Querier for a given query route path. +func (qrt *queryRouter) Route(path string) sdk.Querier { + return qrt.routes[path] } diff --git a/baseapp/queryrouter_test.go b/baseapp/queryrouter_test.go new file mode 100644 index 000000000..774302831 --- /dev/null +++ b/baseapp/queryrouter_test.go @@ -0,0 +1,33 @@ +package baseapp + +import ( + "testing" + + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var testQuerier = func(_ sdk.Context, _ []string, _ abci.RequestQuery) (res []byte, err sdk.Error) { + return nil, nil +} + +func TestQueryRouter(t *testing.T) { + qr := NewQueryRouter() + + // require panic on invalid route + require.Panics(t, func() { + qr.AddRoute("*", testQuerier) + }) + + qr.AddRoute("testRoute", testQuerier) + q := qr.Route("testRoute") + require.NotNil(t, q) + + // require panic on duplicate route + require.Panics(t, func() { + qr.AddRoute("testRoute", testQuerier) + }) +} diff --git a/baseapp/router.go b/baseapp/router.go index 4be3aec74..5e829d73c 100644 --- a/baseapp/router.go +++ b/baseapp/router.go @@ -1,7 +1,7 @@ package baseapp import ( - "regexp" + "fmt" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -12,44 +12,36 @@ type Router interface { Route(path string) (h sdk.Handler) } -// map a transaction type to a handler and an initgenesis function -type route struct { - r string - h sdk.Handler -} - type router struct { - routes []route + routes map[string]sdk.Handler } -// nolint -// NewRouter - create new router -// TODO either make Function unexported or make return type (router) Exported -func NewRouter() *router { +// NewRouter returns a reference to a new router. +// +// TODO: Either make the function private or make return type (router) public. +func NewRouter() *router { // nolint: golint return &router{ - routes: make([]route, 0), + routes: make(map[string]sdk.Handler), } } -var isAlphaNumeric = regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString - -// AddRoute - TODO add description -func (rtr *router) AddRoute(r string, h sdk.Handler) Router { - if !isAlphaNumeric(r) { +// AddRoute adds a route path to the router with a given handler. The route must +// be alphanumeric. +func (rtr *router) AddRoute(path string, h sdk.Handler) Router { + if !isAlphaNumeric(path) { panic("route expressions can only contain alphanumeric characters") } - rtr.routes = append(rtr.routes, route{r, h}) + if rtr.routes[path] != nil { + panic(fmt.Sprintf("route %s has already been initialized", path)) + } + rtr.routes[path] = h return rtr } -// Route - TODO add description -// TODO handle expressive matches. -func (rtr *router) Route(path string) (h sdk.Handler) { - for _, route := range rtr.routes { - if route.r == path { - return route.h - } - } - return nil +// Route returns a handler for a given route path. +// +// TODO: Handle expressive matches. +func (rtr *router) Route(path string) sdk.Handler { + return rtr.routes[path] } diff --git a/baseapp/router_test.go b/baseapp/router_test.go new file mode 100644 index 000000000..86e09cf21 --- /dev/null +++ b/baseapp/router_test.go @@ -0,0 +1,31 @@ +package baseapp + +import ( + "testing" + + "github.com/stretchr/testify/require" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var testHandler = func(_ sdk.Context, _ sdk.Msg) sdk.Result { + return sdk.Result{} +} + +func TestRouter(t *testing.T) { + rtr := NewRouter() + + // require panic on invalid route + require.Panics(t, func() { + rtr.AddRoute("*", testHandler) + }) + + rtr.AddRoute("testRoute", testHandler) + h := rtr.Route("testRoute") + require.NotNil(t, h) + + // require panic on duplicate route + require.Panics(t, func() { + rtr.AddRoute("testRoute", testHandler) + }) +} diff --git a/baseapp/testdata/genesis.json b/baseapp/testdata/genesis.json deleted file mode 100644 index ee8879fd2..000000000 --- a/baseapp/testdata/genesis.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "chain_id": "foo_bar_chain", - "app_options": { - "accounts": [{ - "pub_key": { - "type": "ed25519", - "data": "6880db93598e283a67c4d88fc67a8858aa2de70f713fe94a5109e29c137100c2" - }, - "coins": [ - { - "denom": "blank", - "amount": 12345 - }, - { - "denom": "ETH", - "amount": 654321 - } - ] - }], - "plugin_options": ["plugin1/key1", "value1", "plugin1/key2", "value2"] - } -} diff --git a/baseapp/testdata/genesis2.json b/baseapp/testdata/genesis2.json deleted file mode 100644 index 18ec87164..000000000 --- a/baseapp/testdata/genesis2.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "chain_id": "addr_accounts_chain", - "app_options": { - "accounts": [{ - "name": "alice", - "pub_key": { - "type": "ed25519", - "data": "DBD9A46C45868F0A37C92B53113C09B048FBD87B5FBC2F8B199052973B8FAA36" - }, - "coins": [ - { - "denom": "one", - "amount": 111 - } - ] - }, { - "name": "bob", - "address": "C471FB670E44D219EE6DF2FC284BE38793ACBCE1", - "coins": [ - { - "denom": "two", - "amount": 222 - } - ] - }, { - "name": "sam", - "pub_key": { - "type": "secp256k1", - "data": "02AA8342F63CCCCE6DDB128525BA048CE0B2993DA3B4308746E1F216361A87651E" - }, - "coins": [ - { - "denom": "four", - "amount": 444 - } - ] - }] - } -} diff --git a/baseapp/testdata/genesis2b.json b/baseapp/testdata/genesis2b.json deleted file mode 100644 index a880b3c64..000000000 --- a/baseapp/testdata/genesis2b.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "chain_id": "addr_accounts_chain", - "app_options": { - "accounts": [{ - "name": "alice", - "pub_key": { - "type": "ed25519", - "data": "DBD9A46C45868F0A37C92B53113C09B048FBD87B5FBC2F8B199052973B8FAA36" - }, - "coins": [ - { - "denom": "one", - "amount": 111 - } - ] - }, { - "name": "bob", - "address": "C471FB670E44D219EE6DF2FC284BE38793ACBCE1", - "coins": [ - { - "denom": "two", - "amount": 222 - } - ] - }, { - "name": "carl", - "address": "1234ABCDD18E8EFE3FFC4B0506BF9BF8E5B0D9E9", - "pub_key": { - "type": "ed25519", - "data": "177C0AC45E86257F0708DC085D592AB22AAEECD1D26381B757F7C96135921858" - }, - "coins": [ - { - "denom": "three", - "amount": 333 - } - ] - }, { - "name": "sam", - "pub_key": { - "type": "secp256k1", - "data": "02AA8342F63CCCCE6DDB128525BA048CE0B2993DA3B4308746E1F216361A87651E" - }, - "coins": [ - { - "denom": "four", - "amount": 444 - } - ] - }] - } -} diff --git a/baseapp/testdata/genesis3.json b/baseapp/testdata/genesis3.json deleted file mode 100644 index b58b1d740..000000000 --- a/baseapp/testdata/genesis3.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "chain_id": "foo_bar_chain" -} From 7173164f73519ce68a41135195cb3bd04e0ef6c8 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 5 Feb 2019 19:20:07 -0800 Subject: [PATCH 60/99] Merge PR #3504: Fix validator address encoding in tm validator set command --- PENDING.md | 2 + client/lcd/lcd_test.go | 2 +- client/rpc/validators.go | 108 +++++++++++++++++++++-------------- cmd/gaia/cmd/gaiacli/main.go | 2 +- 4 files changed, 69 insertions(+), 45 deletions(-) diff --git a/PENDING.md b/PENDING.md index 8aa9ca3c5..6925c8bcf 100644 --- a/PENDING.md +++ b/PENDING.md @@ -15,6 +15,8 @@ BREAKING CHANGES - `--insecure` flag is removed. - `--tls` is now used to enable secure layer. - [\#3451](https://github.com/cosmos/cosmos-sdk/pull/3451) `gaiacli` now returns transactions in plain text including tags. + * [\#3501](https://github.com/cosmos/cosmos-sdk/issues/3501) Change validator + address Bech32 encoding to consensus address in `tendermint-validator-set`. * Gaia * [\#3457](https://github.com/cosmos/cosmos-sdk/issues/3457) Changed governance tally validatorGovInfo to use sdk.Int power instead of sdk.Dec diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 25860f79e..b0458ba39 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -198,7 +198,7 @@ func TestValidators(t *testing.T) { cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() resultVals := getValidatorSets(t, port, -1, false) - require.Contains(t, resultVals.Validators[0].Address.String(), "cosmosvaloper") + require.Contains(t, resultVals.Validators[0].Address.String(), "cosmosvalcons") require.Contains(t, resultVals.Validators[0].PubKey, "cosmosvalconspub") getValidatorSets(t, port, 2, false) getValidatorSets(t, port, 10000000, true) diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 379b3bc6f..091ac97e8 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -5,6 +5,9 @@ import ( "fmt" "net/http" "strconv" + "strings" + + "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/client/rest" @@ -22,26 +25,53 @@ import ( // TODO these next two functions feel kinda hacky based on their placement //ValidatorCommand returns the validator set for a given height -func ValidatorCommand() *cobra.Command { +func ValidatorCommand(cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ Use: "tendermint-validator-set [height]", Short: "Get the full tendermint validator set at given height", Args: cobra.MaximumNArgs(1), - RunE: printValidators, + RunE: func(cmd *cobra.Command, args []string) error { + var height *int64 + + // optional height + if len(args) > 0 { + h, err := strconv.Atoi(args[0]) + if err != nil { + return err + } + if h > 0 { + tmp := int64(h) + height = &tmp + } + } + + cliCtx := context.NewCLIContext().WithCodec(cdc) + + result, err := getValidators(cliCtx, height) + if err != nil { + return err + } + + return cliCtx.PrintOutput(result) + }, } + cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) + cmd.Flags().Bool(client.FlagIndentResponse, false, "indent JSON response") + viper.BindPFlag(client.FlagIndentResponse, cmd.Flags().Lookup(client.FlagIndentResponse)) + return cmd } // Validator output in bech32 format type ValidatorOutput struct { - Address sdk.ValAddress `json:"address"` // in bech32 - PubKey string `json:"pub_key"` // in bech32 - ProposerPriority int64 `json:"proposer_priority"` - VotingPower int64 `json:"voting_power"` + Address sdk.ConsAddress `json:"address"` + PubKey string `json:"pub_key"` + ProposerPriority int64 `json:"proposer_priority"` + VotingPower int64 `json:"voting_power"` } // Validators at a certain height output in bech32 format @@ -50,6 +80,27 @@ type ResultValidatorsOutput struct { Validators []ValidatorOutput `json:"validators"` } +func (rvo ResultValidatorsOutput) String() string { + var b strings.Builder + + b.WriteString(fmt.Sprintf("block height: %d\n", rvo.BlockHeight)) + + for _, val := range rvo.Validators { + b.WriteString( + fmt.Sprintf(` + Address: %s + Pubkey: %s + ProposerPriority: %d + VotingPower: %d + `, + val.Address, val.PubKey, val.ProposerPriority, val.VotingPower, + ), + ) + } + + return b.String() +} + func bech32ValidatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error) { bechValPubkey, err := sdk.Bech32ifyConsPub(validator.PubKey) if err != nil { @@ -57,33 +108,33 @@ func bech32ValidatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error } return ValidatorOutput{ - Address: sdk.ValAddress(validator.Address), + Address: sdk.ConsAddress(validator.Address), PubKey: bechValPubkey, ProposerPriority: validator.ProposerPriority, VotingPower: validator.VotingPower, }, nil } -func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) { +func getValidators(cliCtx context.CLIContext, height *int64) (ResultValidatorsOutput, error) { // get the node node, err := cliCtx.GetNode() if err != nil { - return nil, err + return ResultValidatorsOutput{}, err } validatorsRes, err := node.Validators(height) if err != nil { - return nil, err + return ResultValidatorsOutput{}, err } if !cliCtx.TrustNode { check, err := cliCtx.Verify(validatorsRes.BlockHeight) if err != nil { - return nil, err + return ResultValidatorsOutput{}, err } if !bytes.Equal(check.ValidatorsHash, tmtypes.NewValidatorSet(validatorsRes.Validators).Hash()) { - return nil, fmt.Errorf("got invalid validatorset") + return ResultValidatorsOutput{}, fmt.Errorf("received invalid validatorset") } } @@ -95,40 +146,11 @@ func getValidators(cliCtx context.CLIContext, height *int64) ([]byte, error) { for i := 0; i < len(validatorsRes.Validators); i++ { outputValidatorsRes.Validators[i], err = bech32ValidatorOutput(validatorsRes.Validators[i]) if err != nil { - return nil, err + return ResultValidatorsOutput{}, err } } - if cliCtx.Indent { - return cdc.MarshalJSONIndent(outputValidatorsRes, "", " ") - } - return cdc.MarshalJSON(outputValidatorsRes) - -} - -// CMD - -func printValidators(cmd *cobra.Command, args []string) error { - var height *int64 - // optional height - if len(args) > 0 { - h, err := strconv.Atoi(args[0]) - if err != nil { - return err - } - if h > 0 { - tmp := int64(h) - height = &tmp - } - } - - output, err := getValidators(context.NewCLIContext(), height) - if err != nil { - return err - } - - fmt.Println(string(output)) - return nil + return outputValidatorsRes, nil } // REST diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index 3065ee64c..f9cb85173 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -115,7 +115,7 @@ func queryCmd(cdc *amino.Codec, mc []sdk.ModuleClients) *cobra.Command { } queryCmd.AddCommand( - rpc.ValidatorCommand(), + rpc.ValidatorCommand(cdc), rpc.BlockCommand(), tx.SearchTxCmd(cdc), tx.QueryTxCmd(cdc), From df94f522f99fc62329c90f63f6b71817ad957af3 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Tue, 5 Feb 2019 20:57:36 -0800 Subject: [PATCH 61/99] Merge PR #3498: various improvements to gaiad gentx/init --- PENDING.md | 2 ++ cmd/gaia/cli_test/test_helpers.go | 2 +- cmd/gaia/init/gentx.go | 17 +++++++++++++---- cmd/gaia/init/init.go | 11 ++++------- cmd/gaia/init/init_test.go | 13 +++---------- docs/gaia/deploy-testnet.md | 2 +- docs/gaia/join-testnet.md | 4 ++-- 7 files changed, 26 insertions(+), 25 deletions(-) diff --git a/PENDING.md b/PENDING.md index 6925c8bcf..41695c068 100644 --- a/PENDING.md +++ b/PENDING.md @@ -15,6 +15,7 @@ BREAKING CHANGES - `--insecure` flag is removed. - `--tls` is now used to enable secure layer. - [\#3451](https://github.com/cosmos/cosmos-sdk/pull/3451) `gaiacli` now returns transactions in plain text including tags. + - [\#3497](https://github.com/cosmos/cosmos-sdk/issues/3497) `gaiad init` now takes moniker as required arguments, not as parameter. * [\#3501](https://github.com/cosmos/cosmos-sdk/issues/3501) Change validator address Bech32 encoding to consensus address in `tendermint-validator-set`. @@ -63,6 +64,7 @@ IMPROVEMENTS * Gaia CLI (`gaiacli`) * [\#3476](https://github.com/cosmos/cosmos-sdk/issues/3476) New `withdraw-all-rewards` command to withdraw all delegations rewards for delegators. + - [\#3497](https://github.com/cosmos/cosmos-sdk/issues/3497) `gaiad gentx` supports `--ip` and `--node-id` flags to override defaults. * Gaia * [\#3418](https://github.com/cosmos/cosmos-sdk/issues/3418) Add vesting account diff --git a/cmd/gaia/cli_test/test_helpers.go b/cmd/gaia/cli_test/test_helpers.go index 5a206a997..b54fc523a 100644 --- a/cmd/gaia/cli_test/test_helpers.go +++ b/cmd/gaia/cli_test/test_helpers.go @@ -153,7 +153,7 @@ func (f *Fixtures) UnsafeResetAll(flags ...string) { // GDInit is gaiad init // NOTE: GDInit sets the ChainID for the Fixtures instance func (f *Fixtures) GDInit(moniker string, flags ...string) { - cmd := fmt.Sprintf("gaiad init -o --moniker=%s --home=%s", moniker, f.GDHome) + cmd := fmt.Sprintf("gaiad init -o --home=%s %s", f.GDHome, moniker) _, stderr := tests.ExecuteT(f.T, addFlags(cmd, flags), app.DefaultKeyPass) var chainID string diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index ec3ae3573..6ce761965 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -62,10 +62,15 @@ following delegation and commission default parameters: return err } - ip, err := server.ExternalIP() - if err != nil { - fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP, "+ - "consequently the tx's memo field will be unset: %s", err) + // Read --nodeID, if empty take it from priv_validator.json + if nodeIDString := viper.GetString(cli.FlagNodeID); nodeIDString != "" { + nodeID = nodeIDString + } + + ip := viper.GetString(cli.FlagIP) + if ip == "" { + fmt.Fprintf(os.Stderr, "couldn't retrieve an external IP; "+ + "the tx's memo field will be unset") } genDoc, err := LoadGenesisDoc(cdc, config.GenesisFile()) @@ -156,11 +161,15 @@ following delegation and commission default parameters: }, } + ip, _ := server.ExternalIP() + cmd.Flags().String(tmcli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().String(flagClientHome, app.DefaultCLIHome, "client's home directory") cmd.Flags().String(client.FlagName, "", "name of private key with which to sign the gentx") cmd.Flags().String(client.FlagOutputDocument, "", "write the genesis transaction JSON document to the given file instead of the default location") + cmd.Flags().String(cli.FlagIP, ip, "The node's public IP") + cmd.Flags().String(cli.FlagNodeID, "", "The node's NodeID") cmd.Flags().AddFlagSet(cli.FsCommissionCreate) cmd.Flags().AddFlagSet(cli.FsAmount) cmd.Flags().AddFlagSet(cli.FsPk) diff --git a/cmd/gaia/init/init.go b/cmd/gaia/init/init.go index 922cfbafa..a2af2a1c2 100644 --- a/cmd/gaia/init/init.go +++ b/cmd/gaia/init/init.go @@ -21,7 +21,6 @@ import ( const ( flagOverwrite = "overwrite" flagClientHome = "home-client" - flagMoniker = "moniker" ) type printInfo struct { @@ -46,11 +45,11 @@ func displayInfo(cdc *codec.Codec, info printInfo) error { // nolint func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ - Use: "init", + Use: "init [moniker]", Short: "Initialize private validator, p2p, genesis, and application configuration files", Long: `Initialize validators's and node's configuration files.`, - Args: cobra.NoArgs, - RunE: func(_ *cobra.Command, _ []string) error { + Args: cobra.ExactArgs(1), + RunE: func(_ *cobra.Command, args []string) error { config := ctx.Config config.SetRoot(viper.GetString(cli.HomeFlag)) @@ -64,7 +63,7 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { return err } - config.Moniker = viper.GetString(flagMoniker) + config.Moniker = args[0] var appState json.RawMessage genFile := config.GenesisFile() @@ -89,8 +88,6 @@ func InitCmd(ctx *server.Context, cdc *codec.Codec) *cobra.Command { cmd.Flags().String(cli.HomeFlag, app.DefaultNodeHome, "node's home directory") cmd.Flags().BoolP(flagOverwrite, "o", false, "overwrite the genesis.json file") cmd.Flags().String(client.FlagChainID, "", "genesis file chain-id, if left blank will be randomly created") - cmd.Flags().String(flagMoniker, "", "set the validator's moniker") - cmd.MarkFlagRequired(flagMoniker) return cmd } diff --git a/cmd/gaia/init/init_test.go b/cmd/gaia/init/init_test.go index faf324e6c..e10347334 100644 --- a/cmd/gaia/init/init_test.go +++ b/cmd/gaia/init/init_test.go @@ -36,10 +36,7 @@ func TestInitCmd(t *testing.T) { cdc := app.MakeCodec() cmd := InitCmd(ctx, cdc) - viper.Set(flagMoniker, "gaianode-test") - - err = cmd.RunE(nil, nil) - require.NoError(t, err) + require.NoError(t, cmd.RunE(nil, []string{"gaianode-test"})) } func setupClientHome(t *testing.T) func() { @@ -64,11 +61,9 @@ func TestEmptyState(t *testing.T) { ctx := server.NewContext(cfg, logger) cdc := app.MakeCodec() - viper.Set(flagMoniker, "gaianode-test") cmd := InitCmd(ctx, cdc) - err = cmd.RunE(nil, nil) - require.NoError(t, err) + require.NoError(t, cmd.RunE(nil, []string{"gaianode-test"})) old := os.Stdout r, w, _ := os.Pipe() @@ -103,7 +98,6 @@ func TestStartStandAlone(t *testing.T) { os.RemoveAll(home) }() viper.Set(cli.HomeFlag, home) - viper.Set(client.FlagName, "moniker") defer setupClientHome(t)() logger := log.NewNopLogger() @@ -112,8 +106,7 @@ func TestStartStandAlone(t *testing.T) { ctx := server.NewContext(cfg, logger) cdc := app.MakeCodec() initCmd := InitCmd(ctx, cdc) - err = initCmd.RunE(nil, nil) - require.NoError(t, err) + require.NoError(t, initCmd.RunE(nil, []string{"gaianode-test"})) app, err := mock.NewApp(home, logger) require.Nil(t, err) diff --git a/docs/gaia/deploy-testnet.md b/docs/gaia/deploy-testnet.md index c617e2052..a91c6d09c 100644 --- a/docs/gaia/deploy-testnet.md +++ b/docs/gaia/deploy-testnet.md @@ -26,7 +26,7 @@ This guide helps you create a single validator node that runs a network locally cd $HOME # Initialize the genesis.json file that will help you to bootstrap the network -gaiad init --chain-id testing --moniker testing +gaiad init --chain-id=testing testing # Create a key to hold your validator account gaiacli keys add validator diff --git a/docs/gaia/join-testnet.md b/docs/gaia/join-testnet.md index 91b3afef6..df47f88bb 100644 --- a/docs/gaia/join-testnet.md +++ b/docs/gaia/join-testnet.md @@ -19,11 +19,11 @@ These instructions are for setting up a brand new full node from scratch. First, initialize the node and create the necessary config files: ```bash -gaiad init --moniker +gaiad init ``` ::: warning Note -Only ASCII characters are supported for the `--moniker`. Using Unicode characters will render your node unreachable. +Monikers can contain only ASCII characters. Using Unicode characters will render your node unreachable. ::: You can edit this `moniker` later, in the `~/.gaiad/config/config.toml` file: From 52f2ec71a96d7e441dff6dd8d6f74e61a864d16f Mon Sep 17 00:00:00 2001 From: frog power 4000 Date: Tue, 5 Feb 2019 21:30:48 -0800 Subject: [PATCH 62/99] Merge PR #3400: power reduction for Tendermint * add uncompiled power functionality * fix some compile errors * Power -> TendermintPower * tests rename GetTendermintPower * test fix * working * fix delegation tests * fix slash tests * staking/keeper tests passing * docs reversion * debuggin workin * x/staking test pass * fix gov tests * fix x/slashing tests * working distribution test fixes * fix distribution tests * lint * fix lcd tests * fix gov test * lint * CLI fixes, rm stakingTypes * typos * working cli fixes * cli test fix * cli tests fixed * testnet creation modification * typo * pending * Sanitize Dec.Roundint64 (#3475) * merge fixes * @cwgoes comments * fix tests * change power reduction to 10^-6 * option to turn off minting for LCD tests --- PENDING.md | 2 + client/lcd/lcd_test.go | 166 ++++++------ client/lcd/test_helpers.go | 102 +++---- cmd/gaia/app/genesis.go | 6 +- cmd/gaia/app/genesis_test.go | 9 +- cmd/gaia/app/sim_test.go | 9 +- cmd/gaia/cli_test/cli_test.go | 128 +++++---- cmd/gaia/cli_test/test_helpers.go | 8 +- cmd/gaia/init/gentx.go | 7 +- cmd/gaia/init/testnet.go | 9 +- types/decimal.go | 16 ++ types/stake.go | 5 +- x/auth/ante.go | 2 +- x/auth/client/txbuilder/txbuilder.go | 2 +- x/distribution/keeper/delegation_test.go | 98 ++++--- x/distribution/keeper/keeper_test.go | 18 +- x/distribution/keeper/test_common.go | 12 +- x/gov/endblocker_test.go | 17 +- x/gov/genesis.go | 5 +- x/gov/keeper_test.go | 11 +- x/gov/msgs_test.go | 6 +- x/gov/proposals.go | 18 ++ x/gov/simulation/msgs.go | 7 +- x/gov/tally.go | 40 +-- x/gov/tally_test.go | 39 +-- x/gov/test_common.go | 14 +- x/mint/params.go | 5 +- x/mock/app.go | 22 +- x/mock/test_utils.go | 8 +- x/slashing/app_test.go | 14 +- x/slashing/handler_test.go | 22 +- x/slashing/keeper.go | 5 +- x/slashing/keeper_test.go | 110 ++++---- x/slashing/params.go | 11 +- x/slashing/signing_info.go | 24 +- x/slashing/test_common.go | 7 +- x/slashing/tick_test.go | 7 +- x/staking/app_test.go | 20 +- x/staking/genesis.go | 6 +- x/staking/genesis_test.go | 31 ++- x/staking/handler_test.go | 286 +++++++++++--------- x/staking/keeper/delegation_test.go | 329 ++++++++++------------- x/staking/keeper/keeper.go | 8 +- x/staking/keeper/slash.go | 3 +- x/staking/keeper/slash_test.go | 97 ++++--- x/staking/keeper/test_common.go | 9 +- x/staking/keeper/val_state_change.go | 8 +- x/staking/keeper/validator.go | 11 +- x/staking/keeper/validator_test.go | 194 +++++++------ x/staking/querier/querier_test.go | 16 +- x/staking/simulation/invariants.go | 14 +- x/staking/staking.go | 5 + x/staking/test_common.go | 18 +- x/staking/types/commission.go | 30 +-- x/staking/types/genesis.go | 2 +- x/staking/types/validator.go | 36 ++- 56 files changed, 1159 insertions(+), 955 deletions(-) diff --git a/PENDING.md b/PENDING.md index 41695c068..334878e7b 100644 --- a/PENDING.md +++ b/PENDING.md @@ -23,6 +23,7 @@ BREAKING CHANGES * [\#3457](https://github.com/cosmos/cosmos-sdk/issues/3457) Changed governance tally validatorGovInfo to use sdk.Int power instead of sdk.Dec * SDK + * \#2513 Tendermint updates are adjusted by 10^-6 relative to staking tokens, * [\#3487](https://github.com/cosmos/cosmos-sdk/pull/3487) Move HTTP/REST utilities out of client/utils into a new dedicated client/rest package. * [\#3490](https://github.com/cosmos/cosmos-sdk/issues/3490) ReadRESTReq() returns bool to avoid callers to write error responses twice. * [\#3502](https://github.com/cosmos/cosmos-sdk/pull/3502) Fixes issue when comparing genesis states @@ -79,6 +80,7 @@ IMPROVEMENTS * [\#2605] x/params add subkey accessing * [\#2986](https://github.com/cosmos/cosmos-sdk/pull/2986) Store Refactor * \#3435 Test that store implementations do not allow nil values + * \#2509 Sanitize all usage of Dec.RoundInt64() * [\#556](https://github.com/cosmos/cosmos-sdk/issues/556) Increase `BaseApp` test coverage. diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index b0458ba39..0e7b860a2 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -27,7 +27,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) const ( @@ -39,7 +38,7 @@ const ( altPw = "12345678901" ) -var fees = sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 5)} +var fees = sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, 5)} func init() { mintkey.BcryptSecurityParameter = 1 @@ -48,7 +47,7 @@ func init() { func TestSeedsAreDifferent(t *testing.T) { addr, _ := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) defer cleanup() mnemonic1 := getKeysSeed(t, port) @@ -58,7 +57,7 @@ func TestSeedsAreDifferent(t *testing.T) { } func TestKeyRecover(t *testing.T) { - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true) defer cleanup() myName1 := "TestKeyRecover_1" @@ -79,7 +78,7 @@ func TestKeyRecover(t *testing.T) { } func TestKeyRecoverHDPath(t *testing.T) { - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true) defer cleanup() mnemonic := getKeysSeed(t, port) @@ -108,7 +107,7 @@ func TestKeys(t *testing.T) { addr1, _ := CreateAddr(t, name1, pw, GetKeyBase(t)) addr1Bech32 := addr1.String() - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr1}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr1}, true) defer cleanup() // get new seed & recover key @@ -157,7 +156,7 @@ func TestVersion(t *testing.T) { t.SkipNow() } - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true) defer cleanup() // node info @@ -180,14 +179,14 @@ func TestVersion(t *testing.T) { } func TestNodeStatus(t *testing.T) { - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true) defer cleanup() getNodeInfo(t, port) getSyncStatus(t, port, false) } func TestBlock(t *testing.T) { - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true) defer cleanup() getBlock(t, port, -1, false) getBlock(t, port, 2, false) @@ -195,7 +194,7 @@ func TestBlock(t *testing.T) { } func TestValidators(t *testing.T) { - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true) defer cleanup() resultVals := getValidatorSets(t, port, -1, false) require.Contains(t, resultVals.Validators[0].Address.String(), "cosmosvalcons") @@ -206,7 +205,7 @@ func TestValidators(t *testing.T) { func TestCoinSend(t *testing.T) { addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) defer cleanup() bz, err := hex.DecodeString("8FA6AB57AD6870F6B5B2E57735F38F2F30E73CB6") @@ -232,14 +231,14 @@ func TestCoinSend(t *testing.T) { coins := acc.GetCoins() expectedBalance := initialBalance[0].Minus(fees[0]) - require.Equal(t, stakingTypes.DefaultBondDenom, coins[0].Denom) + require.Equal(t, staking.DefaultBondDenom, coins[0].Denom) require.Equal(t, expectedBalance.Amount.SubRaw(1), coins[0].Amount) expectedBalance = coins[0] // query receiver acc2 := getAccount(t, port, receiveAddr) coins2 := acc2.GetCoins() - require.Equal(t, stakingTypes.DefaultBondDenom, coins2[0].Denom) + require.Equal(t, staking.DefaultBondDenom, coins2[0].Denom) require.Equal(t, int64(1), coins2[0].Amount.Int64()) // test failure with too little gas @@ -275,7 +274,7 @@ func TestCoinSend(t *testing.T) { require.NotZero(t, gasEstResp.GasEstimate) acc = getAccount(t, port, addr) - require.Equal(t, expectedBalance.Amount, acc.GetCoins().AmountOf(stakingTypes.DefaultBondDenom)) + require.Equal(t, expectedBalance.Amount, acc.GetCoins().AmountOf(staking.DefaultBondDenom)) // run successful tx gas := fmt.Sprintf("%d", gasEstResp.GasEstimate) @@ -290,12 +289,12 @@ func TestCoinSend(t *testing.T) { acc = getAccount(t, port, addr) expectedBalance = expectedBalance.Minus(fees[0]) - require.Equal(t, expectedBalance.Amount.SubRaw(1), acc.GetCoins().AmountOf(stakingTypes.DefaultBondDenom)) + require.Equal(t, expectedBalance.Amount.SubRaw(1), acc.GetCoins().AmountOf(staking.DefaultBondDenom)) } func TestCoinSendAccAuto(t *testing.T) { addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) defer cleanup() acc := getAccount(t, port, addr) @@ -310,13 +309,13 @@ func TestCoinSendAccAuto(t *testing.T) { coins := acc.GetCoins() expectedBalance := initialBalance[0].Minus(fees[0]) - require.Equal(t, stakingTypes.DefaultBondDenom, coins[0].Denom) + require.Equal(t, staking.DefaultBondDenom, coins[0].Denom) require.Equal(t, expectedBalance.Amount.SubRaw(1), coins[0].Amount) } func TestCoinMultiSendGenerateOnly(t *testing.T) { addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) defer cleanup() // generate only @@ -337,7 +336,7 @@ func TestCoinMultiSendGenerateOnly(t *testing.T) { func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) defer cleanup() acc := getAccount(t, port, addr) @@ -409,7 +408,7 @@ func TestCoinSendGenerateSignAndBroadcast(t *testing.T) { func TestTxs(t *testing.T) { addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) defer cleanup() var emptyTxs []sdk.TxResponse @@ -448,7 +447,7 @@ func TestTxs(t *testing.T) { func TestPoolParamsQuery(t *testing.T) { addr, _ := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) defer cleanup() defaultParams := staking.DefaultParams() @@ -459,16 +458,21 @@ func TestPoolParamsQuery(t *testing.T) { pool := getStakingPool(t, port) initialPool := staking.InitialPool() - initialPool.NotBondedTokens = initialPool.NotBondedTokens.Add(sdk.NewInt(100)) - initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewInt(100)) // Delegate tx on GaiaAppGenState - initialPool.NotBondedTokens = initialPool.NotBondedTokens.Add(sdk.NewInt(50)) // freeFermionsAcc = 50 on GaiaAppGenState + tokens := staking.TokensFromTendermintPower(100) + freeFermions := staking.TokensFromTendermintPower(50) + initialPool.NotBondedTokens = initialPool.NotBondedTokens.Add(tokens) + initialPool.BondedTokens = initialPool.BondedTokens.Add(tokens) // Delegate tx on GaiaAppGenState + initialPool.NotBondedTokens = initialPool.NotBondedTokens.Add(freeFermions) // freeFermionsAcc = 50 on GaiaAppGenState require.Equal(t, initialPool.BondedTokens, pool.BondedTokens) - require.Equal(t, initialPool.NotBondedTokens, pool.NotBondedTokens) + + //TODO include this test once REST for distribution is online, need to include distribution tokens from inflation + // for this equality to make sense + //require.Equal(t, initialPool.NotBondedTokens, pool.NotBondedTokens) } func TestValidatorsQuery(t *testing.T) { - cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true) defer cleanup() require.Equal(t, 1, len(valPubKeys)) @@ -488,7 +492,7 @@ func TestValidatorsQuery(t *testing.T) { } func TestValidatorQuery(t *testing.T) { - cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true) defer cleanup() require.Equal(t, 1, len(valPubKeys)) require.Equal(t, 1, len(operAddrs)) @@ -500,20 +504,22 @@ func TestValidatorQuery(t *testing.T) { func TestBonding(t *testing.T) { addr, _ := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 2, []sdk.AccAddress{addr}) + cleanup, valPubKeys, operAddrs, port := InitializeTestLCD(t, 2, []sdk.AccAddress{addr}, true) defer cleanup() require.Equal(t, 2, len(valPubKeys)) require.Equal(t, 2, len(operAddrs)) - amt := sdk.NewDec(60) + amt := staking.TokensFromTendermintPower(60) + amtDec := sdk.NewDecFromInt(amt) validator := getValidator(t, port, operAddrs[0]) acc := getAccount(t, port, addr) initialBalance := acc.GetCoins() // create bond TX - resultTx := doDelegate(t, port, name1, pw, addr, operAddrs[0], 60, fees) + delTokens := staking.TokensFromTendermintPower(60) + resultTx := doDelegate(t, port, name1, pw, addr, operAddrs[0], delTokens, fees) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.Code) @@ -530,16 +536,16 @@ func TestBonding(t *testing.T) { acc = getAccount(t, port, addr) coins := acc.GetCoins() expectedBalance := initialBalance[0].Minus(fees[0]) - require.Equal(t, expectedBalance.Amount.SubRaw(60), coins.AmountOf(stakingTypes.DefaultBondDenom)) + require.Equal(t, expectedBalance.Amount.Sub(delTokens), coins.AmountOf(staking.DefaultBondDenom)) expectedBalance = coins[0] // query delegation bond := getDelegation(t, port, addr, operAddrs[0]) - require.Equal(t, amt, bond.Shares) + require.Equal(t, amtDec, bond.Shares) delegatorDels := getDelegatorDelegations(t, port, addr) require.Len(t, delegatorDels, 1) - require.Equal(t, amt, delegatorDels[0].Shares) + require.Equal(t, amtDec, delegatorDels[0].Shares) // query all delegations to validator bonds := getValidatorDelegations(t, port, operAddrs[0]) @@ -548,13 +554,14 @@ func TestBonding(t *testing.T) { bondedValidators := getDelegatorValidators(t, port, addr) require.Len(t, bondedValidators, 1) require.Equal(t, operAddrs[0], bondedValidators[0].OperatorAddr) - require.Equal(t, validator.DelegatorShares.Add(amt).String(), bondedValidators[0].DelegatorShares.String()) + require.Equal(t, validator.DelegatorShares.Add(amtDec).String(), bondedValidators[0].DelegatorShares.String()) bondedValidator := getDelegatorValidator(t, port, addr, operAddrs[0]) require.Equal(t, operAddrs[0], bondedValidator.OperatorAddr) // testing unbonding - resultTx = doUndelegate(t, port, name1, pw, addr, operAddrs[0], 30, fees) + unbondingTokens := staking.TokensFromTendermintPower(30) + resultTx = doUndelegate(t, port, name1, pw, addr, operAddrs[0], unbondingTokens, fees) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.Code) @@ -564,8 +571,8 @@ func TestBonding(t *testing.T) { coins = acc.GetCoins() expectedBalance = expectedBalance.Minus(fees[0]) require.True(t, - expectedBalance.Amount.LT(coins.AmountOf(stakingTypes.DefaultBondDenom)) || - expectedBalance.Amount.Equal(coins.AmountOf(stakingTypes.DefaultBondDenom)), + expectedBalance.Amount.LT(coins.AmountOf(staking.DefaultBondDenom)) || + expectedBalance.Amount.Equal(coins.AmountOf(staking.DefaultBondDenom)), "should get tokens back from automatic withdrawal after an unbonding delegation", ) expectedBalance = coins[0] @@ -580,10 +587,11 @@ func TestBonding(t *testing.T) { ubd := getUnbondingDelegation(t, port, addr, operAddrs[0]) require.Len(t, ubd.Entries, 1) - require.Equal(t, int64(30), ubd.Entries[0].Balance.Amount.Int64()) + require.Equal(t, delTokens.DivRaw(2), ubd.Entries[0].Balance.Amount) // test redelegation - resultTx = doBeginRedelegation(t, port, name1, pw, addr, operAddrs[0], operAddrs[1], 30, fees) + rdTokens := staking.TokensFromTendermintPower(30) + resultTx = doBeginRedelegation(t, port, name1, pw, addr, operAddrs[0], operAddrs[1], rdTokens, fees) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.Code) @@ -592,8 +600,8 @@ func TestBonding(t *testing.T) { acc = getAccount(t, port, addr) expectedBalance = expectedBalance.Minus(fees[0]) require.True(t, - expectedBalance.Amount.LT(coins.AmountOf(stakingTypes.DefaultBondDenom)) || - expectedBalance.Amount.Equal(coins.AmountOf(stakingTypes.DefaultBondDenom)), + expectedBalance.Amount.LT(coins.AmountOf(staking.DefaultBondDenom)) || + expectedBalance.Amount.Equal(coins.AmountOf(staking.DefaultBondDenom)), "should get tokens back from automatic withdrawal after an unbonding delegation", ) @@ -607,34 +615,35 @@ func TestBonding(t *testing.T) { require.Equal(t, resultTx.Height, txs[0].Height) // query delegations, unbondings and redelegations from validator and delegator + rdShares := sdk.NewDecFromInt(rdTokens) delegatorDels = getDelegatorDelegations(t, port, addr) require.Len(t, delegatorDels, 1) - require.Equal(t, "30.000000000000000000", delegatorDels[0].GetShares().String()) + require.Equal(t, rdShares, delegatorDels[0].GetShares()) redelegation := getRedelegations(t, port, addr, operAddrs[0], operAddrs[1]) require.Len(t, redelegation, 1) require.Len(t, redelegation[0].Entries, 1) - require.Equal(t, "30", redelegation[0].Entries[0].Balance.Amount.String()) + require.Equal(t, rdTokens, redelegation[0].Entries[0].Balance.Amount) delegatorUbds := getDelegatorUnbondingDelegations(t, port, addr) require.Len(t, delegatorUbds, 1) require.Len(t, delegatorUbds[0].Entries, 1) - require.Equal(t, "30", delegatorUbds[0].Entries[0].Balance.Amount.String()) + require.Equal(t, rdTokens, delegatorUbds[0].Entries[0].Balance.Amount) delegatorReds := getRedelegations(t, port, addr, nil, nil) require.Len(t, delegatorReds, 1) require.Len(t, delegatorReds[0].Entries, 1) - require.Equal(t, "30", delegatorReds[0].Entries[0].Balance.Amount.String()) + require.Equal(t, rdTokens, delegatorReds[0].Entries[0].Balance.Amount) validatorUbds := getValidatorUnbondingDelegations(t, port, operAddrs[0]) require.Len(t, validatorUbds, 1) require.Len(t, validatorUbds[0].Entries, 1) - require.Equal(t, "30", validatorUbds[0].Entries[0].Balance.Amount.String()) + require.Equal(t, rdTokens, validatorUbds[0].Entries[0].Balance.Amount) validatorReds := getRedelegations(t, port, nil, operAddrs[0], nil) require.Len(t, validatorReds, 1) require.Len(t, validatorReds[0].Entries, 1) - require.Equal(t, "30", validatorReds[0].Entries[0].Balance.Amount.String()) + require.Equal(t, rdTokens, validatorReds[0].Entries[0].Balance.Amount) // TODO Undonding status not currently implemented // require.Equal(t, sdk.Unbonding, bondedValidators[0].Status) @@ -655,14 +664,15 @@ func TestBonding(t *testing.T) { func TestSubmitProposal(t *testing.T) { addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) defer cleanup() acc := getAccount(t, port, addr) initialBalance := acc.GetCoins() // create SubmitProposal TX - resultTx := doSubmitProposal(t, port, seed, name1, pw, addr, 5, fees) + proposalTokens := staking.TokensFromTendermintPower(5) + resultTx := doSubmitProposal(t, port, seed, name1, pw, addr, proposalTokens, fees) tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed @@ -674,7 +684,7 @@ func TestSubmitProposal(t *testing.T) { // verify balance acc = getAccount(t, port, addr) expectedBalance := initialBalance[0].Minus(fees[0]) - require.Equal(t, expectedBalance.Amount.SubRaw(5), acc.GetCoins().AmountOf(stakingTypes.DefaultBondDenom)) + require.Equal(t, expectedBalance.Amount.Sub(proposalTokens), acc.GetCoins().AmountOf(staking.DefaultBondDenom)) // query proposal proposal := getProposal(t, port, proposalID) @@ -687,14 +697,15 @@ func TestSubmitProposal(t *testing.T) { func TestDeposit(t *testing.T) { addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) defer cleanup() acc := getAccount(t, port, addr) initialBalance := acc.GetCoins() // create SubmitProposal TX - resultTx := doSubmitProposal(t, port, seed, name1, pw, addr, 5, fees) + proposalTokens := staking.TokensFromTendermintPower(5) + resultTx := doSubmitProposal(t, port, seed, name1, pw, addr, proposalTokens, fees) tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed @@ -707,7 +718,7 @@ func TestDeposit(t *testing.T) { acc = getAccount(t, port, addr) coins := acc.GetCoins() expectedBalance := initialBalance[0].Minus(fees[0]) - require.Equal(t, expectedBalance.Amount.SubRaw(5), coins.AmountOf(stakingTypes.DefaultBondDenom)) + require.Equal(t, expectedBalance.Amount.Sub(proposalTokens), coins.AmountOf(staking.DefaultBondDenom)) expectedBalance = coins[0] // query proposal @@ -715,13 +726,14 @@ func TestDeposit(t *testing.T) { require.Equal(t, "Test", proposal.GetTitle()) // create SubmitProposal TX - resultTx = doDeposit(t, port, seed, name1, pw, addr, proposalID, 5, fees) + depositTokens := staking.TokensFromTendermintPower(5) + resultTx = doDeposit(t, port, seed, name1, pw, addr, proposalID, depositTokens, fees) tests.WaitForHeight(resultTx.Height+1, port) // verify balance after deposit and fee acc = getAccount(t, port, addr) expectedBalance = expectedBalance.Minus(fees[0]) - require.Equal(t, expectedBalance.Amount.SubRaw(5), acc.GetCoins().AmountOf(stakingTypes.DefaultBondDenom)) + require.Equal(t, expectedBalance.Amount.Sub(depositTokens), acc.GetCoins().AmountOf(staking.DefaultBondDenom)) // query tx txs := getTransactions(t, port, fmt.Sprintf("action=deposit&depositor=%s", addr)) @@ -729,24 +741,26 @@ func TestDeposit(t *testing.T) { require.Equal(t, resultTx.Height, txs[0].Height) // query proposal + totalCoins := sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, staking.TokensFromTendermintPower(10))} proposal = getProposal(t, port, proposalID) - require.True(t, proposal.GetTotalDeposit().IsEqual(sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 10)})) + require.True(t, proposal.GetTotalDeposit().IsEqual(totalCoins)) // query deposit deposit := getDeposit(t, port, proposalID, addr) - require.True(t, deposit.Amount.IsEqual(sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 10)})) + require.True(t, deposit.Amount.IsEqual(totalCoins)) } func TestVote(t *testing.T) { addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, _, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, operAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) defer cleanup() acc := getAccount(t, port, addr) initialBalance := acc.GetCoins() // create SubmitProposal TX - resultTx := doSubmitProposal(t, port, seed, name1, pw, addr, 10, fees) + proposalTokens := staking.TokensFromTendermintPower(10) + resultTx := doSubmitProposal(t, port, seed, name1, pw, addr, proposalTokens, fees) tests.WaitForHeight(resultTx.Height+1, port) // check if tx was committed @@ -759,7 +773,7 @@ func TestVote(t *testing.T) { acc = getAccount(t, port, addr) coins := acc.GetCoins() expectedBalance := initialBalance[0].Minus(fees[0]) - require.Equal(t, expectedBalance.Amount.SubRaw(10), coins.AmountOf(stakingTypes.DefaultBondDenom)) + require.Equal(t, expectedBalance.Amount.Sub(proposalTokens), coins.AmountOf(staking.DefaultBondDenom)) expectedBalance = coins[0] // query proposal @@ -775,7 +789,7 @@ func TestVote(t *testing.T) { acc = getAccount(t, port, addr) coins = acc.GetCoins() expectedBalance = expectedBalance.Minus(fees[0]) - require.Equal(t, expectedBalance.Amount, coins.AmountOf(stakingTypes.DefaultBondDenom)) + require.Equal(t, expectedBalance.Amount, coins.AmountOf(staking.DefaultBondDenom)) expectedBalance = coins[0] // query tx @@ -791,18 +805,19 @@ func TestVote(t *testing.T) { require.Equal(t, sdk.ZeroDec(), tally.Yes, "tally should be 0 as the address is not bonded") // create bond TX - resultTx = doDelegate(t, port, name1, pw, addr, operAddrs[0], 60, fees) + delTokens := staking.TokensFromTendermintPower(60) + resultTx = doDelegate(t, port, name1, pw, addr, operAddrs[0], delTokens, fees) tests.WaitForHeight(resultTx.Height+1, port) // verify balance acc = getAccount(t, port, addr) coins = acc.GetCoins() expectedBalance = expectedBalance.Minus(fees[0]) - require.Equal(t, expectedBalance.Amount.SubRaw(60), coins.AmountOf(stakingTypes.DefaultBondDenom)) + require.Equal(t, expectedBalance.Amount.Sub(delTokens), coins.AmountOf(staking.DefaultBondDenom)) expectedBalance = coins[0] tally = getTally(t, port, proposalID) - require.Equal(t, sdk.NewDec(60), tally.Yes, "tally should be equal to the amount delegated") + require.Equal(t, sdk.NewDecFromInt(delTokens), tally.Yes, "tally should be equal to the amount delegated") // change vote option resultTx = doVote(t, port, seed, name1, pw, addr, proposalID, "No", fees) @@ -811,16 +826,16 @@ func TestVote(t *testing.T) { // verify balance acc = getAccount(t, port, addr) expectedBalance = expectedBalance.Minus(fees[0]) - require.Equal(t, expectedBalance.Amount, acc.GetCoins().AmountOf(stakingTypes.DefaultBondDenom)) + require.Equal(t, expectedBalance.Amount, acc.GetCoins().AmountOf(staking.DefaultBondDenom)) tally = getTally(t, port, proposalID) require.Equal(t, sdk.ZeroDec(), tally.Yes, "tally should be 0 the user changed the option") - require.Equal(t, sdk.NewDec(60), tally.No, "tally should be equal to the amount delegated") + require.Equal(t, sdk.NewDecFromInt(delTokens), tally.No, "tally should be equal to the amount delegated") } func TestUnjail(t *testing.T) { addr, _ := CreateAddr(t, name1, pw, GetKeyBase(t)) - cleanup, valPubKeys, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, valPubKeys, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, true) defer cleanup() // XXX: any less than this and it fails @@ -836,11 +851,11 @@ func TestUnjail(t *testing.T) { func TestProposalsQuery(t *testing.T) { addrs, seeds, names, passwords := CreateAddrs(t, GetKeyBase(t), 2) - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addrs[0], addrs[1]}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addrs[0], addrs[1]}, true) defer cleanup() depositParam := getDepositParam(t, port) - halfMinDeposit := depositParam.MinDeposit.AmountOf(stakingTypes.DefaultBondDenom).Int64() / 2 + halfMinDeposit := depositParam.MinDeposit.AmountOf(staking.DefaultBondDenom).DivRaw(2) getVotingParam(t, port) getTallyingParam(t, port) @@ -887,7 +902,8 @@ func TestProposalsQuery(t *testing.T) { require.Equal(t, deposit, deposits[0]) // increasing the amount of the deposit should update the existing one - resultTx = doDeposit(t, port, seeds[0], names[0], passwords[0], addrs[0], proposalID1, 1, fees) + depositTokens := staking.TokensFromTendermintPower(1) + resultTx = doDeposit(t, port, seeds[0], names[0], passwords[0], addrs[0], proposalID1, depositTokens, fees) tests.WaitForHeight(resultTx.Height+1, port) deposits = getDeposits(t, port, proposalID1) @@ -955,7 +971,7 @@ func TestProposalsQuery(t *testing.T) { } func TestSlashingGetParams(t *testing.T) { - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true) defer cleanup() res, body := Request(t, port, "GET", "/slashing/parameters", nil) @@ -967,7 +983,7 @@ func TestSlashingGetParams(t *testing.T) { } func TestDistributionGetParams(t *testing.T) { - cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) + cleanup, _, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}, true) defer cleanup() res, body := Request(t, port, "GET", "/distribution/parameters", nil) @@ -977,8 +993,7 @@ func TestDistributionGetParams(t *testing.T) { func TestDistributionFlow(t *testing.T) { addr, seed := CreateAddr(t, name1, pw, GetKeyBase(t)) - //addr2, seed2 = CreateAddr(t, name2, pw, GetKeyBase(t)) - cleanup, _, valAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) + cleanup, _, valAddrs, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}, false) defer cleanup() valAddr := valAddrs[0] @@ -999,7 +1014,8 @@ func TestDistributionFlow(t *testing.T) { require.Equal(t, valDistInfo.SelfBondRewards, sdk.DecCoins(nil)) // Delegate some coins - resultTx := doDelegate(t, port, name1, pw, addr, valAddr, 60, fees) + delTokens := staking.TokensFromTendermintPower(60) + resultTx := doDelegate(t, port, name1, pw, addr, valAddr, delTokens, fees) tests.WaitForHeight(resultTx.Height+1, port) require.Equal(t, uint32(0), resultTx.Code) diff --git a/client/lcd/test_helpers.go b/client/lcd/test_helpers.go index 2c6223de3..588285233 100644 --- a/client/lcd/test_helpers.go +++ b/client/lcd/test_helpers.go @@ -15,6 +15,9 @@ import ( "strings" "testing" + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rest" @@ -23,26 +26,22 @@ import ( gapp "github.com/cosmos/cosmos-sdk/cmd/gaia/app" "github.com/cosmos/cosmos-sdk/codec" crkeys "github.com/cosmos/cosmos-sdk/crypto/keys" - cryptoKeys "github.com/cosmos/cosmos-sdk/crypto/keys" "github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/tests" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" - authRest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" txbuilder "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - bankRest "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + bankrest "github.com/cosmos/cosmos-sdk/x/bank/client/rest" + distr "github.com/cosmos/cosmos-sdk/x/distribution" + distrrest "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" "github.com/cosmos/cosmos-sdk/x/gov" - govRest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" + govrest "github.com/cosmos/cosmos-sdk/x/gov/client/rest" gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils" "github.com/cosmos/cosmos-sdk/x/slashing" - slashingRest "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" + slashingrest "github.com/cosmos/cosmos-sdk/x/slashing/client/rest" "github.com/cosmos/cosmos-sdk/x/staking" - stakingRest "github.com/cosmos/cosmos-sdk/x/staking/client/rest" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - "github.com/spf13/viper" - "github.com/stretchr/testify/require" + stakingrest "github.com/cosmos/cosmos-sdk/x/staking/client/rest" abci "github.com/tendermint/tendermint/abci/types" tmcfg "github.com/tendermint/tendermint/config" @@ -59,9 +58,6 @@ import ( ctypes "github.com/tendermint/tendermint/rpc/core/types" tmrpc "github.com/tendermint/tendermint/rpc/lib/server" tmtypes "github.com/tendermint/tendermint/types" - - distr "github.com/cosmos/cosmos-sdk/x/distribution" - distrRest "github.com/cosmos/cosmos-sdk/x/distribution/client/rest" ) // makePathname creates a unique pathname for each test. It will panic if it @@ -202,9 +198,8 @@ func (b AddrSeedSlice) Swap(i, j int) { // their respective sockets where nValidators is the total number of validators // and initAddrs are the accounts to initialize with some steak tokens. It // returns a cleanup function, a set of validator public keys, and a port. -func InitializeTestLCD( - t *testing.T, nValidators int, initAddrs []sdk.AccAddress, -) (cleanup func(), valConsPubKeys []crypto.PubKey, valOperAddrs []sdk.ValAddress, port string) { +func InitializeTestLCD(t *testing.T, nValidators int, initAddrs []sdk.AccAddress, minting bool) ( + cleanup func(), valConsPubKeys []crypto.PubKey, valOperAddrs []sdk.ValAddress, port string) { if nValidators < 1 { panic("InitializeTestLCD must use at least one validator") @@ -239,16 +234,19 @@ func InitializeTestLCD( operPrivKey := secp256k1.GenPrivKey() operAddr := operPrivKey.PubKey().Address() pubKey := privVal.GetPubKey() - delegation := 100 + + power := int64(100) if i > 0 { pubKey = ed25519.GenPrivKey().PubKey() - delegation = 1 + power = 1 } + startTokens := staking.TokensFromTendermintPower(power) + msg := staking.NewMsgCreateValidator( sdk.ValAddress(operAddr), pubKey, - sdk.NewCoin(stakingTypes.DefaultBondDenom, sdk.NewInt(int64(delegation))), - staking.Description{Moniker: fmt.Sprintf("validator-%d", i+1)}, + sdk.NewCoin(staking.DefaultBondDenom, startTokens), + staking.NewDescription(fmt.Sprintf("validator-%d", i+1), "", "", ""), staking.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), ) stdSignMsg := txbuilder.StdSignMsg{ @@ -260,12 +258,14 @@ func InitializeTestLCD( tx := auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, []auth.StdSignature{{Signature: sig, PubKey: operPrivKey.PubKey()}}, "") txBytes, err := cdc.MarshalJSON(tx) require.Nil(t, err) + genTxs = append(genTxs, txBytes) valConsPubKeys = append(valConsPubKeys, pubKey) valOperAddrs = append(valOperAddrs, sdk.ValAddress(operAddr)) accAuth := auth.NewBaseAccountWithAddress(sdk.AccAddress(operAddr)) - accAuth.Coins = sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 150)} + accTokens := staking.TokensFromTendermintPower(150) + accAuth.Coins = sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, accTokens)} accs = append(accs, gapp.NewGenesisAccount(&accAuth)) } @@ -279,15 +279,21 @@ func InitializeTestLCD( // add some tokens to init accounts for _, addr := range initAddrs { accAuth := auth.NewBaseAccountWithAddress(addr) - accAuth.Coins = sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 100)} + accTokens := staking.TokensFromTendermintPower(100) + accAuth.Coins = sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, accTokens)} acc := gapp.NewGenesisAccount(&accAuth) genesisState.Accounts = append(genesisState.Accounts, acc) - genesisState.StakingData.Pool.NotBondedTokens = genesisState.StakingData.Pool.NotBondedTokens.Add(sdk.NewInt(100)) + genesisState.StakingData.Pool.NotBondedTokens = genesisState.StakingData.Pool.NotBondedTokens.Add(accTokens) } - inflationMin := sdk.MustNewDecFromStr("10000.0") + inflationMin := sdk.ZeroDec() + if minting { + inflationMin = sdk.MustNewDecFromStr("10000.0") + genesisState.MintData.Params.InflationMax = sdk.MustNewDecFromStr("15000.0") + } else { + genesisState.MintData.Params.InflationMax = inflationMin + } genesisState.MintData.Minter.Inflation = inflationMin - genesisState.MintData.Params.InflationMax = sdk.MustNewDecFromStr("15000.0") genesisState.MintData.Params.InflationMin = inflationMin appState, err := codec.MarshalJSONIndent(cdc, genesisState) @@ -335,6 +341,7 @@ func startTM( tmcfg *tmcfg.Config, logger log.Logger, genDoc *tmtypes.GenesisDoc, privVal tmtypes.PrivValidator, app abci.Application, ) (*nm.Node, error) { + genDocProvider := func() (*tmtypes.GenesisDoc, error) { return genDoc, nil } dbProvider := func(*nm.DBContext) (dbm.DB, error) { return dbm.NewMemDB(), nil } nodeKey, err := p2p.LoadOrGenNodeKey(tmcfg.NodeKeyFile()) @@ -384,12 +391,12 @@ func registerRoutes(rs *RestServer) { keys.RegisterRoutes(rs.Mux, rs.CliCtx.Indent) rpc.RegisterRoutes(rs.CliCtx, rs.Mux) tx.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) - authRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, auth.StoreKey) - bankRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) - distrRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, distr.StoreKey) - stakingRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) - slashingRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) - govRest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) + authrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, auth.StoreKey) + bankrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + distrrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, distr.StoreKey) + stakingrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + slashingrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc, rs.KeyBase) + govrest.RegisterRoutes(rs.CliCtx, rs.Mux, rs.Cdc) } // Request makes a test LCD test request. It returns a response object and a @@ -678,7 +685,7 @@ func doTransferWithGas( kb := client.MockKeyBase() receiveInfo, _, err := kb.CreateMnemonic( - "receive_address", cryptoKeys.English, gapp.DefaultKeyPass, cryptoKeys.SigningAlgo("secp256k1"), + "receive_address", crkeys.English, gapp.DefaultKeyPass, crkeys.SigningAlgo("secp256k1"), ) require.Nil(t, err) @@ -700,7 +707,7 @@ func doTransferWithGas( ) sr := rest.SendReq{ - Amount: sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 1)}, + Amount: sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, 1)}, BaseReq: baseReq, } @@ -720,7 +727,7 @@ func doTransferWithGasAccAuto( kb := client.MockKeyBase() receiveInfo, _, err := kb.CreateMnemonic( - "receive_address", cryptoKeys.English, gapp.DefaultKeyPass, cryptoKeys.SigningAlgo("secp256k1"), + "receive_address", crkeys.English, gapp.DefaultKeyPass, crkeys.SigningAlgo("secp256k1"), ) require.Nil(t, err) @@ -733,7 +740,7 @@ func doTransferWithGasAccAuto( ) sr := rest.SendReq{ - Amount: sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 1)}, + Amount: sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, 1)}, BaseReq: baseReq, } @@ -755,7 +762,8 @@ type sendReq struct { // POST /staking/delegators/{delegatorAddr}/delegations Submit delegation func doDelegate(t *testing.T, port, name, password string, - delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64, fees sdk.Coins) (resultTx sdk.TxResponse) { + delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) { + acc := getAccount(t, port, delAddr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() @@ -765,7 +773,7 @@ func doDelegate(t *testing.T, port, name, password string, BaseReq: baseReq, DelegatorAddr: delAddr, ValidatorAddr: valAddr, - Delegation: sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, amount), + Delegation: sdk.NewCoin(staking.DefaultBondDenom, amount), } req, err := cdc.MarshalJSON(msg) require.NoError(t, err) @@ -788,7 +796,7 @@ type msgDelegationsInput struct { // POST /staking/delegators/{delegatorAddr}/delegations Submit delegation func doUndelegate(t *testing.T, port, name, password string, - delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount int64, fees sdk.Coins) (resultTx sdk.TxResponse) { + delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) { acc := getAccount(t, port, delAddr) accnum := acc.GetAccountNumber() @@ -799,7 +807,7 @@ func doUndelegate(t *testing.T, port, name, password string, BaseReq: baseReq, DelegatorAddr: delAddr, ValidatorAddr: valAddr, - SharesAmount: sdk.NewDec(amount), + SharesAmount: sdk.NewDecFromInt(amount), } req, err := cdc.MarshalJSON(msg) require.NoError(t, err) @@ -823,7 +831,8 @@ type msgUndelegateInput struct { // POST /staking/delegators/{delegatorAddr}/delegations Submit delegation func doBeginRedelegation(t *testing.T, port, name, password string, - delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, amount int64, fees sdk.Coins) (resultTx sdk.TxResponse) { + delAddr sdk.AccAddress, valSrcAddr, valDstAddr sdk.ValAddress, amount sdk.Int, + fees sdk.Coins) (resultTx sdk.TxResponse) { acc := getAccount(t, port, delAddr) accnum := acc.GetAccountNumber() @@ -837,7 +846,7 @@ func doBeginRedelegation(t *testing.T, port, name, password string, DelegatorAddr: delAddr, ValidatorSrcAddr: valSrcAddr, ValidatorDstAddr: valDstAddr, - SharesAmount: sdk.NewDec(amount), + SharesAmount: sdk.NewDecFromInt(amount), } req, err := cdc.MarshalJSON(msg) require.NoError(t, err) @@ -1056,7 +1065,9 @@ func getStakingParams(t *testing.T, port string) staking.Params { // ICS 22 - Gov // ---------------------------------------------------------------------- // POST /gov/proposals Submit a proposal -func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, amount int64, fees sdk.Coins) (resultTx sdk.TxResponse) { +func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, + amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) { + acc := getAccount(t, port, proposerAddr) accnum := acc.GetAccountNumber() sequence := acc.GetSequence() @@ -1068,7 +1079,7 @@ func doSubmitProposal(t *testing.T, port, seed, name, password string, proposerA Description: "test", ProposalType: "Text", Proposer: proposerAddr, - InitialDeposit: sdk.Coins{sdk.NewCoin(stakingTypes.DefaultBondDenom, sdk.NewInt(amount))}, + InitialDeposit: sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, amount)}, BaseReq: baseReq, } @@ -1151,7 +1162,8 @@ func getProposalsFilterStatus(t *testing.T, port string, status gov.ProposalStat } // POST /gov/proposals/{proposalId}/deposits Deposit tokens to a proposal -func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, amount int64, fees sdk.Coins) (resultTx sdk.TxResponse) { +func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk.AccAddress, proposalID uint64, + amount sdk.Int, fees sdk.Coins) (resultTx sdk.TxResponse) { acc := getAccount(t, port, proposerAddr) accnum := acc.GetAccountNumber() @@ -1161,7 +1173,7 @@ func doDeposit(t *testing.T, port, seed, name, password string, proposerAddr sdk dr := rest.DepositReq{ Depositor: proposerAddr, - Amount: sdk.Coins{sdk.NewCoin(stakingTypes.DefaultBondDenom, sdk.NewInt(amount))}, + Amount: sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, amount)}, BaseReq: baseReq, } diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 3eae8af87..da71e750a 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -23,14 +23,12 @@ import ( "github.com/cosmos/cosmos-sdk/x/mint" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/staking" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( // bonded tokens given to genesis validators/accounts - freeFermionVal = int64(100) - freeFermionsAcc = sdk.NewInt(150) - bondDenom = stakingTypes.DefaultBondDenom + freeFermionsAcc = staking.TokensFromTendermintPower(150) + bondDenom = staking.DefaultBondDenom ) // State to Unmarshal diff --git a/cmd/gaia/app/genesis_test.go b/cmd/gaia/app/genesis_test.go index 82829b699..eb4511217 100644 --- a/cmd/gaia/app/genesis_test.go +++ b/cmd/gaia/app/genesis_test.go @@ -15,7 +15,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/staking" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( @@ -104,7 +103,7 @@ func TestGaiaAppGenState(t *testing.T) { func makeMsg(name string, pk crypto.PubKey) auth.StdTx { desc := staking.NewDescription(name, "", "", "") - comm := stakingTypes.CommissionMsg{} + comm := staking.CommissionMsg{} msg := staking.NewMsgCreateValidator(sdk.ValAddress(pk.Address()), pk, sdk.NewInt64Coin(bondDenom, 50), desc, comm) return auth.NewStdTx([]sdk.Msg{msg}, auth.StdFee{}, nil, "") @@ -131,7 +130,7 @@ func TestGaiaGenesisValidation(t *testing.T) { // require bonded + jailed validator fails validation genesisState = makeGenesisState(t, genTxs) - val1 := stakingTypes.NewValidator(addr1, pk1, stakingTypes.Description{Moniker: "test #2"}) + val1 := staking.NewValidator(addr1, pk1, staking.NewDescription("test #2", "", "", "")) val1.Jailed = true val1.Status = sdk.Bonded genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val1) @@ -141,7 +140,7 @@ func TestGaiaGenesisValidation(t *testing.T) { // require duplicate validator fails validation val1.Jailed = false genesisState = makeGenesisState(t, genTxs) - val2 := stakingTypes.NewValidator(addr1, pk1, stakingTypes.Description{Moniker: "test #3"}) + val2 := staking.NewValidator(addr1, pk1, staking.NewDescription("test #3", "", "", "")) genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val1) genesisState.StakingData.Validators = append(genesisState.StakingData.Validators, val2) err = GaiaValidateGenesisState(genesisState) @@ -152,7 +151,7 @@ func TestNewDefaultGenesisAccount(t *testing.T) { addr := secp256k1.GenPrivKeySecp256k1([]byte("")).PubKey().Address() acc := NewDefaultGenesisAccount(sdk.AccAddress(addr)) require.Equal(t, sdk.NewInt(1000), acc.Coins.AmountOf("footoken")) - require.Equal(t, sdk.NewInt(150), acc.Coins.AmountOf(bondDenom)) + require.Equal(t, staking.TokensFromTendermintPower(150), acc.Coins.AmountOf(bondDenom)) } func TestGenesisStateSanitize(t *testing.T) { diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 860404a56..60ba58322 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -34,7 +34,6 @@ import ( slashingsim "github.com/cosmos/cosmos-sdk/x/slashing/simulation" "github.com/cosmos/cosmos-sdk/x/staking" stakingsim "github.com/cosmos/cosmos-sdk/x/staking/simulation" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( @@ -98,7 +97,7 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest // randomly generate some genesis accounts for i, acc := range accs { - coins := sdk.Coins{sdk.NewCoin(stakingTypes.DefaultBondDenom, sdk.NewInt(amount))} + coins := sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(amount))} bacc := auth.NewBaseAccountWithAddress(acc.Address) bacc.SetCoins(coins) @@ -155,7 +154,7 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest govGenesis := gov.GenesisState{ StartingProposalID: uint64(r.Intn(100)), DepositParams: gov.DepositParams{ - MinDeposit: sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, int64(r.Intn(1e3)))}, + MinDeposit: sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, int64(r.Intn(1e3)))}, MaxDepositPeriod: vp, }, VotingParams: gov.VotingParams{ @@ -174,7 +173,7 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest Params: staking.Params{ UnbondingTime: time.Duration(randIntBetween(r, 60, 60*60*24*3*2)) * time.Second, MaxValidators: uint16(r.Intn(250)), - BondDenom: stakingTypes.DefaultBondDenom, + BondDenom: staking.DefaultBondDenom, }, } fmt.Printf("Selected randomly generated staking parameters:\n\t%+v\n", stakingGenesis) @@ -195,7 +194,7 @@ func appStateRandomizedFn(r *rand.Rand, accs []simulation.Account, genesisTimest Minter: mint.InitialMinter( sdk.NewDecWithPrec(int64(r.Intn(99)), 2)), Params: mint.NewParams( - stakingTypes.DefaultBondDenom, + staking.DefaultBondDenom, sdk.NewDecWithPrec(int64(r.Intn(99)), 2), sdk.NewDecWithPrec(20, 2), sdk.NewDecWithPrec(7, 2), diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 2139f1fd0..aefb2ab7f 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -20,7 +20,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/staking" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestGaiaCLIKeysAddMultisig(t *testing.T) { @@ -167,8 +166,9 @@ func TestGaiaCLIFeesDeduction(t *testing.T) { require.Equal(t, fooAmt.Int64(), fooAcc.GetCoins().AmountOf(fooDenom).Int64()) // insufficient funds (coins + fees) tx fails + largeCoins := staking.TokensFromTendermintPower(10000000) success, _, _ = f.TxSend( - keyFoo, barAddr, sdk.NewInt64Coin(fooDenom, 10000000), + keyFoo, barAddr, sdk.NewCoin(fooDenom, largeCoins), fmt.Sprintf("--fees=%s", sdk.NewInt64Coin(feeDenom, 2))) require.False(t, success) @@ -201,45 +201,47 @@ func TestGaiaCLISend(t *testing.T) { barAddr := f.KeyAddress(keyBar) fooAcc := f.QueryAccount(fooAddr) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(denom).Int64()) + startTokens := staking.TokensFromTendermintPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) // Send some tokens from one account to the other - f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10)) + sendTokens := staking.TokensFromTendermintPower(10) + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens)) tests.WaitForNextNBlocksTM(1, f.Port) // Ensure account balances match expected barAcc := f.QueryAccount(barAddr) - require.Equal(t, int64(10), barAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) fooAcc = f.QueryAccount(fooAddr) - require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, startTokens.Sub(sendTokens), fooAcc.GetCoins().AmountOf(denom)) // Test --dry-run - success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--dry-run") + success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--dry-run") require.True(t, success) // Check state didn't change fooAcc = f.QueryAccount(fooAddr) - require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, startTokens.Sub(sendTokens), fooAcc.GetCoins().AmountOf(denom)) // test autosequencing - f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10)) + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens)) tests.WaitForNextNBlocksTM(1, f.Port) // Ensure account balances match expected barAcc = f.QueryAccount(barAddr) - require.Equal(t, int64(20), barAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, sendTokens.MulRaw(2), barAcc.GetCoins().AmountOf(denom)) fooAcc = f.QueryAccount(fooAddr) - require.Equal(t, int64(30), fooAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, startTokens.Sub(sendTokens.MulRaw(2)), fooAcc.GetCoins().AmountOf(denom)) // test memo - f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--memo='testmemo'") + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--memo='testmemo'") tests.WaitForNextNBlocksTM(1, f.Port) // Ensure account balances match expected barAcc = f.QueryAccount(barAddr) - require.Equal(t, int64(30), barAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, sendTokens.MulRaw(3), barAcc.GetCoins().AmountOf(denom)) fooAcc = f.QueryAccount(fooAddr) - require.Equal(t, int64(20), fooAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, startTokens.Sub(sendTokens.MulRaw(3)), fooAcc.GetCoins().AmountOf(denom)) f.Cleanup() } @@ -256,34 +258,36 @@ func TestGaiaCLIGasAuto(t *testing.T) { barAddr := f.KeyAddress(keyBar) fooAcc := f.QueryAccount(fooAddr) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(denom).Int64()) + startTokens := staking.TokensFromTendermintPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) // Test failure with auto gas disabled and very little gas set by hand - success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--gas=10") + sendTokens := staking.TokensFromTendermintPower(10) + success, _, _ := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=10") require.False(t, success) // Check state didn't change fooAcc = f.QueryAccount(fooAddr) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) // Test failure with negative gas - success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--gas=-100") + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=-100") require.False(t, success) // Check state didn't change fooAcc = f.QueryAccount(fooAddr) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) // Test failure with 0 gas - success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--gas=0") + success, _, _ = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=0") require.False(t, success) // Check state didn't change fooAcc = f.QueryAccount(fooAddr) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) // Enable auto gas - success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--gas=auto") + success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=auto") require.NotEmpty(t, stderr) require.True(t, success) cdc := app.MakeCodec() @@ -295,7 +299,7 @@ func TestGaiaCLIGasAuto(t *testing.T) { // Check state has changed accordingly fooAcc = f.QueryAccount(fooAddr) - require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, startTokens.Sub(sendTokens), fooAcc.GetCoins().AmountOf(denom)) f.Cleanup() } @@ -308,23 +312,17 @@ func TestGaiaCLICreateValidator(t *testing.T) { proc := f.GDStart() defer proc.Stop(false) - fooAddr := f.KeyAddress(keyFoo) barAddr := f.KeyAddress(keyBar) barVal := sdk.ValAddress(barAddr) consPubKey := sdk.MustBech32ifyConsPub(ed25519.GenPrivKey().PubKey()) - f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10)) + sendTokens := staking.TokensFromTendermintPower(10) + f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens)) tests.WaitForNextNBlocksTM(1, f.Port) barAcc := f.QueryAccount(barAddr) - require.Equal(t, int64(10), barAcc.GetCoins().AmountOf(denom).Int64()) - fooAcc := f.QueryAccount(fooAddr) - require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(denom).Int64()) - - defaultParams := staking.DefaultParams() - initialPool := staking.InitialPool() - initialPool.BondedTokens = initialPool.BondedTokens.Add(sdk.NewInt(101)) // Delegate tx on GaiaAppGenState + require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) // Generate a create validator transaction and ensure correctness success, stdout, stderr := f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewInt64Coin(denom, 2), "--generate-only") @@ -337,21 +335,22 @@ func TestGaiaCLICreateValidator(t *testing.T) { require.Equal(t, 0, len(msg.GetSignatures())) // Test --dry-run - success, _, _ = f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewInt64Coin(denom, 2), "--dry-run") + newValTokens := staking.TokensFromTendermintPower(2) + success, _, _ = f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewCoin(denom, newValTokens), "--dry-run") require.True(t, success) // Create the validator - f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewInt64Coin(denom, 2)) + f.TxStakingCreateValidator(keyBar, consPubKey, sdk.NewCoin(denom, newValTokens)) tests.WaitForNextNBlocksTM(1, f.Port) // Ensure funds were deducted properly barAcc = f.QueryAccount(barAddr) - require.Equal(t, int64(8), barAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, sendTokens.Sub(newValTokens), barAcc.GetCoins().AmountOf(denom)) // Ensure that validator state is as expected validator := f.QueryStakingValidator(barVal) require.Equal(t, validator.OperatorAddr, barVal) - require.True(sdk.IntEq(t, sdk.NewInt(2), validator.Tokens)) + require.True(sdk.IntEq(t, newValTokens, validator.Tokens)) // Query delegations to the validator validatorDelegations := f.QueryStakingDelegationsTo(barVal) @@ -359,27 +358,21 @@ func TestGaiaCLICreateValidator(t *testing.T) { require.NotZero(t, validatorDelegations[0].Shares) // unbond a single share - success = f.TxStakingUnbond(keyBar, "1", barVal) + unbondTokens := staking.TokensFromTendermintPower(1) + success = f.TxStakingUnbond(keyBar, unbondTokens.String(), barVal) require.True(t, success) tests.WaitForNextNBlocksTM(1, f.Port) // Ensure bonded staking is correct + remainingTokens := newValTokens.Sub(unbondTokens) validator = f.QueryStakingValidator(barVal) - require.Equal(t, "1", validator.Tokens.String()) + require.Equal(t, remainingTokens, validator.Tokens) // Get unbonding delegations from the validator validatorUbds := f.QueryStakingUnbondingDelegationsFrom(barVal) require.Len(t, validatorUbds, 1) require.Len(t, validatorUbds[0].Entries, 1) - require.Equal(t, "1", validatorUbds[0].Entries[0].Balance.Amount.String()) - - // Query staking parameters - params := f.QueryStakingParameters() - require.True(t, defaultParams.Equal(params)) - - // Query staking pool - pool := f.QueryStakingPool() - require.Equal(t, initialPool.BondedTokens, pool.BondedTokens) + require.Equal(t, remainingTokens.String(), validatorUbds[0].Entries[0].Balance.Amount.String()) f.Cleanup() } @@ -399,14 +392,16 @@ func TestGaiaCLISubmitProposal(t *testing.T) { fooAddr := f.KeyAddress(keyFoo) fooAcc := f.QueryAccount(fooAddr) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(stakingTypes.DefaultBondDenom).Int64()) + startTokens := staking.TokensFromTendermintPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(staking.DefaultBondDenom)) proposalsQuery := f.QueryGovProposals() require.Empty(t, proposalsQuery) // Test submit generate only for submit proposal + proposalTokens := staking.TokensFromTendermintPower(5) success, stdout, stderr := f.TxGovSubmitProposal( - keyFoo, "Text", "Test", "test", sdk.NewInt64Coin(denom, 5), "--generate-only") + keyFoo, "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens), "--generate-only") require.True(t, success) require.Empty(t, stderr) msg := unmarshalStdTx(t, stdout) @@ -415,11 +410,11 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, 0, len(msg.GetSignatures())) // Test --dry-run - success, _, _ = f.TxGovSubmitProposal(keyFoo, "Text", "Test", "test", sdk.NewInt64Coin(denom, 5), "--dry-run") + success, _, _ = f.TxGovSubmitProposal(keyFoo, "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens), "--dry-run") require.True(t, success) // Create the proposal - f.TxGovSubmitProposal(keyFoo, "Text", "Test", "test", sdk.NewInt64Coin(denom, 5)) + f.TxGovSubmitProposal(keyFoo, "Text", "Test", "test", sdk.NewCoin(denom, proposalTokens)) tests.WaitForNextNBlocksTM(1, f.Port) // Ensure transaction tags can be queried @@ -428,7 +423,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // Ensure deposit was deducted fooAcc = f.QueryAccount(fooAddr) - require.Equal(t, int64(45), fooAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, startTokens.Sub(proposalTokens), fooAcc.GetCoins().AmountOf(denom)) // Ensure propsal is directly queryable proposal1 := f.QueryGovProposal(1) @@ -441,10 +436,11 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // Query the deposits on the proposal deposit := f.QueryGovDeposit(1, fooAddr) - require.Equal(t, int64(5), deposit.Amount.AmountOf(denom).Int64()) + require.Equal(t, proposalTokens, deposit.Amount.AmountOf(denom)) // Test deposit generate only - success, stdout, stderr = f.TxGovDeposit(1, keyFoo, sdk.NewInt64Coin(denom, 10), "--generate-only") + depositTokens := staking.TokensFromTendermintPower(10) + success, stdout, stderr = f.TxGovDeposit(1, keyFoo, sdk.NewCoin(denom, depositTokens), "--generate-only") require.True(t, success) require.Empty(t, stderr) msg = unmarshalStdTx(t, stdout) @@ -453,17 +449,17 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, 0, len(msg.GetSignatures())) // Run the deposit transaction - f.TxGovDeposit(1, keyFoo, sdk.NewInt64Coin(denom, 10)) + f.TxGovDeposit(1, keyFoo, sdk.NewCoin(denom, depositTokens)) tests.WaitForNextNBlocksTM(1, f.Port) // test query deposit deposits := f.QueryGovDeposits(1) require.Len(t, deposits, 1) - require.Equal(t, int64(15), deposits[0].Amount.AmountOf(denom).Int64()) + require.Equal(t, proposalTokens.Add(depositTokens), deposits[0].Amount.AmountOf(denom)) // Ensure querying the deposit returns the proper amount deposit = f.QueryGovDeposit(1, fooAddr) - require.Equal(t, int64(15), deposit.Amount.AmountOf(denom).Int64()) + require.Equal(t, proposalTokens.Add(depositTokens), deposit.Amount.AmountOf(denom)) // Ensure tags are set on the transaction txs = f.QueryTxs(1, 50, "action:deposit", fmt.Sprintf("depositor:%s", fooAddr)) @@ -471,7 +467,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { // Ensure account has expected amount of funds fooAcc = f.QueryAccount(fooAddr) - require.Equal(t, int64(35), fooAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, startTokens.Sub(proposalTokens.Add(depositTokens)), fooAcc.GetCoins().AmountOf(denom)) // Fetch the proposal and ensure it is now in the voting period proposal1 = f.QueryGovProposal(1) @@ -515,7 +511,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, uint64(1), proposalsQuery[0].GetProposalID()) // submit a second test proposal - f.TxGovSubmitProposal(keyFoo, "Text", "Apples", "test", sdk.NewInt64Coin(denom, 5)) + f.TxGovSubmitProposal(keyFoo, "Text", "Apples", "test", sdk.NewCoin(denom, proposalTokens)) tests.WaitForNextNBlocksTM(1, f.Port) // Test limit on proposals query @@ -635,7 +631,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { barAddr := f.KeyAddress(keyBar) // Test generate sendTx with default gas - success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--generate-only") + sendTokens := staking.TokensFromTendermintPower(10) + success, stdout, stderr := f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--generate-only") require.True(t, success) require.Empty(t, stderr) msg := unmarshalStdTx(t, stdout) @@ -644,7 +641,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { require.Equal(t, 0, len(msg.GetSignatures())) // Test generate sendTx with --gas=$amount - success, stdout, stderr = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--gas=100", "--generate-only") + success, stdout, stderr = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=100", "--generate-only") require.True(t, success) require.Empty(t, stderr) msg = unmarshalStdTx(t, stdout) @@ -653,7 +650,7 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { require.Equal(t, 0, len(msg.GetSignatures())) // Test generate sendTx, estimate gas - success, stdout, stderr = f.TxSend(keyFoo, barAddr, sdk.NewInt64Coin(denom, 10), "--gas=auto", "--generate-only") + success, stdout, stderr = f.TxSend(keyFoo, barAddr, sdk.NewCoin(denom, sendTokens), "--gas=auto", "--generate-only") require.True(t, success) require.NotEmpty(t, stderr) msg = unmarshalStdTx(t, stdout) @@ -689,7 +686,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { // Ensure foo has right amount of funds fooAcc := f.QueryAccount(fooAddr) - require.Equal(t, int64(50), fooAcc.GetCoins().AmountOf(denom).Int64()) + startTokens := staking.TokensFromTendermintPower(50) + require.Equal(t, startTokens, fooAcc.GetCoins().AmountOf(denom)) // Test broadcast success, stdout, _ = f.TxBroadcast(signedTxFile.Name()) @@ -706,8 +704,8 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { // Ensure account state barAcc := f.QueryAccount(barAddr) fooAcc = f.QueryAccount(fooAddr) - require.Equal(t, int64(10), barAcc.GetCoins().AmountOf(denom).Int64()) - require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf(denom).Int64()) + require.Equal(t, sendTokens, barAcc.GetCoins().AmountOf(denom)) + require.Equal(t, startTokens.Sub(sendTokens), fooAcc.GetCoins().AmountOf(denom)) f.Cleanup() } diff --git a/cmd/gaia/cli_test/test_helpers.go b/cmd/gaia/cli_test/test_helpers.go index b54fc523a..740ea519b 100644 --- a/cmd/gaia/cli_test/test_helpers.go +++ b/cmd/gaia/cli_test/test_helpers.go @@ -38,10 +38,10 @@ const ( ) var startCoins = sdk.Coins{ - sdk.NewInt64Coin(feeDenom, 1000000), - sdk.NewInt64Coin(fee2Denom, 1000000), - sdk.NewInt64Coin(fooDenom, 1000), - sdk.NewInt64Coin(denom, 150), + sdk.NewCoin(feeDenom, staking.TokensFromTendermintPower(1000000)), + sdk.NewCoin(fee2Denom, staking.TokensFromTendermintPower(1000000)), + sdk.NewCoin(fooDenom, staking.TokensFromTendermintPower(1000)), + sdk.NewCoin(denom, staking.TokensFromTendermintPower(150)), } //___________________________________________________________________________________ diff --git a/cmd/gaia/init/gentx.go b/cmd/gaia/init/gentx.go index 6ce761965..93e4b5da9 100644 --- a/cmd/gaia/init/gentx.go +++ b/cmd/gaia/init/gentx.go @@ -26,12 +26,13 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" + "github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking/client/cli" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) -const ( - defaultAmount = "100" + stakingTypes.DefaultBondDenom +var ( + defaultTokens = staking.TokensFromTendermintPower(100) + defaultAmount = defaultTokens.String() + staking.DefaultBondDenom defaultCommissionRate = "0.1" defaultCommissionMaxRate = "0.2" defaultCommissionMaxChangeRate = "0.01" diff --git a/cmd/gaia/init/testnet.go b/cmd/gaia/init/testnet.go index c69384a2d..7ce811568 100644 --- a/cmd/gaia/init/testnet.go +++ b/cmd/gaia/init/testnet.go @@ -188,18 +188,21 @@ func initTestnet(config *tmconfig.Config, cdc *codec.Codec) error { return err } + accTokens := staking.TokensFromTendermintPower(1000) + accStakingTokens := staking.TokensFromTendermintPower(500) accs = append(accs, app.GenesisAccount{ Address: addr, Coins: sdk.Coins{ - sdk.NewInt64Coin(fmt.Sprintf("%stoken", nodeDirName), 1000), - sdk.NewInt64Coin(stakingtypes.DefaultBondDenom, 500), + sdk.NewCoin(fmt.Sprintf("%stoken", nodeDirName), accTokens), + sdk.NewCoin(stakingtypes.DefaultBondDenom, accStakingTokens), }, }) + valTokens := staking.TokensFromTendermintPower(100) msg := staking.NewMsgCreateValidator( sdk.ValAddress(addr), valPubKeys[i], - sdk.NewInt64Coin(stakingtypes.DefaultBondDenom, 100), + sdk.NewCoin(stakingtypes.DefaultBondDenom, valTokens), staking.NewDescription(nodeDirName, "", "", ""), staking.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()), ) diff --git a/types/decimal.go b/types/decimal.go index 976ddd560..5f59a0f54 100644 --- a/types/decimal.go +++ b/types/decimal.go @@ -249,6 +249,16 @@ func (d Dec) MulInt(i Int) Dec { return Dec{mul} } +// MulInt64 - multiplication with int64 +func (d Dec) MulInt64(i int64) Dec { + mul := new(big.Int).Mul(d.Int, big.NewInt(i)) + + if mul.BitLen() > 255+DecimalPrecisionBits { + panic("Int overflow") + } + return Dec{mul} +} + // quotient func (d Dec) Quo(d2 Dec) Dec { @@ -287,6 +297,12 @@ func (d Dec) QuoInt(i Int) Dec { return Dec{mul} } +// QuoInt64 - quotient with int64 +func (d Dec) QuoInt64(i int64) Dec { + mul := new(big.Int).Quo(d.Int, big.NewInt(i)) + return Dec{mul} +} + // is integer, e.g. decimals are zero func (d Dec) IsInteger() bool { return new(big.Int).Rem(d.Int, precisionReuse).Sign() == 0 diff --git a/types/stake.go b/types/stake.go index 25c02d848..cd3e442a6 100644 --- a/types/stake.go +++ b/types/stake.go @@ -42,8 +42,9 @@ type Validator interface { GetOperator() ValAddress // operator address to receive/return validators coins GetConsPubKey() crypto.PubKey // validation consensus pubkey GetConsAddr() ConsAddress // validation consensus address - GetPower() Int // validation power GetTokens() Int // validation tokens + GetBondedTokens() Int // validator bonded tokens + GetTendermintPower() int64 // validation power in tendermint GetCommission() Dec // validator commission rate GetDelegatorShares() Dec // total outstanding delegator shares GetBondHeight() int64 // height in which the validator became active @@ -54,7 +55,7 @@ type Validator interface { func ABCIValidator(v Validator) abci.Validator { return abci.Validator{ Address: v.GetConsPubKey().Address(), - Power: v.GetPower().Int64(), + Power: v.GetTendermintPower(), } } diff --git a/x/auth/ante.go b/x/auth/ante.go index bc39f5323..37f159536 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -342,7 +342,7 @@ func EnsureSufficientMempoolFees(ctx sdk.Context, stdFee StdFee) sdk.Result { glDec := sdk.NewDec(int64(stdFee.Gas)) for i, gp := range minGasPrices { fee := gp.Amount.Mul(glDec) - requiredFees[i] = sdk.NewInt64Coin(gp.Denom, fee.Ceil().RoundInt64()) + requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) } if !stdFee.Amount.IsAllGTE(requiredFees) { diff --git a/x/auth/client/txbuilder/txbuilder.go b/x/auth/client/txbuilder/txbuilder.go index 331d811ba..df8bb9782 100644 --- a/x/auth/client/txbuilder/txbuilder.go +++ b/x/auth/client/txbuilder/txbuilder.go @@ -177,7 +177,7 @@ func (bldr TxBuilder) Build(msgs []sdk.Msg) (StdSignMsg, error) { fees = make(sdk.Coins, len(bldr.gasPrices)) for i, gp := range bldr.gasPrices { fee := gp.Amount.Mul(glDec) - fees[i] = sdk.NewInt64Coin(gp.Denom, fee.Ceil().RoundInt64()) + fees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt()) } } diff --git a/x/distribution/keeper/delegation_test.go b/x/distribution/keeper/delegation_test.go index 3f601ad32..fb0cda012 100644 --- a/x/distribution/keeper/delegation_test.go +++ b/x/distribution/keeper/delegation_test.go @@ -71,9 +71,12 @@ func TestCalculateRewardsAfterSlash(t *testing.T) { // create validator with 50% commission commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + valPower := int64(100) + valTokens := staking.TokensFromTendermintPower(valPower) msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission) - require.True(t, sh(ctx, msg).IsOK()) + sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission) + got := sh(ctx, msg) + require.True(t, got.IsOK(), "%v", got) // end block to bond validator staking.EndBlocker(ctx, sk) @@ -95,7 +98,7 @@ func TestCalculateRewardsAfterSlash(t *testing.T) { ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // slash the validator by 50% - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), 100, sdk.NewDecWithPrec(5, 1)) + sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), valPower, sdk.NewDecWithPrec(5, 1)) // retrieve validator val = sk.Validator(ctx, valOpAddr1) @@ -104,8 +107,8 @@ func TestCalculateRewardsAfterSlash(t *testing.T) { ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // allocate some rewards - initial := int64(10) - tokens := sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial)}} + initial := staking.TokensFromTendermintPower(10) + tokens := sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDecFromInt(initial)}} k.AllocateTokensToValidator(ctx, val, tokens) // end period @@ -115,10 +118,11 @@ func TestCalculateRewardsAfterSlash(t *testing.T) { rewards = k.calculateDelegationRewards(ctx, val, del, endingPeriod) // rewards should be half the tokens - require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial / 2)}}, rewards) + require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDecFromInt(initial.DivRaw(2))}}, rewards) // commission should be the other half - require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial / 2)}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDecFromInt(initial.DivRaw(2))}}, + k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) } func TestCalculateRewardsAfterManySlashes(t *testing.T) { @@ -129,9 +133,11 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) { k.SetOutstandingRewards(ctx, sdk.DecCoins{}) // create validator with 50% commission + power := int64(100) + valTokens := staking.TokensFromTendermintPower(power) commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission) + sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission) require.True(t, sh(ctx, msg).IsOK()) // end block to bond validator @@ -154,7 +160,7 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) { ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // slash the validator by 50% - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), 100, sdk.NewDecWithPrec(5, 1)) + sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power, sdk.NewDecWithPrec(5, 1)) // fetch the validator again val = sk.Validator(ctx, valOpAddr1) @@ -163,12 +169,12 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) { ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // allocate some rewards - initial := int64(10) - tokens := sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial)}} + initial := staking.TokensFromTendermintPower(10) + tokens := sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDecFromInt(initial)}} k.AllocateTokensToValidator(ctx, val, tokens) // slash the validator by 50% again - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), 50, sdk.NewDecWithPrec(5, 1)) + sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power/2, sdk.NewDecWithPrec(5, 1)) // fetch the validator again val = sk.Validator(ctx, valOpAddr1) @@ -186,10 +192,11 @@ func TestCalculateRewardsAfterManySlashes(t *testing.T) { rewards = k.calculateDelegationRewards(ctx, val, del, endingPeriod) // rewards should be half the tokens - require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial)}}, rewards) + require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDecFromInt(initial)}}, rewards) // commission should be the other half - require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial)}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDecFromInt(initial)}}, + k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) } func TestCalculateRewardsMultiDelegator(t *testing.T) { @@ -251,22 +258,27 @@ func TestCalculateRewardsMultiDelegator(t *testing.T) { } func TestWithdrawDelegationRewardsBasic(t *testing.T) { - balance := int64(1000) - ctx, ak, k, sk, _ := CreateTestInputDefault(t, false, balance) + balancePower := int64(1000) + balanceTokens := staking.TokensFromTendermintPower(balancePower) + ctx, ak, k, sk, _ := CreateTestInputDefault(t, false, balancePower) sh := staking.NewHandler(sk) // initialize state k.SetOutstandingRewards(ctx, sdk.DecCoins{}) // create validator with 50% commission - bond := int64(100) + power := int64(100) + valTokens := staking.TokensFromTendermintPower(power) commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(bond)), staking.Description{}, commission) + sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission) require.True(t, sh(ctx, msg).IsOK()) // assert correct initial balance - require.Equal(t, sdk.Coins{{staking.DefaultBondDenom, sdk.NewInt(balance - bond)}}, ak.GetAccount(ctx, sdk.AccAddress(valOpAddr1)).GetCoins()) + expTokens := balanceTokens.Sub(valTokens) + require.Equal(t, + sdk.Coins{{staking.DefaultBondDenom, expTokens}}, + ak.GetAccount(ctx, sdk.AccAddress(valOpAddr1)).GetCoins()) // end block to bond validator staking.EndBlocker(ctx, sk) @@ -278,8 +290,8 @@ func TestWithdrawDelegationRewardsBasic(t *testing.T) { val := sk.Validator(ctx, valOpAddr1) // allocate some rewards - initial := int64(10) - tokens := sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial)}} + initial := staking.TokensFromTendermintPower(10) + tokens := sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDecFromInt(initial)}} k.AllocateTokensToValidator(ctx, val, tokens) // historical count should be 2 (initial + latest for delegation) @@ -292,13 +304,15 @@ func TestWithdrawDelegationRewardsBasic(t *testing.T) { require.Equal(t, uint64(2), k.GetValidatorHistoricalReferenceCount(ctx)) // assert correct balance - require.Equal(t, sdk.Coins{{staking.DefaultBondDenom, sdk.NewInt(balance - bond + (initial / 2))}}, ak.GetAccount(ctx, sdk.AccAddress(valOpAddr1)).GetCoins()) + exp := balanceTokens.Sub(valTokens).Add(initial.DivRaw(2)) + require.Equal(t, sdk.Coins{{staking.DefaultBondDenom, exp}}, ak.GetAccount(ctx, sdk.AccAddress(valOpAddr1)).GetCoins()) // withdraw commission require.Nil(t, k.WithdrawValidatorCommission(ctx, valOpAddr1)) // assert correct balance - require.Equal(t, sdk.Coins{{staking.DefaultBondDenom, sdk.NewInt(balance - bond + initial)}}, ak.GetAccount(ctx, sdk.AccAddress(valOpAddr1)).GetCoins()) + exp = balanceTokens.Sub(valTokens).Add(initial) + require.Equal(t, sdk.Coins{{staking.DefaultBondDenom, exp}}, ak.GetAccount(ctx, sdk.AccAddress(valOpAddr1)).GetCoins()) } func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { @@ -309,9 +323,11 @@ func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { k.SetOutstandingRewards(ctx, sdk.DecCoins{}) // create validator with 50% commission + power := int64(100) + valTokens := staking.TokensFromTendermintPower(power) commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission) + sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission) require.True(t, sh(ctx, msg).IsOK()) // end block to bond validator @@ -334,15 +350,15 @@ func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // allocate some rewards - initial := int64(10) - tokens := sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial)}} + initial := sdk.NewDecFromInt(staking.TokensFromTendermintPower(10)) + tokens := sdk.DecCoins{{staking.DefaultBondDenom, initial}} k.AllocateTokensToValidator(ctx, val, tokens) // slash the validator by 50% - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), 100, sdk.NewDecWithPrec(5, 1)) + sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power, sdk.NewDecWithPrec(5, 1)) // slash the validator by 50% again - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), 50, sdk.NewDecWithPrec(5, 1)) + sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power/2, sdk.NewDecWithPrec(5, 1)) // fetch the validator again val = sk.Validator(ctx, valOpAddr1) @@ -360,10 +376,10 @@ func TestCalculateRewardsAfterManySlashesInSameBlock(t *testing.T) { rewards = k.calculateDelegationRewards(ctx, val, del, endingPeriod) // rewards should be half the tokens - require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial)}}, rewards) + require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, initial}}, rewards) // commission should be the other half - require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial)}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, initial}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) } func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { @@ -375,8 +391,10 @@ func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { // create validator with 50% commission commission := staking.NewCommissionMsg(sdk.NewDecWithPrec(5, 1), sdk.NewDecWithPrec(5, 1), sdk.NewDec(0)) + power := int64(100) + valTokens := staking.TokensFromTendermintPower(power) msg := staking.NewMsgCreateValidator(valOpAddr1, valConsPk1, - sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100)), staking.Description{}, commission) + sdk.NewCoin(staking.DefaultBondDenom, valTokens), staking.Description{}, commission) require.True(t, sh(ctx, msg).IsOK()) // end block to bond validator @@ -387,17 +405,19 @@ func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { del1 := sk.Delegation(ctx, sdk.AccAddress(valOpAddr1), valOpAddr1) // allocate some rewards - initial := int64(30) - tokens := sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial)}} + initial := sdk.NewDecFromInt(staking.TokensFromTendermintPower(30)) + tokens := sdk.DecCoins{{staking.DefaultBondDenom, initial}} k.AllocateTokensToValidator(ctx, val, tokens) // slash the validator ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), 100, sdk.NewDecWithPrec(5, 1)) + sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power, sdk.NewDecWithPrec(5, 1)) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // second delegation - msg2 := staking.NewMsgDelegate(sdk.AccAddress(valOpAddr2), valOpAddr1, sdk.NewCoin(staking.DefaultBondDenom, sdk.NewInt(100))) + delTokens := staking.TokensFromTendermintPower(100) + msg2 := staking.NewMsgDelegate(sdk.AccAddress(valOpAddr2), valOpAddr1, + sdk.NewCoin(staking.DefaultBondDenom, delTokens)) require.True(t, sh(ctx, msg2).IsOK()) del2 := sk.Delegation(ctx, sdk.AccAddress(valOpAddr2), valOpAddr1) @@ -409,7 +429,7 @@ func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { // slash the validator again ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) - sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), 100, sdk.NewDecWithPrec(5, 1)) + sk.Slash(ctx, valConsAddr1, ctx.BlockHeight(), power, sdk.NewDecWithPrec(5, 1)) ctx = ctx.WithBlockHeight(ctx.BlockHeight() + 3) // fetch updated validator @@ -422,16 +442,16 @@ func TestCalculateRewardsMultiDelegatorMultiSlash(t *testing.T) { rewards := k.calculateDelegationRewards(ctx, val, del1, endingPeriod) // rewards for del1 should be 2/3 initial (half initial first period, 1/6 initial second period) - require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec((initial / 2) + (initial / 6))}}, rewards) + require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, initial.QuoInt64(2).Add(initial.QuoInt64(6))}}, rewards) // calculate delegation rewards for del2 rewards = k.calculateDelegationRewards(ctx, val, del2, endingPeriod) // rewards for del2 should be initial / 3 - require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial / 3)}}, rewards) + require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, initial.QuoInt64(3)}}, rewards) // commission should be equal to initial (twice 50% commission, unaffected by slashing) - require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, sdk.NewDec(initial)}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) + require.Equal(t, sdk.DecCoins{{staking.DefaultBondDenom, initial}}, k.GetValidatorAccumulatedCommission(ctx, valOpAddr1)) } func TestCalculateRewardsMultiDelegatorMultWithdraw(t *testing.T) { diff --git a/x/distribution/keeper/keeper_test.go b/x/distribution/keeper/keeper_test.go index 702946918..e30eb78f9 100644 --- a/x/distribution/keeper/keeper_test.go +++ b/x/distribution/keeper/keeper_test.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/distribution/types" + "github.com/cosmos/cosmos-sdk/x/staking" ) func TestSetWithdrawAddr(t *testing.T) { @@ -31,9 +32,10 @@ func TestWithdrawValidatorCommission(t *testing.T) { // check initial balance balance := ak.GetAccount(ctx, sdk.AccAddress(valOpAddr3)).GetCoins() - require.Equal(t, balance, sdk.Coins{ - {"stake", sdk.NewInt(1000)}, - }) + expTokens := staking.TokensFromTendermintPower(1000) + require.Equal(t, sdk.Coins{ + {"stake", staking.TokensFromTendermintPower(1000)}, + }, balance) // set commission keeper.SetValidatorAccumulatedCommission(ctx, valOpAddr3, sdk.DecCoins{ @@ -46,17 +48,17 @@ func TestWithdrawValidatorCommission(t *testing.T) { // check balance increase balance = ak.GetAccount(ctx, sdk.AccAddress(valOpAddr3)).GetCoins() - require.Equal(t, balance, sdk.Coins{ + require.Equal(t, sdk.Coins{ {"mytoken", sdk.NewInt(1)}, - {"stake", sdk.NewInt(1001)}, - }) + {"stake", expTokens.AddRaw(1)}, + }, balance) // check remainder remainder := keeper.GetValidatorAccumulatedCommission(ctx, valOpAddr3) - require.Equal(t, remainder, sdk.DecCoins{ + require.Equal(t, sdk.DecCoins{ {"mytoken", sdk.NewDec(1).Quo(sdk.NewDec(4))}, {"stake", sdk.NewDec(1).Quo(sdk.NewDec(2))}, - }) + }, remainder) require.True(t, true) } diff --git a/x/distribution/keeper/test_common.go b/x/distribution/keeper/test_common.go index 8e8b157c4..0a36910e8 100644 --- a/x/distribution/keeper/test_common.go +++ b/x/distribution/keeper/test_common.go @@ -71,18 +71,20 @@ func MakeTestCodec() *codec.Codec { } // test input with default values -func CreateTestInputDefault(t *testing.T, isCheckTx bool, initCoins int64) ( +func CreateTestInputDefault(t *testing.T, isCheckTx bool, initPower int64) ( sdk.Context, auth.AccountKeeper, Keeper, staking.Keeper, DummyFeeCollectionKeeper) { communityTax := sdk.NewDecWithPrec(2, 2) - return CreateTestInputAdvanced(t, isCheckTx, initCoins, communityTax) + return CreateTestInputAdvanced(t, isCheckTx, initPower, communityTax) } // hogpodge of all sorts of input required for testing -func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initCoins int64, +func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initPower int64, communityTax sdk.Dec) ( sdk.Context, auth.AccountKeeper, Keeper, staking.Keeper, DummyFeeCollectionKeeper) { + initCoins := staking.TokensFromTendermintPower(initPower) + keyDistr := sdk.NewKVStoreKey(types.StoreKey) keyStaking := sdk.NewKVStoreKey(staking.StoreKey) tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) @@ -119,10 +121,10 @@ func CreateTestInputAdvanced(t *testing.T, isCheckTx bool, initCoins int64, for _, addr := range addrs { pool := sk.GetPool(ctx) _, _, err := ck.AddCoins(ctx, addr, sdk.Coins{ - {sk.GetParams(ctx).BondDenom, sdk.NewInt(initCoins)}, + sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins), }) require.Nil(t, err) - pool.NotBondedTokens = pool.NotBondedTokens.Add(sdk.NewInt(initCoins)) + pool.NotBondedTokens = pool.NotBondedTokens.Add(initCoins) sk.SetPool(ctx, pool) } diff --git a/x/gov/endblocker_test.go b/x/gov/endblocker_test.go index 952f87f77..9ce46419e 100644 --- a/x/gov/endblocker_test.go +++ b/x/gov/endblocker_test.go @@ -9,7 +9,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/staking" ) func TestTickExpiredDepositPeriod(t *testing.T) { @@ -23,7 +23,7 @@ func TestTickExpiredDepositPeriod(t *testing.T) { require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 5)}) + newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) @@ -66,7 +66,7 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 5)}) + newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) @@ -83,7 +83,7 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newProposalMsg2 := NewMsgSubmitProposal("Test2", "test2", ProposalTypeText, addrs[1], sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 5)}) + newProposalMsg2 := NewMsgSubmitProposal("Test2", "test2", ProposalTypeText, addrs[1], sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, 5)}) res = govHandler(ctx, newProposalMsg2) require.True(t, res.IsOK()) @@ -126,7 +126,7 @@ func TestTickPassedDepositPeriod(t *testing.T) { require.False(t, activeQueue.Valid()) activeQueue.Close() - newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 5)}) + newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, 5)}) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) @@ -145,7 +145,7 @@ func TestTickPassedDepositPeriod(t *testing.T) { require.False(t, inactiveQueue.Valid()) inactiveQueue.Close() - newDepositMsg := NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 5)}) + newDepositMsg := NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, 5)}) res = govHandler(ctx, newDepositMsg) require.True(t, res.IsOK()) @@ -169,7 +169,8 @@ func TestTickPassedVotingPeriod(t *testing.T) { require.False(t, activeQueue.Valid()) activeQueue.Close() - newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 5)}) + proposalCoins := sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, staking.TokensFromTendermintPower(5))} + newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], proposalCoins) res := govHandler(ctx, newProposalMsg) require.True(t, res.IsOK()) @@ -180,7 +181,7 @@ func TestTickPassedVotingPeriod(t *testing.T) { newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) ctx = ctx.WithBlockHeader(newHeader) - newDepositMsg := NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 5)}) + newDepositMsg := NewMsgDeposit(addrs[1], proposalID, proposalCoins) res = govHandler(ctx, newDepositMsg) require.True(t, res.IsOK()) diff --git a/x/gov/genesis.go b/x/gov/genesis.go index a86085b22..36c699e19 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -5,7 +5,7 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/staking" ) // GenesisState - all staking state that must be provided at genesis @@ -42,10 +42,11 @@ func NewGenesisState(startingProposalID uint64, dp DepositParams, vp VotingParam // get raw genesis raw message for testing func DefaultGenesisState() GenesisState { + minDepositTokens := staking.TokensFromTendermintPower(10) return GenesisState{ StartingProposalID: 1, DepositParams: DepositParams{ - MinDeposit: sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 10)}, + MinDeposit: sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, minDepositTokens)}, MaxDepositPeriod: time.Duration(172800) * time.Second, }, VotingParams: VotingParams{ diff --git a/x/gov/keeper_test.go b/x/gov/keeper_test.go index f4308ef19..8fc2460ca 100644 --- a/x/gov/keeper_test.go +++ b/x/gov/keeper_test.go @@ -9,7 +9,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/staking" ) func TestGetSetProposal(t *testing.T) { @@ -70,15 +70,14 @@ func TestDeposits(t *testing.T) { proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() - fourSteak := sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 4)} - fiveSteak := sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 5)} + fourSteak := sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, staking.TokensFromTendermintPower(4))} + fiveSteak := sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, staking.TokensFromTendermintPower(5))} addr0Initial := keeper.ck.GetCoins(ctx, addrs[0]) addr1Initial := keeper.ck.GetCoins(ctx, addrs[1]) - // require.True(t, addr0Initial.IsEqual(sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 42)})) - require.Equal(t, sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 42)}, addr0Initial) - + expTokens := staking.TokensFromTendermintPower(42) + require.Equal(t, sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, expTokens)}, addr0Initial) require.True(t, proposal.GetTotalDeposit().IsEqual(sdk.Coins{})) // Check no deposits at beginning diff --git a/x/gov/msgs_test.go b/x/gov/msgs_test.go index a9fa88396..682aecdce 100644 --- a/x/gov/msgs_test.go +++ b/x/gov/msgs_test.go @@ -8,14 +8,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/mock" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/staking" ) var ( - coinsPos = sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 1000)} + coinsPos = sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, 1000)} coinsZero = sdk.Coins{} coinsPosNotAtoms = sdk.Coins{sdk.NewInt64Coin("foo", 10000)} - coinsMulti = sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 1000), sdk.NewInt64Coin("foo", 10000)} + coinsMulti = sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, 1000), sdk.NewInt64Coin("foo", 10000)} ) func init() { diff --git a/x/gov/proposals.go b/x/gov/proposals.go index e5dccfb02..e11400f1c 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -362,6 +362,24 @@ type TallyResult struct { NoWithVeto sdk.Dec `json:"no_with_veto"` } +func NewTallyResult(yes, abstain, no, noWithVeto sdk.Dec) TallyResult { + return TallyResult{ + Yes: yes, + Abstain: abstain, + No: no, + NoWithVeto: noWithVeto, + } +} + +func NewTallyResultFromMap(results map[VoteOption]sdk.Dec) TallyResult { + return TallyResult{ + Yes: results[OptionYes], + Abstain: results[OptionAbstain], + No: results[OptionNo], + NoWithVeto: results[OptionNoWithVeto], + } +} + // checks if two proposals are equal func EmptyTallyResult() TallyResult { return TallyResult{ diff --git a/x/gov/simulation/msgs.go b/x/gov/simulation/msgs.go index faca7ec8e..7c078bdcf 100644 --- a/x/gov/simulation/msgs.go +++ b/x/gov/simulation/msgs.go @@ -11,11 +11,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/mock/simulation" "github.com/cosmos/cosmos-sdk/x/staking" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" -) - -const ( - denom = stakingTypes.DefaultBondDenom ) // SimulateSubmittingVotingAndSlashingForProposal simulates creating a msg Submit Proposal @@ -183,7 +178,7 @@ func operationSimulateMsgVote(k gov.Keeper, acc simulation.Account, proposalID u func randomDeposit(r *rand.Rand) sdk.Coins { // TODO Choose based on account balance and min deposit amount := int64(r.Intn(20)) + 1 - return sdk.Coins{sdk.NewInt64Coin(denom, amount)} + return sdk.Coins{sdk.NewInt64Coin(staking.DefaultBondDenom, amount)} } // Pick a random proposal ID diff --git a/x/gov/tally.go b/x/gov/tally.go index 7877511d4..d6cac26f5 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -7,12 +7,24 @@ import ( // validatorGovInfo used for tallying type validatorGovInfo struct { Address sdk.ValAddress // address of the validator operator - Power sdk.Int // Power of a Validator + BondedTokens sdk.Int // Power of a Validator DelegatorShares sdk.Dec // Total outstanding delegator shares Minus sdk.Dec // Minus of validator, used to compute validator's voting power Vote VoteOption // Vote of the validator } +func newValidatorGovInfo(address sdk.ValAddress, bondedTokens sdk.Int, delegatorShares, + minus sdk.Dec, vote VoteOption) validatorGovInfo { + + return validatorGovInfo{ + Address: address, + BondedTokens: bondedTokens, + DelegatorShares: delegatorShares, + Minus: minus, + Vote: vote, + } +} + func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tallyResults TallyResult) { results := make(map[VoteOption]sdk.Dec) results[OptionYes] = sdk.ZeroDec() @@ -24,13 +36,13 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall currValidators := make(map[string]validatorGovInfo) keeper.vs.IterateBondedValidatorsByPower(ctx, func(index int64, validator sdk.Validator) (stop bool) { - currValidators[validator.GetOperator().String()] = validatorGovInfo{ - Address: validator.GetOperator(), - Power: validator.GetPower(), - DelegatorShares: validator.GetDelegatorShares(), - Minus: sdk.ZeroDec(), - Vote: OptionEmpty, - } + currValidators[validator.GetOperator().String()] = newValidatorGovInfo( + validator.GetOperator(), + validator.GetBondedTokens(), + validator.GetDelegatorShares(), + sdk.ZeroDec(), + OptionEmpty, + ) return false }) @@ -57,7 +69,7 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall currValidators[valAddrStr] = val delegatorShare := delegation.GetShares().Quo(val.DelegatorShares) - votingPower := delegatorShare.MulInt(val.Power) + votingPower := delegatorShare.MulInt(val.BondedTokens) results[vote.Option] = results[vote.Option].Add(votingPower) totalVotingPower = totalVotingPower.Add(votingPower) @@ -78,20 +90,14 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall sharesAfterMinus := val.DelegatorShares.Sub(val.Minus) percentAfterMinus := sharesAfterMinus.Quo(val.DelegatorShares) - votingPower := percentAfterMinus.MulInt(val.Power) + votingPower := percentAfterMinus.MulInt(val.BondedTokens) results[val.Vote] = results[val.Vote].Add(votingPower) totalVotingPower = totalVotingPower.Add(votingPower) } tallyParams := keeper.GetTallyParams(ctx) - - tallyResults = TallyResult{ - Yes: results[OptionYes], - Abstain: results[OptionAbstain], - No: results[OptionNo], - NoWithVeto: results[OptionNoWithVeto], - } + tallyResults = NewTallyResultFromMap(results) // If there is no staked coins, the proposal fails if keeper.vs.TotalPower(ctx).IsZero() { diff --git a/x/gov/tally_test.go b/x/gov/tally_test.go index 77a22996b..f2dbe7960 100644 --- a/x/gov/tally_test.go +++ b/x/gov/tally_test.go @@ -10,9 +10,7 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/staking" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( @@ -22,12 +20,15 @@ var ( testCommissionMsg = staking.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) ) -func createValidators(t *testing.T, stakingHandler sdk.Handler, ctx sdk.Context, addrs []sdk.ValAddress, coinAmt []int64) { +func createValidators(t *testing.T, stakingHandler sdk.Handler, ctx sdk.Context, addrs []sdk.ValAddress, powerAmt []int64) { require.True(t, len(addrs) <= len(pubkeys), "Not enough pubkeys specified at top of file.") for i := 0; i < len(addrs); i++ { + + valTokens := staking.TokensFromTendermintPower(powerAmt[i]) valCreateMsg := staking.NewMsgCreateValidator( - addrs[i], pubkeys[i], sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, coinAmt[i]), testDescription, testCommissionMsg, + addrs[i], pubkeys[i], sdk.NewCoin(staking.DefaultBondDenom, valTokens), + testDescription, testCommissionMsg, ) res := stakingHandler(ctx, valCreateMsg) @@ -317,7 +318,8 @@ func TestTallyDelgatorOverride(t *testing.T) { createValidators(t, stakingHandler, ctx, valAddrs, []int64{5, 6, 7}) staking.EndBlocker(ctx, sk) - delegator1Msg := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 30)) + delTokens := staking.TokensFromTendermintPower(30) + delegator1Msg := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewCoin(staking.DefaultBondDenom, delTokens)) stakingHandler(ctx, delegator1Msg) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) @@ -354,7 +356,8 @@ func TestTallyDelgatorInherit(t *testing.T) { createValidators(t, stakingHandler, ctx, valAddrs, []int64{5, 6, 7}) staking.EndBlocker(ctx, sk) - delegator1Msg := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 30)) + delTokens := staking.TokensFromTendermintPower(30) + delegator1Msg := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewCoin(staking.DefaultBondDenom, delTokens)) stakingHandler(ctx, delegator1Msg) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) @@ -389,9 +392,10 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) { createValidators(t, stakingHandler, ctx, valAddrs, []int64{5, 6, 7}) staking.EndBlocker(ctx, sk) - delegator1Msg := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 10)) + delTokens := staking.TokensFromTendermintPower(10) + delegator1Msg := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewCoin(staking.DefaultBondDenom, delTokens)) stakingHandler(ctx, delegator1Msg) - delegator1Msg2 := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 10)) + delegator1Msg2 := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewCoin(staking.DefaultBondDenom, delTokens)) stakingHandler(ctx, delegator1Msg2) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) @@ -420,25 +424,29 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) { ctx := mapp.BaseApp.NewContext(false, abci.Header{}) stakingHandler := staking.NewHandler(sk) + valTokens1 := staking.TokensFromTendermintPower(25) val1CreateMsg := staking.NewMsgCreateValidator( - sdk.ValAddress(addrs[0]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 25), testDescription, testCommissionMsg, + sdk.ValAddress(addrs[0]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(staking.DefaultBondDenom, valTokens1), testDescription, testCommissionMsg, ) stakingHandler(ctx, val1CreateMsg) + valTokens2 := staking.TokensFromTendermintPower(6) val2CreateMsg := staking.NewMsgCreateValidator( - sdk.ValAddress(addrs[1]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 6), testDescription, testCommissionMsg, + sdk.ValAddress(addrs[1]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(staking.DefaultBondDenom, valTokens2), testDescription, testCommissionMsg, ) stakingHandler(ctx, val2CreateMsg) + valTokens3 := staking.TokensFromTendermintPower(7) val3CreateMsg := staking.NewMsgCreateValidator( - sdk.ValAddress(addrs[2]), ed25519.GenPrivKey().PubKey(), sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 7), testDescription, testCommissionMsg, + sdk.ValAddress(addrs[2]), ed25519.GenPrivKey().PubKey(), sdk.NewCoin(staking.DefaultBondDenom, valTokens3), testDescription, testCommissionMsg, ) stakingHandler(ctx, val3CreateMsg) - delegator1Msg := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 10)) + delTokens := staking.TokensFromTendermintPower(10) + delegator1Msg := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewCoin(staking.DefaultBondDenom, delTokens)) stakingHandler(ctx, delegator1Msg) - delegator1Msg2 := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 10)) + delegator1Msg2 := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewCoin(staking.DefaultBondDenom, delTokens)) stakingHandler(ctx, delegator1Msg2) staking.EndBlocker(ctx, sk) @@ -475,10 +483,11 @@ func TestTallyJailedValidator(t *testing.T) { createValidators(t, stakingHandler, ctx, valAddrs, []int64{25, 6, 7}) staking.EndBlocker(ctx, sk) - delegator1Msg := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 10)) + delTokens := staking.TokensFromTendermintPower(10) + delegator1Msg := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[2]), sdk.NewCoin(staking.DefaultBondDenom, delTokens)) stakingHandler(ctx, delegator1Msg) - delegator1Msg2 := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 10)) + delegator1Msg2 := staking.NewMsgDelegate(addrs[3], sdk.ValAddress(addrs[1]), sdk.NewCoin(staking.DefaultBondDenom, delTokens)) stakingHandler(ctx, delegator1Msg2) val2, found := sk.GetValidator(ctx, sdk.ValAddress(addrs[1])) diff --git a/x/gov/test_common.go b/x/gov/test_common.go index 34a507c26..ee3b95850 100644 --- a/x/gov/test_common.go +++ b/x/gov/test_common.go @@ -16,11 +16,14 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" "github.com/cosmos/cosmos-sdk/x/staking" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) // initialize the mock application for this module -func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []auth.Account) (mapp *mock.App, keeper Keeper, sk staking.Keeper, addrs []sdk.AccAddress, pubKeys []crypto.PubKey, privKeys []crypto.PrivKey) { +func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []auth.Account) ( + mapp *mock.App, keeper Keeper, sk staking.Keeper, addrs []sdk.AccAddress, + pubKeys []crypto.PubKey, privKeys []crypto.PrivKey) { + mapp = mock.NewApp() staking.RegisterCodec(mapp.Cdc) @@ -43,8 +46,10 @@ func getMockApp(t *testing.T, numGenAccs int, genState GenesisState, genAccs []a require.NoError(t, mapp.CompleteSetup(keyStaking, tkeyStaking, keyGov)) + valTokens := staking.TokensFromTendermintPower(42) if genAccs == nil || len(genAccs) == 0 { - genAccs, addrs, pubKeys, privKeys = mock.CreateGenAccounts(numGenAccs, sdk.Coins{sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 42)}) + genAccs, addrs, pubKeys, privKeys = mock.CreateGenAccounts(numGenAccs, + sdk.Coins{sdk.NewCoin(staking.DefaultBondDenom, valTokens)}) } mock.SetGenesis(mapp, genAccs) @@ -68,7 +73,8 @@ func getInitChainer(mapp *mock.App, keeper Keeper, stakingKeeper staking.Keeper, mapp.InitChainer(ctx, req) stakingGenesis := staking.DefaultGenesisState() - stakingGenesis.Pool.NotBondedTokens = sdk.NewInt(100000) + tokens := types.TokensFromTendermintPower(100000) + stakingGenesis.Pool.NotBondedTokens = tokens validators, err := staking.InitGenesis(ctx, stakingKeeper, stakingGenesis) if err != nil { diff --git a/x/mint/params.go b/x/mint/params.go index 45a66f7fc..62018397d 100644 --- a/x/mint/params.go +++ b/x/mint/params.go @@ -3,9 +3,8 @@ package mint import ( "fmt" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/staking" ) // mint parameters @@ -34,7 +33,7 @@ func NewParams(mintDenom string, inflationRateChange, inflationMax, // default minting module parameters func DefaultParams() Params { return Params{ - MintDenom: stakingTypes.DefaultBondDenom, + MintDenom: staking.DefaultBondDenom, InflationRateChange: sdk.NewDecWithPrec(13, 2), InflationMax: sdk.NewDecWithPrec(20, 2), InflationMin: sdk.NewDecWithPrec(7, 2), diff --git a/x/mock/app.go b/x/mock/app.go index 902873ba1..117afa668 100644 --- a/x/mock/app.go +++ b/x/mock/app.go @@ -59,10 +59,10 @@ func NewApp() *App { Cdc: cdc, KeyMain: sdk.NewKVStoreKey(bam.MainStoreKey), KeyAccount: sdk.NewKVStoreKey(auth.StoreKey), - TotalCoinsSupply: sdk.Coins{}, KeyFeeCollection: sdk.NewKVStoreKey("fee"), KeyParams: sdk.NewKVStoreKey("params"), TKeyParams: sdk.NewTransientStoreKey("transient_params"), + TotalCoinsSupply: sdk.Coins{}, } app.ParamsKeeper = params.NewKeeper(app.Cdc, app.KeyParams, app.TKeyParams) @@ -135,6 +135,16 @@ type AddrKeys struct { PrivKey crypto.PrivKey } +func NewAddrKeys(address sdk.AccAddress, pubKey crypto.PubKey, + privKey crypto.PrivKey) AddrKeys { + + return AddrKeys{ + Address: address, + PubKey: pubKey, + PrivKey: privKey, + } +} + // implement `Interface` in sort package. type AddrKeysSlice []AddrKeys @@ -161,7 +171,9 @@ func (b AddrKeysSlice) Swap(i, j int) { // CreateGenAccounts generates genesis accounts loaded with coins, and returns // their addresses, pubkeys, and privkeys. -func CreateGenAccounts(numAccs int, genCoins sdk.Coins) (genAccs []auth.Account, addrs []sdk.AccAddress, pubKeys []crypto.PubKey, privKeys []crypto.PrivKey) { +func CreateGenAccounts(numAccs int, genCoins sdk.Coins) (genAccs []auth.Account, + addrs []sdk.AccAddress, pubKeys []crypto.PubKey, privKeys []crypto.PrivKey) { + addrKeysSlice := AddrKeysSlice{} for i := 0; i < numAccs; i++ { @@ -169,11 +181,7 @@ func CreateGenAccounts(numAccs int, genCoins sdk.Coins) (genAccs []auth.Account, pubKey := privKey.PubKey() addr := sdk.AccAddress(pubKey.Address()) - addrKeysSlice = append(addrKeysSlice, AddrKeys{ - Address: addr, - PubKey: pubKey, - PrivKey: privKey, - }) + addrKeysSlice = append(addrKeysSlice, NewAddrKeys(addr, pubKey, privKey)) } sort.Sort(addrKeysSlice) diff --git a/x/mock/test_utils.go b/x/mock/test_utils.go index f2af216e1..9032d1f3d 100644 --- a/x/mock/test_utils.go +++ b/x/mock/test_utils.go @@ -72,10 +72,10 @@ func CheckGenTx( // block commitment with the given transaction. A test assertion is made using // the parameter 'expPass' against the result. A corresponding result is // returned. -func SignCheckDeliver( - t *testing.T, cdc *codec.Codec, app *baseapp.BaseApp, msgs []sdk.Msg, - accNums []uint64, seq []uint64, expSimPass, expPass bool, priv ...crypto.PrivKey, -) sdk.Result { +func SignCheckDeliver(t *testing.T, cdc *codec.Codec, app *baseapp.BaseApp, + msgs []sdk.Msg, accNums, seq []uint64, expSimPass, expPass bool, + priv ...crypto.PrivKey) sdk.Result { + tx := GenTx(msgs, accNums, seq, priv...) txBytes, err := cdc.MarshalBinaryLengthPrefixed(tx) diff --git a/x/slashing/app_test.go b/x/slashing/app_test.go index 20e538080..6a77f4464 100644 --- a/x/slashing/app_test.go +++ b/x/slashing/app_test.go @@ -12,7 +12,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" "github.com/cosmos/cosmos-sdk/x/staking" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) var ( @@ -26,7 +25,7 @@ func getMockApp(t *testing.T) (*mock.App, staking.Keeper, Keeper) { mapp := mock.NewApp() RegisterCodec(mapp.Cdc) - stakingTypes.RegisterCodec(mapp.Cdc) + staking.RegisterCodec(mapp.Cdc) keyStaking := sdk.NewKVStoreKey(staking.StoreKey) tkeyStaking := sdk.NewTransientStoreKey(staking.TStoreKey) @@ -62,7 +61,8 @@ func getInitChainer(mapp *mock.App, keeper staking.Keeper) sdk.InitChainer { return func(ctx sdk.Context, req abci.RequestInitChain) abci.ResponseInitChain { mapp.InitChainer(ctx, req) stakingGenesis := staking.DefaultGenesisState() - stakingGenesis.Pool.NotBondedTokens = sdk.NewInt(100000) + tokens := staking.TokensFromTendermintPower(100000) + stakingGenesis.Pool.NotBondedTokens = tokens validators, err := staking.InitGenesis(ctx, keeper, stakingGenesis) if err != nil { panic(err) @@ -93,8 +93,10 @@ func checkValidatorSigningInfo(t *testing.T, mapp *mock.App, keeper Keeper, func TestSlashingMsgs(t *testing.T) { mapp, stakingKeeper, keeper := getMockApp(t) - genCoin := sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 42) - bondCoin := sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 10) + genTokens := staking.TokensFromTendermintPower(42) + bondTokens := staking.TokensFromTendermintPower(10) + genCoin := sdk.NewCoin(staking.DefaultBondDenom, genTokens) + bondCoin := sdk.NewCoin(staking.DefaultBondDenom, bondTokens) acc1 := &auth.BaseAccount{ Address: addr1, @@ -116,7 +118,7 @@ func TestSlashingMsgs(t *testing.T) { validator := checkValidator(t, mapp, stakingKeeper, addr1, true) require.Equal(t, sdk.ValAddress(addr1), validator.OperatorAddr) require.Equal(t, sdk.Bonded, validator.Status) - require.True(sdk.IntEq(t, sdk.NewInt(10), validator.BondedTokens())) + require.True(sdk.IntEq(t, bondTokens, validator.BondedTokens())) unjailMsg := MsgUnjail{ValidatorAddr: sdk.ValAddress(validator.ConsPubKey.Address())} // no signing info yet diff --git a/x/slashing/handler_test.go b/x/slashing/handler_test.go index a363a497b..f57dead00 100644 --- a/x/slashing/handler_test.go +++ b/x/slashing/handler_test.go @@ -8,24 +8,25 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestCannotUnjailUnlessJailed(t *testing.T) { // initial setup ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams()) slh := NewHandler(keeper) - amtInt := int64(100) - addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) + amt := types.TokensFromTendermintPower(100) + addr, val := addrs[0], pks[0] msg := NewTestMsgCreateValidator(addr, val, amt) got := staking.NewHandler(sk)(ctx, msg) - require.True(t, got.IsOK()) + require.True(t, got.IsOK(), "%v", got) staking.EndBlocker(ctx, sk) require.Equal( t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))}, ) - require.True(sdk.IntEq(t, amt, sk.Validator(ctx, addr).GetPower())) + require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) // assert non-jailed validator can't be unjailed got = slh(ctx, NewMsgUnjail(addr)) @@ -42,8 +43,8 @@ func TestJailedValidatorDelegations(t *testing.T) { stakingKeeper.SetParams(ctx, stakingParams) // create a validator - amount := int64(10) - valPubKey, bondAmount := pks[0], sdk.NewInt(amount) + bondAmount := staking.TokensFromTendermintPower(10) + valPubKey := pks[0] valAddr, consAddr := addrs[1], sdk.ConsAddress(addrs[0]) msgCreateVal := NewTestMsgCreateValidator(valAddr, valPubKey, bondAmount) @@ -54,12 +55,7 @@ func TestJailedValidatorDelegations(t *testing.T) { staking.EndBlocker(ctx, stakingKeeper) // set dummy signing info - newInfo := ValidatorSigningInfo{ - StartHeight: int64(0), - IndexOffset: int64(0), - JailedUntil: time.Unix(0, 0), - MissedBlocksCounter: int64(0), - } + newInfo := NewValidatorSigningInfo(0, 0, time.Unix(0, 0), false, 0) slashingKeeper.SetValidatorSigningInfo(ctx, consAddr, newInfo) // delegate tokens to the validator @@ -68,7 +64,7 @@ func TestJailedValidatorDelegations(t *testing.T) { got = staking.NewHandler(stakingKeeper)(ctx, msgDelegate) require.True(t, got.IsOK(), "expected delegation to be ok, got %v", got) - unbondShares := sdk.NewDec(10) + unbondShares := sdk.NewDecFromInt(bondAmount) // unbond validator total self-delegations (which should jail the validator) msgUndelegate := staking.NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondShares) diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index aa0e1c5d0..bf7c5a18b 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -9,7 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/params" - staking "github.com/cosmos/cosmos-sdk/x/staking/types" + staking "github.com/cosmos/cosmos-sdk/x/staking" ) // Keeper of the slashing store @@ -49,7 +49,8 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio // Reject evidence if the double is too old if age > k.MaxEvidenceAge(ctx) { - logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, age of %d past max age of %d", pubkey.Address(), infractionHeight, age, k.MaxEvidenceAge(ctx))) + logger.Info(fmt.Sprintf("Ignored double sign from %s at height %d, age of %d past max age of %d", + pubkey.Address(), infractionHeight, age, k.MaxEvidenceAge(ctx))) return } diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index 9c4ead9d4..f43dccac8 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -9,6 +9,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) // Have to change these parameters for tests @@ -30,8 +31,9 @@ func TestHandleDoubleSign(t *testing.T) { ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams()) // validator added pre-genesis ctx = ctx.WithBlockHeight(-1) - amtInt := int64(100) - operatorAddr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) + power := int64(100) + amt := staking.TokensFromTendermintPower(power) + operatorAddr, val := addrs[0], pks[0] got := staking.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt)) require.True(t, got.IsOK()) staking.EndBlocker(ctx, sk) @@ -39,15 +41,15 @@ func TestHandleDoubleSign(t *testing.T) { t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))}, ) - require.True(sdk.IntEq(t, amt, sk.Validator(ctx, operatorAddr).GetPower())) + require.Equal(t, amt, sk.Validator(ctx, operatorAddr).GetBondedTokens()) // handle a signature to set signing info - keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true) + keeper.handleValidatorSignature(ctx, val.Address(), amt.Int64(), true) oldTokens := sk.Validator(ctx, operatorAddr).GetTokens() // double sign less than max age - keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt) + keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), power) // should be jailed require.True(t, sk.Validator(ctx, operatorAddr).GetJailed()) @@ -57,7 +59,7 @@ func TestHandleDoubleSign(t *testing.T) { require.True(t, newTokens.LT(oldTokens)) // New evidence - keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt) + keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), power) // tokens should be the same (capped slash) require.True(t, sk.Validator(ctx, operatorAddr).GetTokens().Equal(newTokens)) @@ -87,8 +89,9 @@ func TestPastMaxEvidenceAge(t *testing.T) { ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams()) // validator added pre-genesis ctx = ctx.WithBlockHeight(-1) - amtInt := int64(100) - operatorAddr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) + power := int64(100) + amt := staking.TokensFromTendermintPower(power) + operatorAddr, val := addrs[0], pks[0] got := staking.NewHandler(sk)(ctx, NewTestMsgCreateValidator(operatorAddr, val, amt)) require.True(t, got.IsOK()) staking.EndBlocker(ctx, sk) @@ -96,23 +99,23 @@ func TestPastMaxEvidenceAge(t *testing.T) { t, ck.GetCoins(ctx, sdk.AccAddress(operatorAddr)), sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))}, ) - require.True(sdk.IntEq(t, amt, sk.Validator(ctx, operatorAddr).GetPower())) + require.Equal(t, amt, sk.Validator(ctx, operatorAddr).GetBondedTokens()) // handle a signature to set signing info - keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true) + keeper.handleValidatorSignature(ctx, val.Address(), power, true) ctx = ctx.WithBlockHeader(abci.Header{Time: time.Unix(1, 0).Add(keeper.MaxEvidenceAge(ctx))}) - oldPower := sk.Validator(ctx, operatorAddr).GetPower() + oldPower := sk.Validator(ctx, operatorAddr).GetTendermintPower() // double sign past max age - keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt) + keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), power) // should still be bonded require.True(t, sk.Validator(ctx, operatorAddr).GetStatus() == sdk.Bonded) // should still have same power - require.True(t, sk.Validator(ctx, operatorAddr).GetPower().Equal(oldPower)) + require.Equal(t, oldPower, sk.Validator(ctx, operatorAddr).GetTendermintPower()) } // Test a validator through uptime, downtime, revocation, @@ -121,8 +124,9 @@ func TestHandleAbsentValidator(t *testing.T) { // initial setup ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams()) - amtInt64 := int64(100) - addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt64) + power := int64(100) + amt := staking.TokensFromTendermintPower(power) + addr, val := addrs[0], pks[0] sh := staking.NewHandler(sk) slh := NewHandler(keeper) got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) @@ -133,7 +137,7 @@ func TestHandleAbsentValidator(t *testing.T) { t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))}, ) - require.True(sdk.IntEq(t, amt, sk.Validator(ctx, addr).GetPower())) + require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) // will exist since the validator has been bonded info, found := keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) @@ -147,7 +151,7 @@ func TestHandleAbsentValidator(t *testing.T) { // 1000 first blocks OK for ; height < keeper.SignedBlocksWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), amtInt64, true) + keeper.handleValidatorSignature(ctx, val.Address(), power, true) } info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) @@ -157,7 +161,7 @@ func TestHandleAbsentValidator(t *testing.T) { // 500 blocks missed for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)); height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), amtInt64, false) + keeper.handleValidatorSignature(ctx, val.Address(), power, false) } info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) @@ -172,7 +176,7 @@ func TestHandleAbsentValidator(t *testing.T) { // 501st block missed ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), amtInt64, false) + keeper.handleValidatorSignature(ctx, val.Address(), power, false) info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) @@ -186,15 +190,15 @@ func TestHandleAbsentValidator(t *testing.T) { validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Unbonding, validator.GetStatus()) - slashAmt := sdk.NewDec(amtInt64).Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64() + slashAmt := sdk.NewDecFromInt(amt).Mul(keeper.SlashFractionDowntime(ctx)).RoundInt64() // validator should have been slashed - require.Equal(t, amtInt64-slashAmt, validator.GetTokens().Int64()) + require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64()) // 502nd block *also* missed (since the LastCommit would have still included the just-unbonded validator) height++ ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), amtInt64, false) + keeper.handleValidatorSignature(ctx, val.Address(), power, false) info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) require.True(t, found) require.Equal(t, int64(0), info.StartHeight) @@ -205,7 +209,7 @@ func TestHandleAbsentValidator(t *testing.T) { // validator should not have been slashed any more, since it was already jailed validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, amtInt64-slashAmt, validator.GetTokens().Int64()) + require.Equal(t, amt.Int64()-slashAmt, validator.GetTokens().Int64()) // unrevocation should fail prior to jail expiration got = slh(ctx, NewMsgUnjail(addr)) @@ -225,7 +229,7 @@ func TestHandleAbsentValidator(t *testing.T) { // validator should have been slashed pool = sk.GetPool(ctx) - require.Equal(t, amtInt64-slashAmt, pool.BondedTokens.Int64()) + require.Equal(t, amt.Int64()-slashAmt, pool.BondedTokens.Int64()) // Validator start height should not have been changed info, found = keeper.getValidatorSigningInfo(ctx, sdk.ConsAddress(val.Address())) @@ -237,7 +241,7 @@ func TestHandleAbsentValidator(t *testing.T) { // validator should not be immediately jailed again height++ ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), amtInt64, false) + keeper.handleValidatorSignature(ctx, val.Address(), power, false) validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Bonded, validator.GetStatus()) @@ -245,7 +249,7 @@ func TestHandleAbsentValidator(t *testing.T) { nextHeight := height + keeper.MinSignedPerWindow(ctx) + 1 for ; height < nextHeight; height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), amtInt64, false) + keeper.handleValidatorSignature(ctx, val.Address(), power, false) } // end block @@ -255,7 +259,7 @@ func TestHandleAbsentValidator(t *testing.T) { nextHeight = height + keeper.MinSignedPerWindow(ctx) + 1 for ; height <= nextHeight; height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), amtInt64, false) + keeper.handleValidatorSignature(ctx, val.Address(), power, false) } // end block @@ -271,22 +275,23 @@ func TestHandleAbsentValidator(t *testing.T) { func TestHandleNewValidator(t *testing.T) { // initial setup ctx, ck, sk, _, keeper := createTestInput(t, keeperTestParams()) - addr, val, amt := addrs[0], pks[0], int64(100) + addr, val := addrs[0], pks[0] + amt := staking.TokensFromTendermintPower(100) sh := staking.NewHandler(sk) // 1000 first blocks not a validator ctx = ctx.WithBlockHeight(keeper.SignedBlocksWindow(ctx) + 1) // Validator created - got := sh(ctx, NewTestMsgCreateValidator(addr, val, sdk.NewInt(amt))) + got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) staking.EndBlocker(ctx, sk) require.Equal( t, ck.GetCoins(ctx, sdk.AccAddress(addr)), - sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.SubRaw(amt))}, + sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))}, ) - require.Equal(t, amt, sk.Validator(ctx, addr).GetPower().Int64()) + require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) // Now a validator, for two blocks keeper.handleValidatorSignature(ctx, val.Address(), 100, true) @@ -304,7 +309,8 @@ func TestHandleNewValidator(t *testing.T) { validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Bonded, validator.GetStatus()) pool := sk.GetPool(ctx) - require.Equal(t, int64(100), pool.BondedTokens.Int64()) + expTokens := staking.TokensFromTendermintPower(100) + require.Equal(t, expTokens, pool.BondedTokens) } // Test a jailed validator being "down" twice @@ -313,8 +319,9 @@ func TestHandleAlreadyJailed(t *testing.T) { // initial setup ctx, _, sk, _, keeper := createTestInput(t, DefaultParams()) - amtInt := int64(100) - addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) + power := int64(100) + amt := staking.TokensFromTendermintPower(power) + addr, val := addrs[0], pks[0] sh := staking.NewHandler(sk) got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) require.True(t, got.IsOK()) @@ -324,13 +331,13 @@ func TestHandleAlreadyJailed(t *testing.T) { height := int64(0) for ; height < keeper.SignedBlocksWindow(ctx); height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true) + keeper.handleValidatorSignature(ctx, val.Address(), power, true) } // 501 blocks missed for ; height < keeper.SignedBlocksWindow(ctx)+(keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx))+1; height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) + keeper.handleValidatorSignature(ctx, val.Address(), power, false) } // end block @@ -341,15 +348,16 @@ func TestHandleAlreadyJailed(t *testing.T) { require.Equal(t, sdk.Unbonding, validator.GetStatus()) // validator should have been slashed - require.Equal(t, amtInt-1, validator.GetTokens().Int64()) + resultingTokens := amt.Sub(staking.TokensFromTendermintPower(1)) + require.Equal(t, resultingTokens, validator.GetTokens()) // another block missed ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) + keeper.handleValidatorSignature(ctx, val.Address(), power, false) // validator should not have been slashed twice validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) - require.Equal(t, amtInt-1, validator.GetTokens().Int64()) + require.Equal(t, resultingTokens, validator.GetTokens()) } @@ -364,8 +372,9 @@ func TestValidatorDippingInAndOut(t *testing.T) { params := sk.GetParams(ctx) params.MaxValidators = 1 sk.SetParams(ctx, params) - amtInt := int64(100) - addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) + power := int64(100) + amt := staking.TokensFromTendermintPower(power) + addr, val := addrs[0], pks[0] consAddr := sdk.ConsAddress(addr) sh := staking.NewHandler(sk) got := sh(ctx, NewTestMsgCreateValidator(addr, val, amt)) @@ -376,12 +385,12 @@ func TestValidatorDippingInAndOut(t *testing.T) { height := int64(0) for ; height < int64(100); height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true) + keeper.handleValidatorSignature(ctx, val.Address(), power, true) } // validator kicked out of validator set - newAmt := int64(101) - got = sh(ctx, NewTestMsgCreateValidator(addrs[1], pks[1], sdk.NewInt(newAmt))) + newAmt := staking.TokensFromTendermintPower(101) + got = sh(ctx, NewTestMsgCreateValidator(addrs[1], pks[1], newAmt)) require.True(t, got.IsOK()) validatorUpdates, _ := staking.EndBlocker(ctx, sk) require.Equal(t, 2, len(validatorUpdates)) @@ -393,16 +402,17 @@ func TestValidatorDippingInAndOut(t *testing.T) { ctx = ctx.WithBlockHeight(height) // validator added back in - got = sh(ctx, newTestMsgDelegate(sdk.AccAddress(addrs[2]), addrs[0], sdk.NewInt(3))) + delTokens := types.TokensFromTendermintPower(3) + got = sh(ctx, newTestMsgDelegate(sdk.AccAddress(addrs[2]), addrs[0], delTokens)) require.True(t, got.IsOK()) validatorUpdates, _ = staking.EndBlocker(ctx, sk) require.Equal(t, 2, len(validatorUpdates)) validator, _ = sk.GetValidator(ctx, addr) require.Equal(t, sdk.Bonded, validator.Status) - newAmt = int64(103) + newPower := int64(103) // validator misses a block - keeper.handleValidatorSignature(ctx, val.Address(), newAmt, false) + keeper.handleValidatorSignature(ctx, val.Address(), newPower, false) height++ // shouldn't be jailed/kicked yet @@ -413,7 +423,7 @@ func TestValidatorDippingInAndOut(t *testing.T) { latest := height for ; height < latest+500; height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), newAmt, false) + keeper.handleValidatorSignature(ctx, val.Address(), newPower, false) } // should now be jailed & kicked @@ -438,7 +448,7 @@ func TestValidatorDippingInAndOut(t *testing.T) { // validator rejoins and starts signing again sk.Unjail(ctx, consAddr) - keeper.handleValidatorSignature(ctx, val.Address(), newAmt, true) + keeper.handleValidatorSignature(ctx, val.Address(), newPower, true) height++ // validator should not be kicked since we reset counter/array when it was jailed @@ -450,7 +460,7 @@ func TestValidatorDippingInAndOut(t *testing.T) { latest = height for ; height < latest+501; height++ { ctx = ctx.WithBlockHeight(height) - keeper.handleValidatorSignature(ctx, val.Address(), newAmt, false) + keeper.handleValidatorSignature(ctx, val.Address(), newPower, false) } // validator should now be jailed & kicked diff --git a/x/slashing/params.go b/x/slashing/params.go index 1b500882c..e5d68fbcd 100644 --- a/x/slashing/params.go +++ b/x/slashing/params.go @@ -81,11 +81,13 @@ func DefaultParams() Params { // TODO Temporarily set to 10 minutes for testnets DowntimeJailDuration: 60 * 10 * time.Second, + // CONTRACT must be less than 1 + // TODO enforce this contract https://github.com/cosmos/cosmos-sdk/issues/3474 MinSignedPerWindow: sdk.NewDecWithPrec(5, 1), - SlashFractionDoubleSign: sdk.NewDec(1).Quo(sdk.NewDec(20)), + SlashFractionDoubleSign: sdk.NewDec(1).QuoInt64(20), - SlashFractionDowntime: sdk.NewDec(1).Quo(sdk.NewDec(100)), + SlashFractionDowntime: sdk.NewDec(1).QuoInt64(100), } } @@ -107,7 +109,10 @@ func (k Keeper) MinSignedPerWindow(ctx sdk.Context) int64 { var minSignedPerWindow sdk.Dec k.paramspace.Get(ctx, KeyMinSignedPerWindow, &minSignedPerWindow) signedBlocksWindow := k.SignedBlocksWindow(ctx) - return sdk.NewDec(signedBlocksWindow).Mul(minSignedPerWindow).RoundInt64() + + // NOTE: RoundInt64 will never panic as minSignedPerWindow is + // less than 1. + return minSignedPerWindow.MulInt64(signedBlocksWindow).RoundInt64() } // Downtime unbond duration diff --git a/x/slashing/signing_info.go b/x/slashing/signing_info.go index 26c9501f5..517304f1a 100644 --- a/x/slashing/signing_info.go +++ b/x/slashing/signing_info.go @@ -90,17 +90,6 @@ func (k Keeper) clearValidatorMissedBlockBitArray(ctx sdk.Context, address sdk.C } } -// Construct a new `ValidatorSigningInfo` struct -func NewValidatorSigningInfo(startHeight int64, indexOffset int64, jailedUntil time.Time, tombstoned bool, missedBlocksCounter int64) ValidatorSigningInfo { - return ValidatorSigningInfo{ - StartHeight: startHeight, - IndexOffset: indexOffset, - JailedUntil: jailedUntil, - Tombstoned: tombstoned, - MissedBlocksCounter: missedBlocksCounter, - } -} - // Signing info for a validator type ValidatorSigningInfo struct { StartHeight int64 `json:"start_height"` // height at which validator was first a candidate OR was unjailed @@ -110,6 +99,19 @@ type ValidatorSigningInfo struct { MissedBlocksCounter int64 `json:"missed_blocks_counter"` // missed blocks counter (to avoid scanning the array every time) } +// Construct a new `ValidatorSigningInfo` struct +func NewValidatorSigningInfo(startHeight, indexOffset int64, jailedUntil time.Time, + tombstoned bool, missedBlocksCounter int64) ValidatorSigningInfo { + + return ValidatorSigningInfo{ + StartHeight: startHeight, + IndexOffset: indexOffset, + JailedUntil: jailedUntil, + Tombstoned: tombstoned, + MissedBlocksCounter: missedBlocksCounter, + } +} + // Return human readable signing info func (i ValidatorSigningInfo) String() string { return fmt.Sprintf(`Start Height: %d diff --git a/x/slashing/test_common.go b/x/slashing/test_common.go index 428961854..93d12d771 100644 --- a/x/slashing/test_common.go +++ b/x/slashing/test_common.go @@ -21,7 +21,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/params" "github.com/cosmos/cosmos-sdk/x/staking" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // TODO remove dependencies on staking (should only refer to validator set type from sdk) @@ -37,7 +36,7 @@ var ( sdk.ValAddress(pks[1].Address()), sdk.ValAddress(pks[2].Address()), } - initCoins = sdk.NewInt(200) + initCoins = staking.TokensFromTendermintPower(200) ) func createTestCodec() *codec.Codec { @@ -76,7 +75,7 @@ func createTestInput(t *testing.T, defaults Params) (sdk.Context, bank.Keeper, s sk := staking.NewKeeper(cdc, keyStaking, tkeyStaking, ck, paramsKeeper.Subspace(staking.DefaultParamspace), staking.DefaultCodespace) genesis := staking.DefaultGenesisState() - genesis.Pool.NotBondedTokens = sdk.NewInt(initCoins.MulRaw(int64(len(addrs))).Int64()) + genesis.Pool.NotBondedTokens = initCoins.MulRaw(int64(len(addrs))) _, err = staking.InitGenesis(ctx, sk, genesis) require.Nil(t, err) @@ -116,7 +115,7 @@ func testAddr(addr string) sdk.AccAddress { func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) staking.MsgCreateValidator { commission := staking.NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) return staking.NewMsgCreateValidator( - address, pubKey, sdk.NewCoin(stakingTypes.DefaultBondDenom, amt), + address, pubKey, sdk.NewCoin(staking.DefaultBondDenom, amt), staking.Description{}, commission, ) } diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index 9ee6d03c0..2476ec99b 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -10,11 +10,14 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/staking" + "github.com/cosmos/cosmos-sdk/x/staking/types" ) func TestBeginBlocker(t *testing.T) { ctx, ck, sk, _, keeper := createTestInput(t, DefaultParams()) - addr, pk, amt := addrs[2], pks[2], sdk.NewInt(100) + power := int64(100) + amt := types.TokensFromTendermintPower(power) + addr, pk := addrs[2], pks[2] // bond the validator got := staking.NewHandler(sk)(ctx, NewTestMsgCreateValidator(addr, pk, amt)) @@ -24,7 +27,7 @@ func TestBeginBlocker(t *testing.T) { t, ck.GetCoins(ctx, sdk.AccAddress(addr)), sdk.Coins{sdk.NewCoin(sk.GetParams(ctx).BondDenom, initCoins.Sub(amt))}, ) - require.True(sdk.IntEq(t, amt, sk.Validator(ctx, addr).GetPower())) + require.Equal(t, amt, sk.Validator(ctx, addr).GetBondedTokens()) val := abci.Validator{ Address: pk.Address(), diff --git a/x/staking/app_test.go b/x/staking/app_test.go index c7203c6e7..a160595c7 100644 --- a/x/staking/app_test.go +++ b/x/staking/app_test.go @@ -10,7 +10,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // getMockApp returns an initialized mock application for this module. @@ -52,7 +51,8 @@ func getInitChainer(mapp *mock.App, keeper Keeper) sdk.InitChainer { mapp.InitChainer(ctx, req) stakingGenesis := DefaultGenesisState() - stakingGenesis.Pool.NotBondedTokens = sdk.NewInt(100000) + tokens := TokensFromTendermintPower(100000) + stakingGenesis.Pool.NotBondedTokens = tokens validators, err := InitGenesis(ctx, keeper, stakingGenesis) if err != nil { @@ -97,8 +97,10 @@ func checkDelegation( func TestStakingMsgs(t *testing.T) { mApp, keeper := getMockApp(t) - genCoin := sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 42) - bondCoin := sdk.NewInt64Coin(stakingTypes.DefaultBondDenom, 10) + genTokens := TokensFromTendermintPower(42) + bondTokens := TokensFromTendermintPower(10) + genCoin := sdk.NewCoin(DefaultBondDenom, genTokens) + bondCoin := sdk.NewCoin(DefaultBondDenom, bondTokens) acc1 := &auth.BaseAccount{ Address: addr1, @@ -127,7 +129,7 @@ func TestStakingMsgs(t *testing.T) { validator := checkValidator(t, mApp, keeper, sdk.ValAddress(addr1), true) require.Equal(t, sdk.ValAddress(addr1), validator.OperatorAddr) require.Equal(t, sdk.Bonded, validator.Status) - require.True(sdk.IntEq(t, sdk.NewInt(10), validator.BondedTokens())) + require.True(sdk.IntEq(t, bondTokens, validator.BondedTokens())) // addr1 create validator on behalf of addr2 createValidatorMsgOnBehalfOf := NewMsgCreateValidatorOnBehalfOf( @@ -141,10 +143,10 @@ func TestStakingMsgs(t *testing.T) { validator = checkValidator(t, mApp, keeper, sdk.ValAddress(addr2), true) require.Equal(t, sdk.ValAddress(addr2), validator.OperatorAddr) require.Equal(t, sdk.Bonded, validator.Status) - require.True(sdk.IntEq(t, sdk.NewInt(10), validator.Tokens)) + require.True(sdk.IntEq(t, bondTokens, validator.Tokens)) // check the bond that should have been created as well - checkDelegation(t, mApp, keeper, addr1, sdk.ValAddress(addr1), true, sdk.NewDec(10)) + checkDelegation(t, mApp, keeper, addr1, sdk.ValAddress(addr1), true, sdk.NewDecFromInt(bondTokens)) // edit the validator description = NewDescription("bar_moniker", "", "", "") @@ -160,10 +162,10 @@ func TestStakingMsgs(t *testing.T) { mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{delegateMsg}, []uint64{0}, []uint64{1}, true, true, priv2) mock.CheckBalance(t, mApp, addr2, sdk.Coins{genCoin.Minus(bondCoin)}) - checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), true, sdk.NewDec(10)) + checkDelegation(t, mApp, keeper, addr2, sdk.ValAddress(addr1), true, sdk.NewDecFromInt(bondTokens)) // begin unbonding - beginUnbondingMsg := NewMsgUndelegate(addr2, sdk.ValAddress(addr1), sdk.NewDec(10)) + beginUnbondingMsg := NewMsgUndelegate(addr2, sdk.ValAddress(addr1), sdk.NewDecFromInt(bondTokens)) mock.SignCheckDeliver(t, mApp.Cdc, mApp.BaseApp, []sdk.Msg{beginUnbondingMsg}, []uint64{0}, []uint64{2}, true, true, priv2) // delegation should exist anymore diff --git a/x/staking/genesis.go b/x/staking/genesis.go index 8fa52c07a..40df1a2f4 100644 --- a/x/staking/genesis.go +++ b/x/staking/genesis.go @@ -77,7 +77,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ panic("expected validator, not found") } update := validator.ABCIValidatorUpdate() - update.Power = lv.Power.Int64() // keep the next-val-set offset, use the last power for the first block + update.Power = lv.Power // keep the next-val-set offset, use the last power for the first block res = append(res, update) } } else { @@ -107,7 +107,7 @@ func ExportGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { return false }) var lastValidatorPowers []types.LastValidatorPower - keeper.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power sdk.Int) (stop bool) { + keeper.IterateLastValidatorPowers(ctx, func(addr sdk.ValAddress, power int64) (stop bool) { lastValidatorPowers = append(lastValidatorPowers, types.LastValidatorPower{addr, power}) return false }) @@ -130,7 +130,7 @@ func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisVali keeper.IterateLastValidators(ctx, func(_ int64, validator sdk.Validator) (stop bool) { vals = append(vals, tmtypes.GenesisValidator{ PubKey: validator.GetConsPubKey(), - Power: validator.GetPower().Int64(), + Power: validator.GetTendermintPower(), Name: validator.GetMoniker(), }) diff --git a/x/staking/genesis_test.go b/x/staking/genesis_test.go index ac9174651..9a9a716fe 100644 --- a/x/staking/genesis_test.go +++ b/x/staking/genesis_test.go @@ -20,7 +20,8 @@ func TestInitGenesis(t *testing.T) { ctx, _, keeper := keep.CreateTestInput(t, false, 1000) pool := keeper.GetPool(ctx) - pool.BondedTokens = sdk.NewInt(2) + pool.BondedTokens = TokensFromTendermintPower(2) + valTokens := TokensFromTendermintPower(1) params := keeper.GetParams(ctx) validators := make([]Validator, 2) @@ -29,16 +30,16 @@ func TestInitGenesis(t *testing.T) { // initialize the validators validators[0].OperatorAddr = sdk.ValAddress(keep.Addrs[0]) validators[0].ConsPubKey = keep.PKs[0] - validators[0].Description = Description{Moniker: "hoop"} + validators[0].Description = NewDescription("hoop", "", "", "") validators[0].Status = sdk.Bonded - validators[0].Tokens = sdk.OneInt() - validators[0].DelegatorShares = sdk.OneDec() + validators[0].Tokens = valTokens + validators[0].DelegatorShares = sdk.NewDecFromInt(valTokens) validators[1].OperatorAddr = sdk.ValAddress(keep.Addrs[1]) validators[1].ConsPubKey = keep.PKs[1] - validators[1].Description = Description{Moniker: "bloop"} + validators[1].Description = NewDescription("bloop", "", "", "") validators[1].Status = sdk.Bonded - validators[1].Tokens = sdk.OneInt() - validators[1].DelegatorShares = sdk.OneDec() + validators[1].Tokens = valTokens + validators[1].DelegatorShares = sdk.NewDecFromInt(valTokens) genesisState := types.NewGenesisState(pool, params, validators, delegations) vals, err := InitGenesis(ctx, keeper, genesisState) @@ -75,23 +76,25 @@ func TestInitGenesisLargeValidatorSet(t *testing.T) { // Assigning 2 to the first 100 vals, 1 to the rest pool := keeper.GetPool(ctx) - pool.BondedTokens = sdk.NewInt(int64(200 + (size - 100))) + bondedTokens := TokensFromTendermintPower(int64(200 + (size - 100))) + pool.BondedTokens = bondedTokens params := keeper.GetParams(ctx) delegations := []Delegation{} validators := make([]Validator, size) for i := range validators { - validators[i] = NewValidator(sdk.ValAddress(keep.Addrs[i]), keep.PKs[i], Description{Moniker: fmt.Sprintf("#%d", i)}) + validators[i] = NewValidator(sdk.ValAddress(keep.Addrs[i]), + keep.PKs[i], NewDescription(fmt.Sprintf("#%d", i), "", "", "")) validators[i].Status = sdk.Bonded + + tokens := TokensFromTendermintPower(1) if i < 100 { - validators[i].Tokens = sdk.NewInt(2) - validators[i].DelegatorShares = sdk.NewDec(2) - } else { - validators[i].Tokens = sdk.OneInt() - validators[i].DelegatorShares = sdk.OneDec() + tokens = TokensFromTendermintPower(2) } + validators[i].Tokens = tokens + validators[i].DelegatorShares = sdk.NewDecFromInt(tokens) } genesisState := types.NewGenesisState(pool, params, validators, delegations) diff --git a/x/staking/handler_test.go b/x/staking/handler_test.go index 3caecca44..e9692e7ff 100644 --- a/x/staking/handler_test.go +++ b/x/staking/handler_test.go @@ -31,8 +31,9 @@ func setInstantUnbondPeriod(keeper keep.Keeper, ctx sdk.Context) types.Params { func TestValidatorByPowerIndex(t *testing.T) { validatorAddr, validatorAddr3 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) - initBond := int64(1000000) - ctx, _, keeper := keep.CreateTestInput(t, false, initBond) + initPower := int64(1000000) + initBond := TokensFromTendermintPower(initPower) + ctx, _, keeper := keep.CreateTestInput(t, false, initPower) _ = setInstantUnbondPeriod(keeper, ctx) // create validator @@ -47,10 +48,8 @@ func TestValidatorByPowerIndex(t *testing.T) { // verify the self-delegation exists bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found) - gotBond := bond.Shares.RoundInt64() - require.Equal(t, initBond, gotBond, - "initBond: %v\ngotBond: %v\nbond: %v\n", - initBond, gotBond, bond) + gotBond := bond.Shares.RoundInt() + require.Equal(t, initBond, gotBond) // verify that the by power index exists validator, found := keeper.GetValidator(ctx, validatorAddr) @@ -59,7 +58,7 @@ func TestValidatorByPowerIndex(t *testing.T) { require.True(t, keep.ValidatorByPowerIndexExists(ctx, keeper, power)) // create a second validator keep it bonded - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], int64(1000000)) + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], initBond) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) @@ -69,13 +68,13 @@ func TestValidatorByPowerIndex(t *testing.T) { // slash and jail the first validator consAddr0 := sdk.ConsAddress(keep.PKs[0].Address()) - keeper.Slash(ctx, consAddr0, 0, initBond, sdk.NewDecWithPrec(5, 1)) + keeper.Slash(ctx, consAddr0, 0, initPower, sdk.NewDecWithPrec(5, 1)) keeper.Jail(ctx, consAddr0) keeper.ApplyAndReturnValidatorSetUpdates(ctx) validator, found = keeper.GetValidator(ctx, validatorAddr) require.True(t, found) - require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding - require.Equal(t, int64(500000), validator.Tokens.Int64()) // ensure tokens slashed + require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding + require.Equal(t, initBond.DivRaw(2), validator.Tokens) // ensure tokens slashed keeper.Unjail(ctx, consAddr0) // the old power record should have been deleted as the power changed @@ -92,7 +91,7 @@ func TestValidatorByPowerIndex(t *testing.T) { require.Equal(t, power2, power3) // unbond self-delegation - msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(1000000)) + msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDecFromInt(initBond)) got = handleMsgUndelegate(ctx, msgUndelegate, keeper) require.True(t, got.IsOK(), "expected msg to be ok, got %v", got) var finishTime time.Time @@ -114,7 +113,8 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) { addr1, addr2 := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]) pk1, pk2 := keep.PKs[0], keep.PKs[1] - msgCreateValidator1 := NewTestMsgCreateValidator(addr1, pk1, 10) + valTokens := types.TokensFromTendermintPower(10) + msgCreateValidator1 := NewTestMsgCreateValidator(addr1, pk1, valTokens) got := handleMsgCreateValidator(ctx, msgCreateValidator1, keeper) require.True(t, got.IsOK(), "%v", got) @@ -125,22 +125,22 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) { assert.Equal(t, sdk.Bonded, validator.Status) assert.Equal(t, addr1, validator.OperatorAddr) assert.Equal(t, pk1, validator.ConsPubKey) - assert.Equal(t, int64(10), validator.BondedTokens().Int64()) - assert.Equal(t, sdk.NewDec(10), validator.DelegatorShares) + assert.Equal(t, valTokens, validator.BondedTokens()) + assert.Equal(t, sdk.NewDecFromInt(valTokens), validator.DelegatorShares) assert.Equal(t, Description{}, validator.Description) // two validators can't have the same operator address - msgCreateValidator2 := NewTestMsgCreateValidator(addr1, pk2, 10) + msgCreateValidator2 := NewTestMsgCreateValidator(addr1, pk2, valTokens) got = handleMsgCreateValidator(ctx, msgCreateValidator2, keeper) require.False(t, got.IsOK(), "%v", got) // two validators can't have the same pubkey - msgCreateValidator3 := NewTestMsgCreateValidator(addr2, pk1, 10) + msgCreateValidator3 := NewTestMsgCreateValidator(addr2, pk1, valTokens) got = handleMsgCreateValidator(ctx, msgCreateValidator3, keeper) require.False(t, got.IsOK(), "%v", got) // must have different pubkey and operator - msgCreateValidator4 := NewTestMsgCreateValidator(addr2, pk2, 10) + msgCreateValidator4 := NewTestMsgCreateValidator(addr2, pk2, valTokens) got = handleMsgCreateValidator(ctx, msgCreateValidator4, keeper) require.True(t, got.IsOK(), "%v", got) @@ -154,8 +154,8 @@ func TestDuplicatesMsgCreateValidator(t *testing.T) { assert.Equal(t, sdk.Bonded, validator.Status) assert.Equal(t, addr2, validator.OperatorAddr) assert.Equal(t, pk2, validator.ConsPubKey) - assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.Tokens)) - assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) + assert.True(sdk.IntEq(t, valTokens, validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDecFromInt(valTokens), validator.DelegatorShares)) assert.Equal(t, Description{}, validator.Description) } @@ -166,7 +166,7 @@ func TestInvalidPubKeyTypeMsgCreateValidator(t *testing.T) { invalidPk := secp256k1.GenPrivKey().PubKey() // invalid pukKey type should not be allowed - msgCreateValidator := NewTestMsgCreateValidator(addr, invalidPk, 10) + msgCreateValidator := NewTestMsgCreateValidator(addr, invalidPk, sdk.NewInt(10)) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.False(t, got.IsOK(), "%v", got) @@ -184,7 +184,8 @@ func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) { validatorAddr := sdk.ValAddress(keep.Addrs[0]) delegatorAddr := keep.Addrs[1] pk := keep.PKs[0] - msgCreateValidatorOnBehalfOf := NewTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, 10) + valTokens := types.TokensFromTendermintPower(10) + msgCreateValidatorOnBehalfOf := NewTestMsgCreateValidatorOnBehalfOf(delegatorAddr, validatorAddr, pk, valTokens) got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper) require.True(t, got.IsOK(), "%v", got) @@ -198,8 +199,8 @@ func TestDuplicatesMsgCreateValidatorOnBehalfOf(t *testing.T) { assert.Equal(t, sdk.Bonded, validator.Status) assert.Equal(t, validatorAddr, validator.OperatorAddr) assert.Equal(t, pk, validator.ConsPubKey) - assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.Tokens)) - assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) + assert.True(sdk.IntEq(t, valTokens, validator.Tokens)) + assert.True(sdk.DecEq(t, sdk.NewDecFromInt(valTokens), validator.DelegatorShares)) assert.Equal(t, Description{}, validator.Description) // one validator cannot be created twice even from different delegator @@ -213,7 +214,7 @@ func TestLegacyValidatorDelegations(t *testing.T) { ctx, _, keeper := keep.CreateTestInput(t, false, int64(1000)) setInstantUnbondPeriod(keeper, ctx) - bondAmount := int64(10) + bondAmount := TokensFromTendermintPower(10) valAddr := sdk.ValAddress(keep.Addrs[0]) valConsPubKey, valConsAddr := keep.PKs[0], sdk.ConsAddress(keep.PKs[0].Address()) delAddr := keep.Addrs[1] @@ -231,8 +232,8 @@ func TestLegacyValidatorDelegations(t *testing.T) { validator, found := keeper.GetValidator(ctx, valAddr) require.True(t, found) require.Equal(t, sdk.Bonded, validator.Status) - require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt64()) - require.Equal(t, bondAmount, validator.BondedTokens().Int64()) + require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) + require.Equal(t, bondAmount, validator.BondedTokens()) // delegate tokens to the validator msgDelegate := NewTestMsgDelegate(delAddr, valAddr, bondAmount) @@ -242,12 +243,12 @@ func TestLegacyValidatorDelegations(t *testing.T) { // verify validator bonded shares validator, found = keeper.GetValidator(ctx, valAddr) require.True(t, found) - require.Equal(t, bondAmount*2, validator.DelegatorShares.RoundInt64()) - require.Equal(t, bondAmount*2, validator.BondedTokens().Int64()) + require.Equal(t, bondAmount.MulRaw(2), validator.DelegatorShares.RoundInt()) + require.Equal(t, bondAmount.MulRaw(2), validator.BondedTokens()) // unbond validator total self-delegations (which should jail the validator) - unbondShares := sdk.NewDec(10) - msgUndelegate := NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, unbondShares) + unbondShares := types.TokensFromTendermintPower(10) + msgUndelegate := NewMsgUndelegate(sdk.AccAddress(valAddr), valAddr, sdk.NewDecFromInt(unbondShares)) got = handleMsgUndelegate(ctx, msgUndelegate, keeper) require.True(t, got.IsOK(), "expected begin unbonding validator msg to be ok, got %v", got) @@ -261,13 +262,13 @@ func TestLegacyValidatorDelegations(t *testing.T) { validator, found = keeper.GetValidator(ctx, valAddr) require.True(t, found) require.True(t, validator.Jailed) - require.Equal(t, int64(10), validator.Tokens.Int64()) + require.Equal(t, bondAmount, validator.Tokens) // verify delegation still exists bond, found := keeper.GetDelegation(ctx, delAddr, valAddr) require.True(t, found) - require.Equal(t, bondAmount, bond.Shares.RoundInt64()) - require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt64()) + require.Equal(t, bondAmount, bond.Shares.RoundInt()) + require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) // verify a delegator cannot create a new delegation to the now jailed validator msgDelegate = NewTestMsgDelegate(delAddr, valAddr, bondAmount) @@ -282,8 +283,8 @@ func TestLegacyValidatorDelegations(t *testing.T) { // verify validator bonded shares validator, found = keeper.GetValidator(ctx, valAddr) require.True(t, found) - require.Equal(t, bondAmount*2, validator.DelegatorShares.RoundInt64()) - require.Equal(t, bondAmount*2, validator.Tokens.Int64()) + require.Equal(t, bondAmount.MulRaw(2), validator.DelegatorShares.RoundInt()) + require.Equal(t, bondAmount.MulRaw(2), validator.Tokens) // unjail the validator now that is has non-zero self-delegated shares keeper.Unjail(ctx, valConsAddr) @@ -296,22 +297,23 @@ func TestLegacyValidatorDelegations(t *testing.T) { // verify validator bonded shares validator, found = keeper.GetValidator(ctx, valAddr) require.True(t, found) - require.Equal(t, bondAmount*3, validator.DelegatorShares.RoundInt64()) - require.Equal(t, bondAmount*3, validator.Tokens.Int64()) + require.Equal(t, bondAmount.MulRaw(3), validator.DelegatorShares.RoundInt()) + require.Equal(t, bondAmount.MulRaw(3), validator.Tokens) // verify new delegation bond, found = keeper.GetDelegation(ctx, delAddr, valAddr) require.True(t, found) - require.Equal(t, bondAmount*2, bond.Shares.RoundInt64()) - require.Equal(t, bondAmount*3, validator.DelegatorShares.RoundInt64()) + require.Equal(t, bondAmount.MulRaw(2), bond.Shares.RoundInt()) + require.Equal(t, bondAmount.MulRaw(3), validator.DelegatorShares.RoundInt()) } func TestIncrementsMsgDelegate(t *testing.T) { - initBond := int64(1000) - ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond) + initPower := int64(1000) + initBond := TokensFromTendermintPower(initPower) + ctx, accMapper, keeper := keep.CreateTestInput(t, false, initPower) params := keeper.GetParams(ctx) - bondAmount := int64(10) + bondAmount := TokensFromTendermintPower(10) validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] // first create validator @@ -325,25 +327,25 @@ func TestIncrementsMsgDelegate(t *testing.T) { validator, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.Equal(t, sdk.Bonded, validator.Status) - require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt64()) - require.Equal(t, bondAmount, validator.BondedTokens().Int64(), "validator: %v", validator) + require.Equal(t, bondAmount, validator.DelegatorShares.RoundInt()) + require.Equal(t, bondAmount, validator.BondedTokens(), "validator: %v", validator) _, found = keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) require.False(t, found) bond, found := keeper.GetDelegation(ctx, sdk.AccAddress(validatorAddr), validatorAddr) require.True(t, found) - require.Equal(t, bondAmount, bond.Shares.RoundInt64()) + require.Equal(t, bondAmount, bond.Shares.RoundInt()) pool := keeper.GetPool(ctx) exRate := validator.DelegatorShareExRate() require.True(t, exRate.Equal(sdk.OneDec()), "expected exRate 1 got %v", exRate) - require.Equal(t, bondAmount, pool.BondedTokens.Int64()) + require.Equal(t, bondAmount, pool.BondedTokens) // just send the same msgbond multiple times msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, bondAmount) - for i := 0; i < 5; i++ { + for i := int64(0); i < 5; i++ { ctx = ctx.WithBlockHeight(int64(i)) got := handleMsgDelegate(ctx, msgDelegate, keeper) @@ -358,12 +360,12 @@ func TestIncrementsMsgDelegate(t *testing.T) { exRate := validator.DelegatorShareExRate() require.True(t, exRate.Equal(sdk.OneDec()), "expected exRate 1 got %v, i = %v", exRate, i) - expBond := int64(i+1) * bondAmount - expDelegatorShares := int64(i+2) * bondAmount // (1 self delegation) - expDelegatorAcc := sdk.NewInt(initBond - expBond) + expBond := bondAmount.MulRaw(i + 1) + expDelegatorShares := bondAmount.MulRaw(i + 2) // (1 self delegation) + expDelegatorAcc := initBond.Sub(expBond) - gotBond := bond.Shares.RoundInt64() - gotDelegatorShares := validator.DelegatorShares.RoundInt64() + gotBond := bond.Shares.RoundInt() + gotDelegatorShares := validator.DelegatorShares.RoundInt() gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom) require.Equal(t, expBond, gotBond, @@ -379,8 +381,9 @@ func TestIncrementsMsgDelegate(t *testing.T) { } func TestIncrementsMsgUnbond(t *testing.T) { - initBond := int64(1000) - ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond) + initPower := int64(1000) + initBond := TokensFromTendermintPower(initPower) + ctx, accMapper, keeper := keep.CreateTestInput(t, false, initPower) params := setInstantUnbondPeriod(keeper, ctx) denom := params.BondDenom @@ -400,22 +403,22 @@ func TestIncrementsMsgUnbond(t *testing.T) { // balance should have been subtracted after delegation amt2 := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(denom) - require.Equal(t, amt1.Sub(sdk.NewInt(initBond)).Int64(), amt2.Int64(), "expected coins to be subtracted") + require.True(sdk.IntEq(t, amt1.Sub(initBond), amt2)) // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) validator, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) - require.Equal(t, initBond*2, validator.DelegatorShares.RoundInt64()) - require.Equal(t, initBond*2, validator.BondedTokens().Int64()) + require.Equal(t, initBond.MulRaw(2), validator.DelegatorShares.RoundInt()) + require.Equal(t, initBond.MulRaw(2), validator.BondedTokens()) // just send the same msgUnbond multiple times // TODO use decimals here unbondShares := sdk.NewDec(10) msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondShares) - numUnbonds := 5 - for i := 0; i < numUnbonds; i++ { + numUnbonds := int64(5) + for i := int64(0); i < numUnbonds; i++ { got := handleMsgUndelegate(ctx, msgUndelegate, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) @@ -430,12 +433,12 @@ func TestIncrementsMsgUnbond(t *testing.T) { bond, found := keeper.GetDelegation(ctx, delegatorAddr, validatorAddr) require.True(t, found) - expBond := initBond - int64(i+1)*unbondShares.RoundInt64() - expDelegatorShares := 2*initBond - int64(i+1)*unbondShares.RoundInt64() - expDelegatorAcc := sdk.NewInt(initBond - expBond) + expBond := initBond.Sub(unbondShares.MulInt64(i + 1).RoundInt()) + expDelegatorShares := (initBond.MulRaw(2)).Sub(unbondShares.MulInt64(i + 1).RoundInt()) + expDelegatorAcc := initBond.Sub(expBond) - gotBond := bond.Shares.RoundInt64() - gotDelegatorShares := validator.DelegatorShares.RoundInt64() + gotBond := bond.Shares.RoundInt() + gotDelegatorShares := validator.DelegatorShares.RoundInt() gotDelegatorAcc := accMapper.GetAccount(ctx, delegatorAddr).GetCoins().AmountOf(params.BondDenom) require.Equal(t, expBond, gotBond, @@ -450,31 +453,31 @@ func TestIncrementsMsgUnbond(t *testing.T) { } // these are more than we have bonded now - errorCases := []int64{ - //1<<64 - 1, // more than int64 - //1<<63 + 1, // more than int64 - 1<<63 - 1, - 1 << 31, + errorCases := []sdk.Int{ + //1<<64 - 1, // more than int64 power + //1<<63 + 1, // more than int64 power + types.TokensFromTendermintPower(1<<63 - 1), + types.TokensFromTendermintPower(1 << 31), initBond, } - for _, c := range errorCases { - unbondShares := sdk.NewDec(c) + for i, c := range errorCases { + unbondShares := sdk.NewDecFromInt(c) msgUndelegate := NewMsgUndelegate(delegatorAddr, validatorAddr, unbondShares) got = handleMsgUndelegate(ctx, msgUndelegate, keeper) - require.False(t, got.IsOK(), "expected unbond msg to fail") + require.False(t, got.IsOK(), "expected unbond msg to fail, index: %v", i) } - leftBonded := initBond - int64(numUnbonds)*unbondShares.RoundInt64() + leftBonded := initBond.Sub(unbondShares.MulInt64(numUnbonds).RoundInt()) // should be unable to unbond one more than we have - unbondShares = sdk.NewDec(leftBonded + 1) + unbondShares = sdk.NewDecFromInt(leftBonded.AddRaw(1)) msgUndelegate = NewMsgUndelegate(delegatorAddr, validatorAddr, unbondShares) got = handleMsgUndelegate(ctx, msgUndelegate, keeper) require.False(t, got.IsOK(), "got: %v\nmsgUnbond: %v\nshares: %v\nleftBonded: %v\n", got, msgUndelegate, unbondShares.String(), leftBonded) // should be able to unbond just what we have - unbondShares = sdk.NewDec(leftBonded) + unbondShares = sdk.NewDecFromInt(leftBonded) msgUndelegate = NewMsgUndelegate(delegatorAddr, validatorAddr, unbondShares) got = handleMsgUndelegate(ctx, msgUndelegate, keeper) require.True(t, got.IsOK(), @@ -482,16 +485,27 @@ func TestIncrementsMsgUnbond(t *testing.T) { } func TestMultipleMsgCreateValidator(t *testing.T) { - initBond := int64(1000) - ctx, accMapper, keeper := keep.CreateTestInput(t, false, initBond) + initPower := int64(1000) + initTokens := types.TokensFromTendermintPower(initPower) + ctx, accMapper, keeper := keep.CreateTestInput(t, false, initPower) params := setInstantUnbondPeriod(keeper, ctx) - validatorAddrs := []sdk.ValAddress{sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), sdk.ValAddress(keep.Addrs[2])} - delegatorAddrs := []sdk.AccAddress{keep.Addrs[3], keep.Addrs[4], keep.Addrs[5]} + validatorAddrs := []sdk.ValAddress{ + sdk.ValAddress(keep.Addrs[0]), + sdk.ValAddress(keep.Addrs[1]), + sdk.ValAddress(keep.Addrs[2]), + } + delegatorAddrs := []sdk.AccAddress{ + keep.Addrs[3], + keep.Addrs[4], + keep.Addrs[5], + } // bond them all for i, validatorAddr := range validatorAddrs { - msgCreateValidatorOnBehalfOf := NewTestMsgCreateValidatorOnBehalfOf(delegatorAddrs[i], validatorAddr, keep.PKs[i], 10) + valTokens := types.TokensFromTendermintPower(10) + msgCreateValidatorOnBehalfOf := NewTestMsgCreateValidatorOnBehalfOf( + delegatorAddrs[i], validatorAddr, keep.PKs[i], valTokens) got := handleMsgCreateValidator(ctx, msgCreateValidatorOnBehalfOf, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) @@ -499,10 +513,10 @@ func TestMultipleMsgCreateValidator(t *testing.T) { validators := keeper.GetValidators(ctx, 100) require.Equal(t, (i + 1), len(validators)) val := validators[i] - balanceExpd := sdk.NewInt(initBond - 10) + balanceExpd := initTokens.Sub(valTokens) balanceGot := accMapper.GetAccount(ctx, delegatorAddrs[i]).GetCoins().AmountOf(params.BondDenom) require.Equal(t, i+1, len(validators), "expected %d validators got %d, validators: %v", i+1, len(validators), validators) - require.Equal(t, 10, int(val.DelegatorShares.RoundInt64()), "expected %d shares, got %d", 10, val.DelegatorShares) + require.Equal(t, valTokens, val.DelegatorShares.RoundInt(), "expected %d shares, got %d", 10, val.DelegatorShares) require.Equal(t, balanceExpd, balanceGot, "expected account to have %d, got %d", balanceExpd, balanceGot) } @@ -510,7 +524,8 @@ func TestMultipleMsgCreateValidator(t *testing.T) { for i, validatorAddr := range validatorAddrs { _, found := keeper.GetValidator(ctx, validatorAddr) require.True(t, found) - msgUndelegate := NewMsgUndelegate(delegatorAddrs[i], validatorAddr, sdk.NewDec(10)) // remove delegation + unbondingTokens := types.TokensFromTendermintPower(10) + msgUndelegate := NewMsgUndelegate(delegatorAddrs[i], validatorAddr, sdk.NewDecFromInt(unbondingTokens)) // remove delegation got := handleMsgUndelegate(ctx, msgUndelegate, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) var finishTime time.Time @@ -527,9 +542,8 @@ func TestMultipleMsgCreateValidator(t *testing.T) { _, found = keeper.GetValidator(ctx, validatorAddr) require.False(t, found) - expBalance := sdk.NewInt(initBond) gotBalance := accMapper.GetAccount(ctx, delegatorAddrs[i]).GetCoins().AmountOf(params.BondDenom) - require.Equal(t, expBalance, gotBalance, "expected account to have %d, got %d", expBalance, gotBalance) + require.Equal(t, initTokens, gotBalance, "expected account to have %d, got %d", initTokens, gotBalance) } } @@ -539,13 +553,13 @@ func TestMultipleMsgDelegate(t *testing.T) { _ = setInstantUnbondPeriod(keeper, ctx) //first make a validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10) + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected msg to be ok, got %v", got) // delegate multiple parties for i, delegatorAddr := range delegatorAddrs { - msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, 10) + msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) got := handleMsgDelegate(ctx, msgDelegate, keeper) require.True(t, got.IsOK(), "expected msg %d to be ok, got %v", i, got) @@ -577,12 +591,12 @@ func TestJailValidator(t *testing.T) { _ = setInstantUnbondPeriod(keeper, ctx) // create the validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10) + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // bond a delegator - msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, 10) + msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) got = handleMsgDelegate(ctx, msgDelegate, keeper) require.True(t, got.IsOK(), "expected ok, got %v", got) @@ -626,19 +640,22 @@ func TestValidatorQueue(t *testing.T) { keeper.SetParams(ctx, params) // create the validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10) + valTokens := types.TokensFromTendermintPower(10) + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], valTokens) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // bond a delegator - msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, 10) + delTokens := types.TokensFromTendermintPower(10) + msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, delTokens) got = handleMsgDelegate(ctx, msgDelegate, keeper) require.True(t, got.IsOK(), "expected ok, got %v", got) EndBlocker(ctx, keeper) // unbond the all self-delegation to put validator in unbonding state - msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10)) + msgUndelegateValidator := NewMsgUndelegate(sdk.AccAddress(validatorAddr), + validatorAddr, sdk.NewDecFromInt(delTokens)) got = handleMsgUndelegate(ctx, msgUndelegateValidator, keeper) require.True(t, got.IsOK(), "expected no error: %v", got) var finishTime time.Time @@ -676,14 +693,17 @@ func TestUnbondingPeriod(t *testing.T) { keeper.SetParams(ctx, params) // create the validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10) + valTokens := types.TokensFromTendermintPower(10) + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], valTokens) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") EndBlocker(ctx, keeper) // begin unbonding - msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr), validatorAddr, sdk.NewDec(10)) + unbondingTokens := types.TokensFromTendermintPower(10) + msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr), + validatorAddr, sdk.NewDecFromInt(unbondingTokens)) got = handleMsgUndelegate(ctx, msgUndelegate, keeper) require.True(t, got.IsOK(), "expected no error") origHeader := ctx.BlockHeader() @@ -714,12 +734,12 @@ func TestUnbondingFromUnbondingValidator(t *testing.T) { validatorAddr, delegatorAddr := sdk.ValAddress(keep.Addrs[0]), keep.Addrs[1] // create the validator - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10) + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // bond a delegator - msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, 10) + msgDelegate := NewTestMsgDelegate(delegatorAddr, validatorAddr, sdk.NewInt(10)) got = handleMsgDelegate(ctx, msgDelegate, keeper) require.True(t, got.IsOK(), "expected ok, got %v", got) @@ -761,7 +781,7 @@ func TestRedelegationPeriod(t *testing.T) { keeper.SetParams(ctx, params) // create the validators - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10) + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) // initial balance amt1 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins().AmountOf(denom) @@ -773,7 +793,7 @@ func TestRedelegationPeriod(t *testing.T) { amt2 := AccMapper.GetAccount(ctx, sdk.AccAddress(validatorAddr)).GetCoins().AmountOf(denom) require.Equal(t, amt1.Sub(sdk.NewInt(10)).Int64(), amt2.Int64(), "expected coins to be subtracted") - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10) + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], sdk.NewInt(10)) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") @@ -820,15 +840,15 @@ func TestTransitiveRedelegation(t *testing.T) { keeper.SetParams(ctx, params) // create the validators - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], 10) + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr, keep.PKs[0], sdk.NewInt(10)) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 10) + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], sdk.NewInt(10)) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10) + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], sdk.NewInt(10)) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") @@ -861,11 +881,12 @@ func TestMultipleRedelegationAtSameTime(t *testing.T) { keeper.SetParams(ctx, params) // create the validators - msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], 10) + valTokens := types.TokensFromTendermintPower(10) + msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - msgCreateValidator = NewTestMsgCreateValidator(valAddr2, keep.PKs[1], 10) + msgCreateValidator = NewTestMsgCreateValidator(valAddr2, keep.PKs[1], valTokens) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") @@ -875,7 +896,7 @@ func TestMultipleRedelegationAtSameTime(t *testing.T) { // begin a redelegate selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr, - valAddr, valAddr2, sdk.NewDec(5)) + valAddr, valAddr2, sdk.NewDecFromInt(valTokens.DivRaw(2))) got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) require.True(t, got.IsOK(), "expected no error, %v", got) @@ -912,11 +933,12 @@ func TestMultipleRedelegationAtUniqueTimes(t *testing.T) { keeper.SetParams(ctx, params) // create the validators - msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], 10) + valTokens := types.TokensFromTendermintPower(10) + msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - msgCreateValidator = NewTestMsgCreateValidator(valAddr2, keep.PKs[1], 10) + msgCreateValidator = NewTestMsgCreateValidator(valAddr2, keep.PKs[1], valTokens) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") @@ -926,7 +948,7 @@ func TestMultipleRedelegationAtUniqueTimes(t *testing.T) { // begin a redelegate selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) msgBeginRedelegate := NewMsgBeginRedelegate(selfDelAddr, - valAddr, valAddr2, sdk.NewDec(5)) + valAddr, valAddr2, sdk.NewDecFromInt(valTokens.DivRaw(2))) got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) require.True(t, got.IsOK(), "expected no error, %v", got) @@ -964,7 +986,8 @@ func TestMultipleUnbondingDelegationAtSameTime(t *testing.T) { keeper.SetParams(ctx, params) // create the validator - msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], 10) + valTokens := types.TokensFromTendermintPower(10) + msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") @@ -973,7 +996,7 @@ func TestMultipleUnbondingDelegationAtSameTime(t *testing.T) { // begin an unbonding delegation selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) - msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, sdk.NewDec(5)) + msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, sdk.NewDecFromInt(valTokens.DivRaw(2))) got = handleMsgUndelegate(ctx, msgUndelegate, keeper) require.True(t, got.IsOK(), "expected no error, %v", got) @@ -1009,7 +1032,8 @@ func TestMultipleUnbondingDelegationAtUniqueTimes(t *testing.T) { keeper.SetParams(ctx, params) // create the validator - msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], 10) + valTokens := types.TokensFromTendermintPower(10) + msgCreateValidator := NewTestMsgCreateValidator(valAddr, keep.PKs[0], valTokens) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") @@ -1018,7 +1042,7 @@ func TestMultipleUnbondingDelegationAtUniqueTimes(t *testing.T) { // begin an unbonding delegation selfDelAddr := sdk.AccAddress(valAddr) // (the validator is it's own delegator) - msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, sdk.NewDec(5)) + msgUndelegate := NewMsgUndelegate(selfDelAddr, valAddr, sdk.NewDecFromInt(valTokens.DivRaw(2))) got = handleMsgUndelegate(ctx, msgUndelegate, keeper) require.True(t, got.IsOK(), "expected no error, %v", got) @@ -1064,21 +1088,24 @@ func TestUnbondingWhenExcessValidators(t *testing.T) { keeper.SetParams(ctx, params) // add three validators - msgCreateValidator := NewTestMsgCreateValidator(validatorAddr1, keep.PKs[0], 50) + valTokens1 := types.TokensFromTendermintPower(50) + msgCreateValidator := NewTestMsgCreateValidator(validatorAddr1, keep.PKs[0], valTokens1) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 1, len(keeper.GetLastValidators(ctx))) - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], 30) + valTokens2 := types.TokensFromTendermintPower(30) + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr2, keep.PKs[1], valTokens2) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) require.Equal(t, 2, len(keeper.GetLastValidators(ctx))) - msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], 10) + valTokens3 := types.TokensFromTendermintPower(10) + msgCreateValidator = NewTestMsgCreateValidator(validatorAddr3, keep.PKs[2], valTokens3) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // apply TM updates @@ -1086,7 +1113,7 @@ func TestUnbondingWhenExcessValidators(t *testing.T) { require.Equal(t, 2, len(keeper.GetLastValidators(ctx))) // unbond the valdator-2 - msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr2), validatorAddr2, sdk.NewDec(30)) + msgUndelegate := NewMsgUndelegate(sdk.AccAddress(validatorAddr2), validatorAddr2, sdk.NewDecFromInt(valTokens2)) got = handleMsgUndelegate(ctx, msgUndelegate, keeper) require.True(t, got.IsOK(), "expected no error on runMsgUndelegate") @@ -1108,16 +1135,17 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { valA, valB, del := sdk.ValAddress(keep.Addrs[0]), sdk.ValAddress(keep.Addrs[1]), keep.Addrs[2] consAddr0 := sdk.ConsAddress(keep.PKs[0].Address()) - msgCreateValidator := NewTestMsgCreateValidator(valA, keep.PKs[0], 10) + valTokens := types.TokensFromTendermintPower(10) + msgCreateValidator := NewTestMsgCreateValidator(valA, keep.PKs[0], valTokens) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") - msgCreateValidator = NewTestMsgCreateValidator(valB, keep.PKs[1], 10) + msgCreateValidator = NewTestMsgCreateValidator(valB, keep.PKs[1], valTokens) got = handleMsgCreateValidator(ctx, msgCreateValidator, keeper) require.True(t, got.IsOK(), "expected no error on runMsgCreateValidator") // delegate 10 stake - msgDelegate := NewTestMsgDelegate(del, valA, 10) + msgDelegate := NewTestMsgDelegate(del, valA, valTokens) got = handleMsgDelegate(ctx, msgDelegate, keeper) require.True(t, got.IsOK(), "expected no error on runMsgDelegate") @@ -1129,19 +1157,21 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { ctx = ctx.WithBlockHeight(1) // begin unbonding 4 stake - msgUndelegate := NewMsgUndelegate(del, valA, sdk.NewDec(4)) + ubdTokens := types.TokensFromTendermintPower(4) + msgUndelegate := NewMsgUndelegate(del, valA, sdk.NewDecFromInt(ubdTokens)) got = handleMsgUndelegate(ctx, msgUndelegate, keeper) require.True(t, got.IsOK(), "expected no error on runMsgUndelegate") // begin redelegate 6 stake - msgBeginRedelegate := NewMsgBeginRedelegate(del, valA, valB, sdk.NewDec(6)) + rdTokens := types.TokensFromTendermintPower(6) + msgBeginRedelegate := NewMsgBeginRedelegate(del, valA, valB, sdk.NewDecFromInt(rdTokens)) got = handleMsgBeginRedelegate(ctx, msgBeginRedelegate, keeper) require.True(t, got.IsOK(), "expected no error on runMsgBeginRedelegate") // destination delegation should have 6 shares delegation, found := keeper.GetDelegation(ctx, del, valB) require.True(t, found) - require.Equal(t, sdk.NewDec(6), delegation.Shares) + require.Equal(t, sdk.NewDecFromInt(rdTokens), delegation.Shares) // must apply validator updates updates = keeper.ApplyAndReturnValidatorSetUpdates(ctx) @@ -1154,23 +1184,23 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { ubd, found := keeper.GetUnbondingDelegation(ctx, del, valA) require.True(t, found) require.Len(t, ubd.Entries, 1) - require.Equal(t, int64(2), ubd.Entries[0].Balance.Amount.Int64()) + require.Equal(t, ubdTokens.DivRaw(2), ubd.Entries[0].Balance.Amount) // redelegation should have been slashed by half redelegation, found := keeper.GetRedelegation(ctx, del, valA, valB) require.True(t, found) require.Len(t, redelegation.Entries, 1) - require.Equal(t, int64(3), redelegation.Entries[0].Balance.Amount.Int64()) + require.Equal(t, rdTokens.DivRaw(2), redelegation.Entries[0].Balance.Amount) // destination delegation should have been slashed by half delegation, found = keeper.GetDelegation(ctx, del, valB) require.True(t, found) - require.Equal(t, sdk.NewDec(3), delegation.Shares) + require.Equal(t, sdk.NewDecFromInt(rdTokens.DivRaw(2)), delegation.Shares) // validator power should have been reduced by half validator, found := keeper.GetValidator(ctx, valA) require.True(t, found) - require.Equal(t, int64(5), validator.GetPower().Int64()) + require.Equal(t, valTokens.DivRaw(2), validator.GetBondedTokens()) // slash the validator for an infraction committed after the unbonding and redelegation begin ctx = ctx.WithBlockHeight(3) @@ -1180,18 +1210,18 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { ubd, found = keeper.GetUnbondingDelegation(ctx, del, valA) require.True(t, found) require.Len(t, ubd.Entries, 1) - require.Equal(t, int64(2), ubd.Entries[0].Balance.Amount.Int64()) + require.Equal(t, ubdTokens.DivRaw(2), ubd.Entries[0].Balance.Amount) // redelegation should be unchanged redelegation, found = keeper.GetRedelegation(ctx, del, valA, valB) require.True(t, found) require.Len(t, redelegation.Entries, 1) - require.Equal(t, int64(3), redelegation.Entries[0].Balance.Amount.Int64()) + require.Equal(t, rdTokens.DivRaw(2), redelegation.Entries[0].Balance.Amount) // destination delegation should be unchanged delegation, found = keeper.GetDelegation(ctx, del, valB) require.True(t, found) - require.Equal(t, sdk.NewDec(3), delegation.Shares) + require.Equal(t, sdk.NewDecFromInt(rdTokens.DivRaw(2)), delegation.Shares) // end blocker EndBlocker(ctx, keeper) diff --git a/x/staking/keeper/delegation_test.go b/x/staking/keeper/delegation_test.go index 590e72826..10e34a81e 100644 --- a/x/staking/keeper/delegation_test.go +++ b/x/staking/keeper/delegation_test.go @@ -1,7 +1,6 @@ package keeper import ( - "fmt" "testing" "time" @@ -32,11 +31,7 @@ func TestDelegation(t *testing.T) { // first add a validators[0] to delegate too - bond1to1 := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[0], - Shares: sdk.NewDec(9), - } + bond1to1 := types.NewDelegation(addrDels[0], addrVals[0], sdk.NewDec(9)) // check the empty keeper first _, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) @@ -56,11 +51,11 @@ func TestDelegation(t *testing.T) { require.True(t, bond1to1.Equal(resBond)) // add some more records - bond1to2 := types.Delegation{addrDels[0], addrVals[1], sdk.NewDec(9)} - bond1to3 := types.Delegation{addrDels[0], addrVals[2], sdk.NewDec(9)} - bond2to1 := types.Delegation{addrDels[1], addrVals[0], sdk.NewDec(9)} - bond2to2 := types.Delegation{addrDels[1], addrVals[1], sdk.NewDec(9)} - bond2to3 := types.Delegation{addrDels[1], addrVals[2], sdk.NewDec(9)} + bond1to2 := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDec(9)) + bond1to3 := types.NewDelegation(addrDels[0], addrVals[2], sdk.NewDec(9)) + bond2to1 := types.NewDelegation(addrDels[1], addrVals[0], sdk.NewDec(9)) + bond2to2 := types.NewDelegation(addrDels[1], addrVals[1], sdk.NewDec(9)) + bond2to3 := types.NewDelegation(addrDels[1], addrVals[2], sdk.NewDec(9)) keeper.SetDelegation(ctx, bond1to2) keeper.SetDelegation(ctx, bond1to3) keeper.SetDelegation(ctx, bond2to1) @@ -176,29 +171,27 @@ func TestUnbondingDelegation(t *testing.T) { func TestUnbondDelegation(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.NotBondedTokens = sdk.NewInt(10) + startTokens := types.TokensFromTendermintPower(10) + pool.NotBondedTokens = startTokens //create a validator and a delegator to that validator validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, startTokens) + require.Equal(t, startTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) - require.Equal(t, int64(10), pool.BondedTokens.Int64()) - require.Equal(t, int64(10), validator.BondedTokens().Int64()) + require.Equal(t, startTokens, pool.BondedTokens) + require.Equal(t, startTokens, validator.BondedTokens()) - delegation := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) keeper.SetDelegation(ctx, delegation) - amount, err := keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewDec(6)) + bondTokens := types.TokensFromTendermintPower(6) + amount, err := keeper.unbond(ctx, addrDels[0], addrVals[0], sdk.NewDecFromInt(bondTokens)) require.NoError(t, err) - require.Equal(t, int64(6), amount.Int64()) // shares to be added to an unbonding delegation + require.Equal(t, bondTokens, amount) // shares to be added to an unbonding delegation delegation, found := keeper.GetDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) @@ -206,33 +199,31 @@ func TestUnbondDelegation(t *testing.T) { require.True(t, found) pool = keeper.GetPool(ctx) - require.Equal(t, int64(4), delegation.Shares.RoundInt64()) - require.Equal(t, int64(4), validator.BondedTokens().Int64()) - require.Equal(t, int64(6), pool.NotBondedTokens.Int64(), "%v", pool) - require.Equal(t, int64(4), pool.BondedTokens.Int64()) + remainingTokens := startTokens.Sub(bondTokens) + require.Equal(t, remainingTokens, delegation.Shares.RoundInt()) + require.Equal(t, remainingTokens, validator.BondedTokens()) + require.Equal(t, bondTokens, pool.NotBondedTokens, "%v", pool) + require.Equal(t, remainingTokens, pool.BondedTokens) } func TestUnbondingDelegationsMaxEntries(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.NotBondedTokens = sdk.NewInt(10) + startTokens := types.TokensFromTendermintPower(10) + pool.NotBondedTokens = startTokens // create a validator and a delegator to that validator validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, startTokens) + require.Equal(t, startTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) - require.Equal(t, int64(10), pool.BondedTokens.Int64()) - require.Equal(t, int64(10), validator.BondedTokens().Int64()) + require.Equal(t, startTokens, pool.BondedTokens) + require.Equal(t, startTokens, validator.BondedTokens()) - delegation := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) keeper.SetDelegation(ctx, delegation) maxEntries := keeper.MaxEntries(ctx) @@ -262,41 +253,35 @@ func TestUnbondingDelegationsMaxEntries(t *testing.T) { // test removing all self delegation from a validator which should // shift it from the bonded to unbonded state func TestUndelegateSelfDelegation(t *testing.T) { - ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.NotBondedTokens = sdk.NewInt(20) + startTokens := types.TokensFromTendermintPower(20) + pool.NotBondedTokens = startTokens //create a validator with a self-delegation validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + valTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) - selfDelegation := types.Delegation{ - DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()), - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + selfDelegation := types.NewDelegation(sdk.AccAddress(addrVals[0].Bytes()), addrVals[0], issuedShares) keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator keeper.DeleteValidatorByPowerIndex(ctx, validator) - validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + delTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) - delegation := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) keeper.SetDelegation(ctx, delegation) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10)) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(valTokens)) require.NoError(t, err) // end block @@ -305,42 +290,37 @@ func TestUndelegateSelfDelegation(t *testing.T) { validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) - require.Equal(t, int64(10), validator.Tokens.Int64()) + require.Equal(t, delTokens, validator.Tokens) require.Equal(t, sdk.Unbonding, validator.Status) } func TestUndelegateFromUnbondingValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.NotBondedTokens = sdk.NewInt(20) + startTokens := types.TokensFromTendermintPower(20) + pool.NotBondedTokens = startTokens //create a validator with a self-delegation validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + valTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) - selfDelegation := types.Delegation{ - DelegatorAddr: sdk.AccAddress(addrVals[0].Bytes()), - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + selfDelegation := types.NewDelegation(sdk.AccAddress(addrVals[0].Bytes()), addrVals[0], issuedShares) keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator keeper.DeleteValidatorByPowerIndex(ctx, validator) - validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + delTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) - delegation := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) keeper.SetDelegation(ctx, delegation) header := ctx.BlockHeader() @@ -352,7 +332,7 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { // unbond the all self-delegation to put validator in unbonding state val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10)) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(valTokens)) require.NoError(t, err) // end block @@ -389,43 +369,38 @@ func TestUndelegateFromUnbondingValidator(t *testing.T) { func TestUndelegateFromUnbondedValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.NotBondedTokens = sdk.NewInt(20) + startTokens := types.TokensFromTendermintPower(20) + pool.NotBondedTokens = startTokens //create a validator with a self-delegation validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + valTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.Delegation{ - DelegatorAddr: val0AccAddr, - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator keeper.DeleteValidatorByPowerIndex(ctx, validator) - validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + delTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) - delegation := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) keeper.SetDelegation(ctx, delegation) ctx = ctx.WithBlockHeight(10) ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10)) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(valTokens)) require.NoError(t, err) // end block @@ -448,7 +423,8 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { require.Equal(t, validator.Status, sdk.Unbonded) // unbond some of the other delegation's shares - _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(6)) + unbondTokens := types.TokensFromTendermintPower(6) + _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDecFromInt(unbondTokens)) require.NoError(t, err) // no ubd should have been found, coins should have been returned direcly to account @@ -456,55 +432,50 @@ func TestUndelegateFromUnbondedValidator(t *testing.T) { require.False(t, found, "%v", ubd) // unbond rest of the other delegation's shares - _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(4)) + remainingTokens := delTokens.Sub(unbondTokens) + _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDecFromInt(remainingTokens)) require.NoError(t, err) // now validator should now be deleted from state validator, found = keeper.GetValidator(ctx, addrVals[0]) - fmt.Println(validator) - require.False(t, found) + require.False(t, found, "%v", validator) } func TestUnbondingAllDelegationFromValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.NotBondedTokens = sdk.NewInt(20) + startTokens := types.TokensFromTendermintPower(20) + pool.NotBondedTokens = startTokens //create a validator with a self-delegation validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + valTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.Delegation{ - DelegatorAddr: val0AccAddr, - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator keeper.DeleteValidatorByPowerIndex(ctx, validator) - validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + delTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) - delegation := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) keeper.SetDelegation(ctx, delegation) ctx = ctx.WithBlockHeight(10) ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10)) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(valTokens)) require.NoError(t, err) // end block @@ -512,7 +483,7 @@ func TestUnbondingAllDelegationFromValidator(t *testing.T) { require.Equal(t, 1, len(updates)) // unbond all the remaining delegation - _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDec(10)) + _, err = keeper.Undelegate(ctx, addrDels[0], addrVals[0], sdk.NewDecFromInt(delTokens)) require.NoError(t, err) // validator should still be in state and still be in unbonding state @@ -618,21 +589,19 @@ func TestRedelegation(t *testing.T) { func TestRedelegateToSameValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.NotBondedTokens = sdk.NewInt(30) + startTokens := types.TokensFromTendermintPower(30) + pool.NotBondedTokens = startTokens // create a validator with a self-delegation validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + valTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.Delegation{ - DelegatorAddr: val0AccAddr, - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) keeper.SetDelegation(ctx, selfDelegation) _, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[0], sdk.NewDec(5)) @@ -643,28 +612,26 @@ func TestRedelegateToSameValidator(t *testing.T) { func TestRedelegationMaxEntries(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.NotBondedTokens = sdk.NewInt(20) + startTokens := types.TokensFromTendermintPower(20) + pool.NotBondedTokens = startTokens // create a validator with a self-delegation validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + valTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.Delegation{ - DelegatorAddr: val0AccAddr, - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) keeper.SetDelegation(ctx, selfDelegation) // create a second validator validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) - validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) - pool.BondedTokens = pool.BondedTokens.Add(sdk.NewInt(10)) + validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + pool.BondedTokens = pool.BondedTokens.Add(valTokens) keeper.SetPool(ctx, pool) validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) require.Equal(t, sdk.Bonded, validator2.Status) @@ -696,46 +663,42 @@ func TestRedelegationMaxEntries(t *testing.T) { func TestRedelegateSelfDelegation(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.NotBondedTokens = sdk.NewInt(30) + startTokens := types.TokensFromTendermintPower(30) + pool.NotBondedTokens = startTokens //create a validator with a self-delegation validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + valTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.Delegation{ - DelegatorAddr: val0AccAddr, - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) keeper.SetDelegation(ctx, selfDelegation) // create a second validator validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) - validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) - pool.BondedTokens = pool.BondedTokens.Add(sdk.NewInt(10)) + validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) + pool.BondedTokens = pool.BondedTokens.Add(valTokens) keeper.SetPool(ctx, pool) validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) require.Equal(t, sdk.Bonded, validator2.Status) - // create a second delegation to this validator - validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + // create a second delegation to validator 1 + delTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) - delegation := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) keeper.SetDelegation(ctx, delegation) - _, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDec(10)) + _, err := keeper.BeginRedelegation(ctx, val0AccAddr, addrVals[0], addrVals[1], sdk.NewDecFromInt(delTokens)) require.NoError(t, err) // end block @@ -744,49 +707,44 @@ func TestRedelegateSelfDelegation(t *testing.T) { validator, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) - require.Equal(t, int64(10), validator.Tokens.Int64()) + require.Equal(t, valTokens, validator.Tokens) require.Equal(t, sdk.Unbonding, validator.Status) } func TestRedelegateFromUnbondingValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.NotBondedTokens = sdk.NewInt(30) + startTokens := types.TokensFromTendermintPower(30) + pool.NotBondedTokens = startTokens //create a validator with a self-delegation validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + valTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.Delegation{ - DelegatorAddr: val0AccAddr, - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator keeper.DeleteValidatorByPowerIndex(ctx, validator) - validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + delTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) - delegation := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) keeper.SetDelegation(ctx, delegation) // create a second validator validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) - validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) @@ -798,7 +756,7 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { ctx = ctx.WithBlockHeader(header) // unbond the all self-delegation to put validator in unbonding state - _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10)) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(delTokens)) require.NoError(t, err) // end block @@ -820,14 +778,15 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { ctx = ctx.WithBlockHeader(header) // unbond some of the other delegation's shares - _, err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6)) + redelegateTokens := types.TokensFromTendermintPower(6) + _, err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDecFromInt(redelegateTokens)) require.NoError(t, err) // retrieve the unbonding delegation ubd, found := keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) require.True(t, found) require.Len(t, ubd.Entries, 1) - require.True(t, ubd.Entries[0].Balance.IsEqual(sdk.NewInt64Coin(params.BondDenom, 6))) + require.True(t, ubd.Entries[0].Balance.IsEqual(sdk.NewCoin(params.BondDenom, redelegateTokens))) assert.Equal(t, blockHeight, ubd.Entries[0].CreationHeight) assert.True(t, blockTime.Add(params.UnbondingTime).Equal(ubd.Entries[0].CompletionTime)) } @@ -835,42 +794,37 @@ func TestRedelegateFromUnbondingValidator(t *testing.T) { func TestRedelegateFromUnbondedValidator(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 0) pool := keeper.GetPool(ctx) - pool.NotBondedTokens = sdk.NewInt(30) + startTokens := types.TokensFromTendermintPower(30) + pool.NotBondedTokens = startTokens //create a validator with a self-delegation validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, issuedShares := validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + valTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares := validator.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) val0AccAddr := sdk.AccAddress(addrVals[0].Bytes()) - selfDelegation := types.Delegation{ - DelegatorAddr: val0AccAddr, - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + selfDelegation := types.NewDelegation(val0AccAddr, addrVals[0], issuedShares) keeper.SetDelegation(ctx, selfDelegation) // create a second delegation to this validator keeper.DeleteValidatorByPowerIndex(ctx, validator) - validator, pool, issuedShares = validator.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + delTokens := types.TokensFromTendermintPower(10) + validator, pool, issuedShares = validator.AddTokensFromDel(pool, delTokens) + require.Equal(t, delTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) pool = keeper.GetPool(ctx) - delegation := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[0], - Shares: issuedShares, - } + delegation := types.NewDelegation(addrDels[0], addrVals[0], issuedShares) keeper.SetDelegation(ctx, delegation) // create a second validator validator2 := types.NewValidator(addrVals[1], PKs[1], types.Description{}) - validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, sdk.NewInt(10)) - require.Equal(t, int64(10), issuedShares.RoundInt64()) + validator2, pool, issuedShares = validator2.AddTokensFromDel(pool, valTokens) + require.Equal(t, valTokens, issuedShares.RoundInt()) keeper.SetPool(ctx, pool) validator2 = TestingUpdateValidator(keeper, ctx, validator2, true) require.Equal(t, sdk.Bonded, validator2.Status) @@ -879,7 +833,7 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) { ctx = ctx.WithBlockTime(time.Unix(333, 0)) // unbond the all self-delegation to put validator in unbonding state - _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDec(10)) + _, err := keeper.Undelegate(ctx, val0AccAddr, addrVals[0], sdk.NewDecFromInt(delTokens)) require.NoError(t, err) // end block @@ -896,7 +850,8 @@ func TestRedelegateFromUnbondedValidator(t *testing.T) { keeper.unbondingToUnbonded(ctx, validator) // redelegate some of the delegation's shares - _, err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDec(6)) + redelegationTokens := types.TokensFromTendermintPower(6) + _, err = keeper.BeginRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1], sdk.NewDecFromInt(redelegationTokens)) require.NoError(t, err) // no red should have been found diff --git a/x/staking/keeper/keeper.go b/x/staking/keeper/keeper.go index 81b79bf02..73f71a8bf 100644 --- a/x/staking/keeper/keeper.go +++ b/x/staking/keeper/keeper.go @@ -114,20 +114,22 @@ func (k Keeper) GetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress) } // Set the last validator power. -func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power sdk.Int) { +func (k Keeper) SetLastValidatorPower(ctx sdk.Context, operator sdk.ValAddress, power int64) { store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryLengthPrefixed(power) store.Set(GetLastValidatorPowerKey(operator), bz) } // Iterate over last validator powers. -func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, handler func(operator sdk.ValAddress, power sdk.Int) (stop bool)) { +func (k Keeper) IterateLastValidatorPowers(ctx sdk.Context, + handler func(operator sdk.ValAddress, power int64) (stop bool)) { + store := ctx.KVStore(k.storeKey) iter := sdk.KVStorePrefixIterator(store, LastValidatorPowerKey) defer iter.Close() for ; iter.Valid(); iter.Next() { addr := sdk.ValAddress(iter.Key()[len(LastValidatorPowerKey):]) - var power sdk.Int + var power int64 k.cdc.MustUnmarshalBinaryLengthPrefixed(iter.Value(), &power) if handler(addr, power) { break diff --git a/x/staking/keeper/slash.go b/x/staking/keeper/slash.go index 3ca7ffcad..100f32674 100644 --- a/x/staking/keeper/slash.go +++ b/x/staking/keeper/slash.go @@ -29,7 +29,8 @@ func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeigh } // Amount of slashing = slash slashFactor * power at time of infraction - slashAmountDec := sdk.NewDec(power).Mul(slashFactor) + amount := types.TokensFromTendermintPower(power) + slashAmountDec := sdk.NewDecFromInt(amount).Mul(slashFactor) slashAmount := slashAmountDec.TruncateInt() // ref https://github.com/cosmos/cosmos-sdk/issues/1348 diff --git a/x/staking/keeper/slash_test.go b/x/staking/keeper/slash_test.go index 3dc5e1364..fb5beb15d 100644 --- a/x/staking/keeper/slash_test.go +++ b/x/staking/keeper/slash_test.go @@ -14,20 +14,21 @@ import ( // TODO integrate with test_common.go helper (CreateTestInput) // setup helper function - creates two validators -func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) { +func setupHelper(t *testing.T, power int64) (sdk.Context, Keeper, types.Params) { // setup - ctx, _, keeper := CreateTestInput(t, false, amt) + ctx, _, keeper := CreateTestInput(t, false, power) params := keeper.GetParams(ctx) pool := keeper.GetPool(ctx) - numVals := 3 - pool.NotBondedTokens = sdk.NewInt(amt * int64(numVals)) + numVals := int64(3) + amt := types.TokensFromTendermintPower(power) + pool.NotBondedTokens = amt.MulRaw(numVals) // add numVals validators - for i := 0; i < numVals; i++ { + for i := int64(0); i < numVals; i++ { validator := types.NewValidator(addrVals[i], PKs[i], types.Description{}) - validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(amt)) - pool.BondedTokens = pool.BondedTokens.Add(sdk.NewInt(amt)) + validator, pool, _ = validator.AddTokensFromDel(pool, amt) + pool.BondedTokens = pool.BondedTokens.Add(amt) keeper.SetPool(ctx, pool) validator = TestingUpdateValidator(keeper, ctx, validator, true) keeper.SetValidatorByConsAddr(ctx, validator) @@ -120,11 +121,7 @@ func TestSlashRedelegation(t *testing.T) { keeper.SetRedelegation(ctx, rd) // set the associated delegation - del := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[1], - Shares: sdk.NewDec(10), - } + del := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDec(10)) keeper.SetDelegation(ctx, del) // started redelegating prior to the current height, stake didn't contribute to infraction @@ -204,9 +201,9 @@ func TestSlashAtNegativeHeight(t *testing.T) { validator = keeper.mustGetValidator(ctx, validator.OperatorAddr) // power decreased - require.True(sdk.IntEq(t, sdk.NewInt(5), validator.GetPower())) + require.Equal(t, int64(5), validator.GetTendermintPower()) // pool bonded shares decreased - require.Equal(t, int64(5), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(5), oldPool.BondedTokens.Sub(newPool.BondedTokens)) } // tests Slash at the current height @@ -231,9 +228,9 @@ func TestSlashValidatorAtCurrentHeight(t *testing.T) { validator = keeper.mustGetValidator(ctx, validator.OperatorAddr) // power decreased - require.True(sdk.IntEq(t, sdk.NewInt(5), validator.GetPower())) + require.Equal(t, int64(5), validator.GetTendermintPower()) // pool bonded shares decreased - require.Equal(t, int64(5), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(5), oldPool.BondedTokens.Sub(newPool.BondedTokens)) } // tests Slash at a previous height with an unbonding delegation @@ -244,8 +241,9 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // set an unbonding delegation with expiration timestamp beyond which the // unbonding delegation shouldn't be slashed + ubdTokens := types.TokensFromTendermintPower(4) ubd := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, - time.Unix(0, 0), sdk.NewInt64Coin(params.BondDenom, 4)) + time.Unix(0, 0), sdk.NewCoin(params.BondDenom, ubdTokens)) keeper.SetUnbondingDelegation(ctx, ubd) // slash validator for the first time @@ -264,11 +262,11 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { require.True(t, found) require.Len(t, ubd.Entries, 1) // balance decreased - require.Equal(t, sdk.NewInt(2), ubd.Entries[0].Balance.Amount) + require.Equal(t, types.TokensFromTendermintPower(2), ubd.Entries[0].Balance.Amount) // read updated pool newPool := keeper.GetPool(ctx) // bonded tokens burned - require.Equal(t, int64(3), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(3), oldPool.BondedTokens.Sub(newPool.BondedTokens)) // read updated validator validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) @@ -276,7 +274,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // was still bonded at the time of discovery and was slashed by half, 4 stake // bonded at the time of discovery hadn't been bonded at the time of infraction // and wasn't slashed - require.True(sdk.IntEq(t, sdk.NewInt(7), validator.GetPower())) + require.Equal(t, int64(7), validator.GetTendermintPower()) // slash validator again ctx = ctx.WithBlockHeight(13) @@ -289,12 +287,12 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // read updated pool newPool = keeper.GetPool(ctx) // bonded tokens burned again - require.Equal(t, int64(6), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(6), oldPool.BondedTokens.Sub(newPool.BondedTokens)) // read updated validator validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) // power decreased by 3 again - require.True(sdk.IntEq(t, sdk.NewInt(4), validator.GetPower())) + require.Equal(t, int64(4), validator.GetTendermintPower()) // slash validator again // all originally bonded stake has been slashed, so this will have no effect @@ -310,12 +308,12 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // read updated pool newPool = keeper.GetPool(ctx) // bonded tokens burned again - require.Equal(t, int64(9), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(9), oldPool.BondedTokens.Sub(newPool.BondedTokens)) // read updated validator validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) // power decreased by 3 again - require.True(sdk.IntEq(t, sdk.NewInt(1), validator.GetPower())) + require.Equal(t, int64(1), validator.GetTendermintPower()) // slash validator again // all originally bonded stake has been slashed, so this will have no effect @@ -331,7 +329,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // read updated pool newPool = keeper.GetPool(ctx) // just 1 bonded token burned again since that's all the validator now has - require.Equal(t, int64(10), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(10), oldPool.BondedTokens.Sub(newPool.BondedTokens)) // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) // read updated validator @@ -348,22 +346,19 @@ func TestSlashWithRedelegation(t *testing.T) { fraction := sdk.NewDecWithPrec(5, 1) // set a redelegation + rdTokens := types.TokensFromTendermintPower(6) rd := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, - time.Unix(0, 0), sdk.NewInt64Coin(params.BondDenom, 6), sdk.NewDec(6), - sdk.NewDec(6)) + time.Unix(0, 0), sdk.NewCoin(params.BondDenom, rdTokens), sdk.NewDecFromInt(rdTokens), + sdk.NewDecFromInt(rdTokens)) keeper.SetRedelegation(ctx, rd) // set the associated delegation - del := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[1], - Shares: sdk.NewDec(6), - } + del := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDecFromInt(rdTokens)) keeper.SetDelegation(ctx, del) // update bonded tokens pool := keeper.GetPool(ctx) - pool.BondedTokens = pool.BondedTokens.Add(sdk.NewInt(6)) + pool.BondedTokens = pool.BondedTokens.Add(rdTokens) keeper.SetPool(ctx, pool) // slash validator @@ -378,11 +373,11 @@ func TestSlashWithRedelegation(t *testing.T) { require.True(t, found) require.Len(t, rd.Entries, 1) // balance decreased - require.Equal(t, sdk.NewInt(3), rd.Entries[0].Balance.Amount) + require.Equal(t, types.TokensFromTendermintPower(3), rd.Entries[0].Balance.Amount) // read updated pool newPool := keeper.GetPool(ctx) // bonded tokens burned - require.Equal(t, int64(5), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(5), oldPool.BondedTokens.Sub(newPool.BondedTokens)) // read updated validator validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) @@ -390,7 +385,7 @@ func TestSlashWithRedelegation(t *testing.T) { // was still bonded at the time of discovery and was slashed by half, 4 stake // bonded at the time of discovery hadn't been bonded at the time of infraction // and wasn't slashed - require.True(sdk.IntEq(t, sdk.NewInt(8), validator.GetPower())) + require.Equal(t, int64(8), validator.GetTendermintPower()) // slash the validator again ctx = ctx.WithBlockHeight(12) @@ -407,12 +402,12 @@ func TestSlashWithRedelegation(t *testing.T) { // read updated pool newPool = keeper.GetPool(ctx) // seven bonded tokens burned - require.Equal(t, int64(12), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(12), oldPool.BondedTokens.Sub(newPool.BondedTokens)) // read updated validator validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) // power decreased by 4 - require.True(sdk.IntEq(t, sdk.NewInt(4), validator.GetPower())) + require.Equal(t, int64(4), validator.GetTendermintPower()) // slash the validator again, by 100% ctx = ctx.WithBlockHeight(12) @@ -429,7 +424,7 @@ func TestSlashWithRedelegation(t *testing.T) { // read updated pool newPool = keeper.GetPool(ctx) // four more bonded tokens burned - require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(16), oldPool.BondedTokens.Sub(newPool.BondedTokens)) // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) // read updated validator @@ -454,7 +449,7 @@ func TestSlashWithRedelegation(t *testing.T) { // read updated pool newPool = keeper.GetPool(ctx) // no more bonded tokens burned - require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(16), oldPool.BondedTokens.Sub(newPool.BondedTokens)) // read updated validator // power still zero, still in unbonding period validator, _ = keeper.GetValidatorByConsAddr(ctx, consAddr) @@ -468,23 +463,21 @@ func TestSlashBoth(t *testing.T) { // set a redelegation with expiration timestamp beyond which the // redelegation shouldn't be slashed + rdATokens := types.TokensFromTendermintPower(6) rdA := types.NewRedelegation(addrDels[0], addrVals[0], addrVals[1], 11, - time.Unix(0, 0), sdk.NewInt64Coin(params.BondDenom, 6), sdk.NewDec(6), - sdk.NewDec(6)) + time.Unix(0, 0), sdk.NewCoin(params.BondDenom, rdATokens), + sdk.NewDecFromInt(rdATokens), sdk.NewDecFromInt(rdATokens)) keeper.SetRedelegation(ctx, rdA) // set the associated delegation - delA := types.Delegation{ - DelegatorAddr: addrDels[0], - ValidatorAddr: addrVals[1], - Shares: sdk.NewDec(6), - } + delA := types.NewDelegation(addrDels[0], addrVals[1], sdk.NewDecFromInt(rdATokens)) keeper.SetDelegation(ctx, delA) // set an unbonding delegation with expiration timestamp (beyond which the // unbonding delegation shouldn't be slashed) + ubdATokens := types.TokensFromTendermintPower(4) ubdA := types.NewUnbondingDelegation(addrDels[0], addrVals[0], 11, - time.Unix(0, 0), sdk.NewInt64Coin(params.BondDenom, 4)) + time.Unix(0, 0), sdk.NewCoin(params.BondDenom, ubdATokens)) keeper.SetUnbondingDelegation(ctx, ubdA) // slash validator @@ -500,16 +493,16 @@ func TestSlashBoth(t *testing.T) { require.True(t, found) require.Len(t, rdA.Entries, 1) // balance decreased - require.Equal(t, sdk.NewInt(3), rdA.Entries[0].Balance.Amount) + require.Equal(t, types.TokensFromTendermintPower(3), rdA.Entries[0].Balance.Amount) // read updated pool newPool := keeper.GetPool(ctx) // not-bonded tokens burned - require.Equal(t, int64(2), oldPool.NotBondedTokens.Sub(newPool.NotBondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(2), oldPool.NotBondedTokens.Sub(newPool.NotBondedTokens)) // bonded tokens burned - require.Equal(t, int64(3), oldPool.BondedTokens.Sub(newPool.BondedTokens).Int64()) + require.Equal(t, types.TokensFromTendermintPower(3), oldPool.BondedTokens.Sub(newPool.BondedTokens)) // read updated validator validator, found = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) require.True(t, found) // power not decreased, all stake was bonded since - require.True(sdk.IntEq(t, sdk.NewInt(10), validator.GetPower())) + require.Equal(t, int64(10), validator.GetTendermintPower()) } diff --git a/x/staking/keeper/test_common.go b/x/staking/keeper/test_common.go index aa95c885c..d01f307b7 100644 --- a/x/staking/keeper/test_common.go +++ b/x/staking/keeper/test_common.go @@ -75,7 +75,10 @@ func MakeTestCodec() *codec.Codec { } // hogpodge of all sorts of input required for testing -func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context, auth.AccountKeeper, Keeper) { +// init power is converted to an amount of tokens +func CreateTestInput(t *testing.T, isCheckTx bool, initPower int64) (sdk.Context, auth.AccountKeeper, Keeper) { + + initCoins := types.TokensFromTendermintPower(initPower) keyStaking := sdk.NewKVStoreKey(types.StoreKey) tkeyStaking := sdk.NewTransientStoreKey(types.TStoreKey) @@ -126,10 +129,10 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context for _, addr := range Addrs { pool := keeper.GetPool(ctx) _, _, err := ck.AddCoins(ctx, addr, sdk.Coins{ - {keeper.BondDenom(ctx), sdk.NewInt(initCoins)}, + {keeper.BondDenom(ctx), initCoins}, }) require.Nil(t, err) - pool.NotBondedTokens = pool.NotBondedTokens.Add(sdk.NewInt(initCoins)) + pool.NotBondedTokens = pool.NotBondedTokens.Add(initCoins) keeper.SetPool(ctx, pool) } diff --git a/x/staking/keeper/val_state_change.go b/x/staking/keeper/val_state_change.go index 70ddb656b..f6399abb8 100644 --- a/x/staking/keeper/val_state_change.go +++ b/x/staking/keeper/val_state_change.go @@ -50,7 +50,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab // if we get to a zero-power validator (which we don't bond), // there are no more possible bonded validators - if validator.Tokens.IsZero() { + if validator.PotentialTendermintPower() == 0 { break } @@ -72,8 +72,8 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab oldPowerBytes, found := last[valAddrBytes] // calculate the new power bytes - newPower := validator.BondedTokens().Int64() - newPowerBytes := k.cdc.MustMarshalBinaryLengthPrefixed(sdk.NewInt(newPower)) + newPower := validator.TendermintPower() + newPowerBytes := k.cdc.MustMarshalBinaryLengthPrefixed(newPower) // update the validator set if power has changed if !found || !bytes.Equal(oldPowerBytes, newPowerBytes) { updates = append(updates, validator.ABCIValidatorUpdate()) @@ -85,7 +85,7 @@ func (k Keeper) ApplyAndReturnValidatorSetUpdates(ctx sdk.Context) (updates []ab } // set validator power on lookup index. - k.SetLastValidatorPower(ctx, valAddr, sdk.NewInt(newPower)) + k.SetLastValidatorPower(ctx, valAddr, newPower) } // validator still in the validator set, so delete from the copy diff --git a/x/staking/keeper/validator.go b/x/staking/keeper/validator.go index d218ca966..8e6330cff 100644 --- a/x/staking/keeper/validator.go +++ b/x/staking/keeper/validator.go @@ -18,6 +18,13 @@ type cachedValidator struct { marshalled string // marshalled amino bytes for the validator object (not operator address) } +func newCachedValidator(val types.Validator, marshalled string) cachedValidator { + return cachedValidator{ + val: val, + marshalled: marshalled, + } +} + // get a single validator func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator types.Validator, found bool) { store := ctx.KVStore(k.storeKey) @@ -37,8 +44,8 @@ func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator ty // amino bytes weren't found in cache, so amino unmarshal and add it to the cache validator = types.MustUnmarshalValidator(k.cdc, value) - cachedVal := cachedValidator{validator, strValue} - k.validatorCache[strValue] = cachedValidator{validator, strValue} + cachedVal := newCachedValidator(validator, strValue) + k.validatorCache[strValue] = newCachedValidator(validator, strValue) k.validatorCacheList.PushBack(cachedVal) // if the cache is too big, pop off the last element from it diff --git a/x/staking/keeper/validator_test.go b/x/staking/keeper/validator_test.go index 9f605312a..13b7a0836 100644 --- a/x/staking/keeper/validator_test.go +++ b/x/staking/keeper/validator_test.go @@ -22,13 +22,14 @@ func TestSetValidator(t *testing.T) { valPubKey := PKs[0] valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) + valTokens := types.TokensFromTendermintPower(10) // test how the validator is set from a purely unbonbed pool validator := types.NewValidator(valAddr, valPubKey, types.Description{}) - validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(10)) + validator, pool, _ = validator.AddTokensFromDel(pool, valTokens) require.Equal(t, sdk.Unbonded, validator.Status) - assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.Tokens)) - assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) + assert.Equal(t, valTokens, validator.Tokens) + assert.Equal(t, valTokens, validator.DelegatorShares.RoundInt()) keeper.SetPool(ctx, pool) keeper.SetValidator(ctx, validator) keeper.SetValidatorByPowerIndex(ctx, validator) @@ -42,8 +43,8 @@ func TestSetValidator(t *testing.T) { // after the save the validator should be bonded require.Equal(t, sdk.Bonded, validator.Status) - assert.True(sdk.IntEq(t, sdk.NewInt(10), validator.Tokens)) - assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares)) + assert.Equal(t, valTokens, validator.Tokens) + assert.Equal(t, valTokens, validator.DelegatorShares.RoundInt()) // Check each store for being saved resVal, found := keeper.GetValidator(ctx, valAddr) @@ -123,8 +124,8 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { keeper.SetParams(ctx, params) // create a random pool - pool.NotBondedTokens = sdk.NewInt(10000) - pool.BondedTokens = sdk.NewInt(1234) + pool.NotBondedTokens = types.TokensFromTendermintPower(10000) + pool.BondedTokens = types.TokensFromTendermintPower(1234) keeper.SetPool(ctx, pool) validators := make([]types.Validator, numVals) @@ -132,7 +133,8 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { moniker := fmt.Sprintf("val#%d", int64(i)) val := types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker}) val.BondHeight = int64(i) - val, pool, _ = val.AddTokensFromDel(pool, sdk.NewInt(int64((i+1)*10))) + delTokens := types.TokensFromTendermintPower(int64((i + 1) * 10)) + val, pool, _ = val.AddTokensFromDel(pool, delTokens) keeper.SetPool(ctx, pool) val = TestingUpdateValidator(keeper, ctx, val, true) @@ -144,7 +146,8 @@ func TestUpdateBondedValidatorsDecreaseCliff(t *testing.T) { // remove enough tokens to kick out the validator below the current cliff // validator and next in line cliff validator keeper.DeleteValidatorByPowerIndex(ctx, nextCliffVal) - nextCliffVal, pool, _ = nextCliffVal.RemoveDelShares(pool, sdk.NewDec(21)) + shares := types.TokensFromTendermintPower(21) + nextCliffVal, pool, _ = nextCliffVal.RemoveDelShares(pool, sdk.NewDecFromInt(shares)) keeper.SetPool(ctx, pool) nextCliffVal = TestingUpdateValidator(keeper, ctx, nextCliffVal, true) @@ -173,13 +176,14 @@ func TestSlashToZeroPowerRemoved(t *testing.T) { // add a validator validator := types.NewValidator(addrVals[0], PKs[0], types.Description{}) - validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(100)) + valTokens := types.TokensFromTendermintPower(100) + validator, pool, _ = validator.AddTokensFromDel(pool, valTokens) require.Equal(t, sdk.Unbonded, validator.Status) - require.Equal(t, int64(100), validator.Tokens.Int64()) + require.Equal(t, valTokens, validator.Tokens) keeper.SetPool(ctx, pool) keeper.SetValidatorByConsAddr(ctx, validator) validator = TestingUpdateValidator(keeper, ctx, validator, true) - require.Equal(t, int64(100), validator.Tokens.Int64(), "\nvalidator %v\npool %v", validator, pool) + require.Equal(t, valTokens, validator.Tokens, "\nvalidator %v\npool %v", validator, pool) // slash the validator by 100% consAddr0 := sdk.ConsAddress(PKs[0].Address()) @@ -198,17 +202,18 @@ func TestValidatorBasics(t *testing.T) { //construct the validators var validators [3]types.Validator - amts := []int64{9, 8, 7} - for i, amt := range amts { + powers := []int64{9, 8, 7} + for i, power := range powers { validators[i] = types.NewValidator(addrVals[i], PKs[i], types.Description{}) validators[i].Status = sdk.Unbonded validators[i].Tokens = sdk.ZeroInt() - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) } - assert.True(sdk.IntEq(t, sdk.NewInt(9), validators[0].Tokens)) - assert.True(sdk.IntEq(t, sdk.NewInt(8), validators[1].Tokens)) - assert.True(sdk.IntEq(t, sdk.NewInt(7), validators[2].Tokens)) + assert.Equal(t, types.TokensFromTendermintPower(9), validators[0].Tokens) + assert.Equal(t, types.TokensFromTendermintPower(8), validators[1].Tokens) + assert.Equal(t, types.TokensFromTendermintPower(7), validators[2].Tokens) // check the empty keeper first _, found := keeper.GetValidator(ctx, addrVals[0]) @@ -241,15 +246,15 @@ func TestValidatorBasics(t *testing.T) { require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validators[0], resVals[0])) assert.Equal(t, sdk.Bonded, validators[0].Status) - assert.True(sdk.IntEq(t, sdk.NewInt(9), validators[0].BondedTokens())) + assert.True(sdk.IntEq(t, types.TokensFromTendermintPower(9), validators[0].BondedTokens())) pool = keeper.GetPool(ctx) assert.True(sdk.IntEq(t, pool.BondedTokens, validators[0].BondedTokens())) // modify a records, save, and retrieve validators[0].Status = sdk.Bonded - validators[0].Tokens = sdk.NewInt(10) - validators[0].DelegatorShares = sdk.NewDec(10) + validators[0].Tokens = types.TokensFromTendermintPower(10) + validators[0].DelegatorShares = sdk.NewDecFromInt(validators[0].Tokens) validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) resVal, found = keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) @@ -437,18 +442,19 @@ func TestGetValidatorsEdgeCases(t *testing.T) { keeper.SetParams(ctx, params) // initialize some validators into the state - amts := []int64{0, 100, 400, 400} + powers := []int64{0, 100, 400, 400} var validators [4]types.Validator - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) moniker := fmt.Sprintf("val#%d", int64(i)) validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{Moniker: moniker}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) validators[i] = TestingUpdateValidator(keeper, ctx, validators[i], true) } - for i := range amts { + for i := range powers { validators[i], found = keeper.GetValidator(ctx, validators[i].OperatorAddr) require.True(t, found) } @@ -459,7 +465,8 @@ func TestGetValidatorsEdgeCases(t *testing.T) { pool := keeper.GetPool(ctx) keeper.DeleteValidatorByPowerIndex(ctx, validators[0]) - validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(500)) + delTokens := types.TokensFromTendermintPower(500) + validators[0], pool, _ = validators[0].AddTokensFromDel(pool, delTokens) keeper.SetPool(ctx, pool) validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) resValidators = keeper.GetBondedValidatorsByPower(ctx) @@ -524,9 +531,12 @@ func TestValidatorBondHeight(t *testing.T) { validators[1] = types.NewValidator(sdk.ValAddress(Addrs[1]), PKs[1], types.Description{}) validators[2] = types.NewValidator(sdk.ValAddress(Addrs[2]), PKs[2], types.Description{}) - validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(200)) - validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(100)) - validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(100)) + tokens0 := types.TokensFromTendermintPower(200) + tokens1 := types.TokensFromTendermintPower(100) + tokens2 := types.TokensFromTendermintPower(100) + validators[0], pool, _ = validators[0].AddTokensFromDel(pool, tokens0) + validators[1], pool, _ = validators[1].AddTokensFromDel(pool, tokens1) + validators[2], pool, _ = validators[2].AddTokensFromDel(pool, tokens2) keeper.SetPool(ctx, pool) validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) @@ -546,8 +556,9 @@ func TestValidatorBondHeight(t *testing.T) { assert.True(ValEq(t, validators[1], resValidators[1])) keeper.DeleteValidatorByPowerIndex(ctx, validators[1]) keeper.DeleteValidatorByPowerIndex(ctx, validators[2]) - validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(50)) - validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(50)) + delTokens := types.TokensFromTendermintPower(50) + validators[1], pool, _ = validators[1].AddTokensFromDel(pool, delTokens) + validators[2], pool, _ = validators[2].AddTokensFromDel(pool, delTokens) keeper.SetPool(ctx, pool) validators[2] = TestingUpdateValidator(keeper, ctx, validators[2], true) resValidators = keeper.GetBondedValidatorsByPower(ctx) @@ -565,16 +576,17 @@ func TestFullValidatorSetPowerChange(t *testing.T) { keeper.SetParams(ctx, params) // initialize some validators into the state - amts := []int64{0, 100, 400, 400, 200} + powers := []int64{0, 100, 400, 400, 200} var validators [5]types.Validator - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) TestingUpdateValidator(keeper, ctx, validators[i], true) } - for i := range amts { + for i := range powers { var found bool validators[i], found = keeper.GetValidator(ctx, validators[i].OperatorAddr) require.True(t, found) @@ -591,7 +603,8 @@ func TestFullValidatorSetPowerChange(t *testing.T) { // test a swap in voting power pool := keeper.GetPool(ctx) - validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(600)) + tokens := types.TokensFromTendermintPower(600) + validators[0], pool, _ = validators[0].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], true) resValidators = keeper.GetBondedValidatorsByPower(ctx) @@ -603,16 +616,17 @@ func TestFullValidatorSetPowerChange(t *testing.T) { func TestApplyAndReturnValidatorSetUpdatesAllNone(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) - amts := []int64{10, 20} + powers := []int64{10, 20} var validators [2]types.Validator - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) valPubKey := PKs[i+1] valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) } @@ -635,12 +649,14 @@ func TestApplyAndReturnValidatorSetUpdatesAllNone(t *testing.T) { func TestApplyAndReturnValidatorSetUpdatesIdentical(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) - amts := []int64{10, 20} + powers := []int64{10, 20} var validators [2]types.Validator - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) } validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) @@ -657,12 +673,14 @@ func TestApplyAndReturnValidatorSetUpdatesIdentical(t *testing.T) { func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) - amts := []int64{10, 20} + powers := []int64{10, 20} var validators [2]types.Validator - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) } validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) @@ -672,7 +690,7 @@ func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) { // test single value change // tendermintUpdate set: {} -> {c1'} validators[0].Status = sdk.Bonded - validators[0].Tokens = sdk.NewInt(600) + validators[0].Tokens = types.TokensFromTendermintPower(600) validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) @@ -684,12 +702,14 @@ func TestApplyAndReturnValidatorSetUpdatesSingleValueChange(t *testing.T) { func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) - amts := []int64{10, 20} + powers := []int64{10, 20} var validators [2]types.Validator - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) } validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) @@ -699,8 +719,10 @@ func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) { // test multiple value change // tendermintUpdate set: {c1, c3} -> {c1', c3'} pool := keeper.GetPool(ctx) - validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(190)) - validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(80)) + delTokens1 := types.TokensFromTendermintPower(190) + delTokens2 := types.TokensFromTendermintPower(80) + validators[0], pool, _ = validators[0].AddTokensFromDel(pool, delTokens1) + validators[1], pool, _ = validators[1].AddTokensFromDel(pool, delTokens2) keeper.SetPool(ctx, pool) validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) @@ -714,14 +736,17 @@ func TestApplyAndReturnValidatorSetUpdatesMultipleValueChange(t *testing.T) { func TestApplyAndReturnValidatorSetUpdatesInserted(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) - amts := []int64{10, 20, 5, 15, 25} + powers := []int64{10, 20, 5, 15, 25} var validators [5]types.Validator - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) } + validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) @@ -760,12 +785,14 @@ func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) { params.MaxValidators = 2 keeper.SetParams(ctx, params) - amts := []int64{10, 20, 5} + powers := []int64{10, 20, 5} var validators [5]types.Validator - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) } validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) @@ -783,7 +810,8 @@ func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) { require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) pool := keeper.GetPool(ctx) - validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(10)) + tokens := types.TokensFromTendermintPower(10) + validators[2], pool, _ = validators[2].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) keeper.SetValidator(ctx, validators[2]) keeper.SetValidatorByPowerIndex(ctx, validators[2]) @@ -797,12 +825,14 @@ func TestApplyAndReturnValidatorSetUpdatesWithCliffValidator(t *testing.T) { func TestApplyAndReturnValidatorSetUpdatesPowerDecrease(t *testing.T) { ctx, _, keeper := CreateTestInput(t, false, 1000) - amts := []int64{100, 100} + powers := []int64{100, 100} var validators [2]types.Validator - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) } validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) @@ -810,21 +840,23 @@ func TestApplyAndReturnValidatorSetUpdatesPowerDecrease(t *testing.T) { require.Equal(t, 2, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) // check initial power - require.Equal(t, int64(100), validators[0].GetPower().Int64()) - require.Equal(t, int64(100), validators[1].GetPower().Int64()) + require.Equal(t, int64(100), validators[0].GetTendermintPower()) + require.Equal(t, int64(100), validators[1].GetTendermintPower()) // test multiple value change // tendermintUpdate set: {c1, c3} -> {c1', c3'} pool := keeper.GetPool(ctx) - validators[0], pool, _ = validators[0].RemoveDelShares(pool, sdk.NewDec(20)) - validators[1], pool, _ = validators[1].RemoveDelShares(pool, sdk.NewDec(30)) + delTokens1 := types.TokensFromTendermintPower(20) + delTokens2 := types.TokensFromTendermintPower(30) + validators[0], pool, _ = validators[0].RemoveDelShares(pool, sdk.NewDecFromInt(delTokens1)) + validators[1], pool, _ = validators[1].RemoveDelShares(pool, sdk.NewDecFromInt(delTokens2)) keeper.SetPool(ctx, pool) validators[0] = TestingUpdateValidator(keeper, ctx, validators[0], false) validators[1] = TestingUpdateValidator(keeper, ctx, validators[1], false) // power has changed - require.Equal(t, int64(80), validators[0].GetPower().Int64()) - require.Equal(t, int64(70), validators[1].GetPower().Int64()) + require.Equal(t, int64(80), validators[0].GetTendermintPower()) + require.Equal(t, int64(70), validators[1].GetTendermintPower()) // Tendermint updates should reflect power change updates := keeper.ApplyAndReturnValidatorSetUpdates(ctx) @@ -840,17 +872,18 @@ func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) { keeper.SetParams(ctx, params) - amts := []int64{100, 100} + powers := []int64{100, 100} var validators [2]types.Validator // initialize some validators into the state - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) valPubKey := PKs[i+1] valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) keeper.SetValidator(ctx, validators[i]) @@ -868,10 +901,11 @@ func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) { require.Equal(t, 0, len(keeper.ApplyAndReturnValidatorSetUpdates(ctx))) // update initial validator set - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) keeper.DeleteValidatorByPowerIndex(ctx, validators[i]) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) keeper.SetValidator(ctx, validators[i]) @@ -900,7 +934,8 @@ func TestApplyAndReturnValidatorSetUpdatesNewValidator(t *testing.T) { valAddr = sdk.ValAddress(valPubKey.Address().Bytes()) validator = types.NewValidator(valAddr, valPubKey, types.Description{}) - validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(500)) + tokens := types.TokensFromTendermintPower(500) + validator, pool, _ = validator.AddTokensFromDel(pool, tokens) keeper.SetValidator(ctx, validator) keeper.SetValidatorByPowerIndex(ctx, validator) keeper.SetPool(ctx, pool) @@ -923,18 +958,19 @@ func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) { keeper.SetParams(ctx, params) - amts := []int64{100, 200, 300} + powers := []int64{100, 200, 300} var validators [3]types.Validator // initialize some validators into the state - for i, amt := range amts { + for i, power := range powers { pool := keeper.GetPool(ctx) moniker := fmt.Sprintf("%d", i) valPubKey := PKs[i+1] valAddr := sdk.ValAddress(valPubKey.Address().Bytes()) validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{Moniker: moniker}) - validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt)) + tokens := types.TokensFromTendermintPower(power) + validators[i], pool, _ = validators[i].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) keeper.SetValidator(ctx, validators[i]) keeper.SetValidatorByPowerIndex(ctx, validators[i]) @@ -959,7 +995,8 @@ func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) { require.True(t, found) keeper.DeleteValidatorByPowerIndex(ctx, validators[0]) - validators[0], pool, _ = validators[0].AddTokensFromDel(pool, sdk.NewInt(1)) + tokens := types.TokensFromTendermintPower(1) + validators[0], pool, _ = validators[0].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) keeper.SetValidator(ctx, validators[0]) keeper.SetValidatorByPowerIndex(ctx, validators[0]) @@ -984,7 +1021,8 @@ func TestApplyAndReturnValidatorSetUpdatesBondTransition(t *testing.T) { require.Equal(t, 0, len(updates)) keeper.DeleteValidatorByPowerIndex(ctx, validators[1]) - validators[1], pool, _ = validators[1].AddTokensFromDel(pool, sdk.NewInt(250)) + tokens = types.TokensFromTendermintPower(250) + validators[1], pool, _ = validators[1].AddTokensFromDel(pool, tokens) keeper.SetPool(ctx, pool) keeper.SetValidator(ctx, validators[1]) keeper.SetValidatorByPowerIndex(ctx, validators[1]) diff --git a/x/staking/querier/querier_test.go b/x/staking/querier/querier_test.go index 3793e119a..58dfc5c6c 100644 --- a/x/staking/querier/querier_test.go +++ b/x/staking/querier/querier_test.go @@ -177,7 +177,8 @@ func TestQueryDelegation(t *testing.T) { keeper.SetValidator(ctx, val2) keeper.SetValidatorByPowerIndex(ctx, val2) - keeper.Delegate(ctx, addrAcc2, sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(20)), val1, true) + delTokens := types.TokensFromTendermintPower(20) + keeper.Delegate(ctx, addrAcc2, sdk.NewCoin(types.DefaultBondDenom, delTokens), val1, true) // apply TM updates keeper.ApplyAndReturnValidatorSetUpdates(ctx) @@ -296,7 +297,8 @@ func TestQueryDelegation(t *testing.T) { require.Equal(t, delegationsRes[0], delegation) // Query unbonging delegation - _, err = keeper.Undelegate(ctx, addrAcc2, val1.OperatorAddr, sdk.NewDec(10)) + unbondingTokens := types.TokensFromTendermintPower(10) + _, err = keeper.Undelegate(ctx, addrAcc2, val1.OperatorAddr, sdk.NewDecFromInt(unbondingTokens)) require.Nil(t, err) queryBondParams = NewQueryBondsParams(addrAcc2, addrVal1) @@ -348,7 +350,9 @@ func TestQueryDelegation(t *testing.T) { require.NotNil(t, err) // Query redelegation - _, err = keeper.BeginRedelegation(ctx, addrAcc2, val1.OperatorAddr, val2.OperatorAddr, sdk.NewDec(10)) + redelegationTokens := types.TokensFromTendermintPower(10) + _, err = keeper.BeginRedelegation(ctx, addrAcc2, val1.OperatorAddr, + val2.OperatorAddr, sdk.NewDecFromInt(redelegationTokens)) require.Nil(t, err) redel, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddr, val2.OperatorAddr) require.True(t, found) @@ -381,10 +385,12 @@ func TestQueryRedelegations(t *testing.T) { keeper.SetValidator(ctx, val1) keeper.SetValidator(ctx, val2) - keeper.Delegate(ctx, addrAcc2, sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(100)), val1, true) + delAmount := types.TokensFromTendermintPower(100) + keeper.Delegate(ctx, addrAcc2, sdk.NewCoin(types.DefaultBondDenom, delAmount), val1, true) _ = keeper.ApplyAndReturnValidatorSetUpdates(ctx) - keeper.BeginRedelegation(ctx, addrAcc2, val1.GetOperator(), val2.GetOperator(), sdk.NewDec(20)) + rdAmount := types.TokensFromTendermintPower(20) + keeper.BeginRedelegation(ctx, addrAcc2, val1.GetOperator(), val2.GetOperator(), sdk.NewDecFromInt(rdAmount)) keeper.ApplyAndReturnValidatorSetUpdates(ctx) redelegation, found := keeper.GetRedelegation(ctx, addrAcc2, val1.OperatorAddr, val2.OperatorAddr) diff --git a/x/staking/simulation/invariants.go b/x/staking/simulation/invariants.go index a5759483e..28cce38a4 100644 --- a/x/staking/simulation/invariants.go +++ b/x/staking/simulation/invariants.go @@ -11,7 +11,6 @@ import ( "github.com/cosmos/cosmos-sdk/x/mock/simulation" "github.com/cosmos/cosmos-sdk/x/staking" "github.com/cosmos/cosmos-sdk/x/staking/keeper" - stakingTypes "github.com/cosmos/cosmos-sdk/x/staking/types" ) // AllInvariants runs all invariants of the staking module. @@ -55,7 +54,7 @@ func SupplyInvariants(ck bank.Keeper, k staking.Keeper, loose := sdk.ZeroDec() bonded := sdk.ZeroDec() am.IterateAccounts(ctx, func(acc auth.Account) bool { - loose = loose.Add(sdk.NewDecFromInt(acc.GetCoins().AmountOf(stakingTypes.DefaultBondDenom))) + loose = loose.Add(sdk.NewDecFromInt(acc.GetCoins().AmountOf(staking.DefaultBondDenom))) return false }) k.IterateUnbondingDelegations(ctx, func(_ int64, ubd staking.UnbondingDelegation) bool { @@ -67,7 +66,7 @@ func SupplyInvariants(ck bank.Keeper, k staking.Keeper, k.IterateValidators(ctx, func(_ int64, validator sdk.Validator) bool { switch validator.GetStatus() { case sdk.Bonded: - bonded = bonded.Add(sdk.NewDecFromInt(validator.GetPower())) + bonded = bonded.Add(sdk.NewDecFromInt(validator.GetBondedTokens())) case sdk.Unbonding, sdk.Unbonded: loose = loose.Add(sdk.NewDecFromInt(validator.GetTokens())) } @@ -77,13 +76,13 @@ func SupplyInvariants(ck bank.Keeper, k staking.Keeper, feePool := d.GetFeePool(ctx) // add outstanding fees - loose = loose.Add(sdk.NewDecFromInt(f.GetCollectedFees(ctx).AmountOf(stakingTypes.DefaultBondDenom))) + loose = loose.Add(sdk.NewDecFromInt(f.GetCollectedFees(ctx).AmountOf(staking.DefaultBondDenom))) // add community pool - loose = loose.Add(feePool.CommunityPool.AmountOf(stakingTypes.DefaultBondDenom)) + loose = loose.Add(feePool.CommunityPool.AmountOf(staking.DefaultBondDenom)) // add yet-to-be-withdrawn - loose = loose.Add(d.GetOutstandingRewards(ctx).AmountOf(stakingTypes.DefaultBondDenom)) + loose = loose.Add(d.GetOutstandingRewards(ctx).AmountOf(staking.DefaultBondDenom)) // Not-bonded tokens should equal coin supply plus unbonding delegations // plus tokens on unbonded validators @@ -119,7 +118,8 @@ func NonNegativePowerInvariant(k staking.Keeper) simulation.Invariant { if !bytes.Equal(iterator.Key(), powerKey) { return fmt.Errorf("power store invariance:\n\tvalidator.Power: %v"+ - "\n\tkey should be: %v\n\tkey in store: %v", validator.GetPower(), powerKey, iterator.Key()) + "\n\tkey should be: %v\n\tkey in store: %v", + validator.GetTendermintPower(), powerKey, iterator.Key()) } if validator.Tokens.IsNegative() { diff --git a/x/staking/staking.go b/x/staking/staking.go index 53d37fd06..3544c2d2c 100644 --- a/x/staking/staking.go +++ b/x/staking/staking.go @@ -14,6 +14,7 @@ type ( Validators = types.Validators Description = types.Description Commission = types.Commission + CommissionMsg = types.CommissionMsg Delegation = types.Delegation Delegations = types.Delegations UnbondingDelegation = types.UnbondingDelegation @@ -82,6 +83,10 @@ var ( DefaultGenesisState = types.DefaultGenesisState RegisterCodec = types.RegisterCodec + ValidatorUpdateDelay = types.ValidatorUpdateDelay + TokensToTendermintPower = types.TokensToTendermintPower + TokensFromTendermintPower = types.TokensFromTendermintPower + NewMsgCreateValidator = types.NewMsgCreateValidator NewMsgCreateValidatorOnBehalfOf = types.NewMsgCreateValidatorOnBehalfOf NewMsgEditValidator = types.NewMsgEditValidator diff --git a/x/staking/test_common.go b/x/staking/test_common.go index d13aab892..ad9d1ef79 100644 --- a/x/staking/test_common.go +++ b/x/staking/test_common.go @@ -26,28 +26,30 @@ var ( commissionMsg = NewCommissionMsg(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()) ) -func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt int64) MsgCreateValidator { +func NewTestMsgCreateValidator(address sdk.ValAddress, pubKey crypto.PubKey, amt sdk.Int) MsgCreateValidator { return types.NewMsgCreateValidator( - address, pubKey, sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(amt)), Description{}, commissionMsg, + address, pubKey, sdk.NewCoin(types.DefaultBondDenom, amt), Description{}, commissionMsg, ) } func NewTestMsgCreateValidatorWithCommission(address sdk.ValAddress, pubKey crypto.PubKey, - amt int64, commissionRate sdk.Dec) MsgCreateValidator { + amt sdk.Int, commissionRate sdk.Dec) MsgCreateValidator { commission := NewCommissionMsg(commissionRate, sdk.OneDec(), sdk.ZeroDec()) return types.NewMsgCreateValidator( - address, pubKey, sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(amt)), Description{}, commission, + address, pubKey, sdk.NewCoin(types.DefaultBondDenom, amt), Description{}, commission, ) } -func NewTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt int64) MsgDelegate { - amount := sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(amt)) +func NewTestMsgDelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amt sdk.Int) MsgDelegate { + amount := sdk.NewCoin(types.DefaultBondDenom, amt) return NewMsgDelegate(delAddr, valAddr, amount) } -func NewTestMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.ValAddress, valPubKey crypto.PubKey, amt int64) MsgCreateValidator { - amount := sdk.NewCoin(types.DefaultBondDenom, sdk.NewInt(amt)) +func NewTestMsgCreateValidatorOnBehalfOf(delAddr sdk.AccAddress, valAddr sdk.ValAddress, + valPubKey crypto.PubKey, amt sdk.Int) MsgCreateValidator { + + amount := sdk.NewCoin(types.DefaultBondDenom, amt) return NewMsgCreateValidatorOnBehalfOf(delAddr, valAddr, valPubKey, amount, Description{}, commissionMsg) } diff --git a/x/staking/types/commission.go b/x/staking/types/commission.go index 730bb6741..4baeea799 100644 --- a/x/staking/types/commission.go +++ b/x/staking/types/commission.go @@ -7,23 +7,21 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -type ( - // Commission defines a commission parameters for a given validator. - Commission struct { - Rate sdk.Dec `json:"rate"` // the commission rate charged to delegators - MaxRate sdk.Dec `json:"max_rate"` // maximum commission rate which this validator can ever charge - MaxChangeRate sdk.Dec `json:"max_change_rate"` // maximum daily increase of the validator commission - UpdateTime time.Time `json:"update_time"` // the last time the commission rate was changed - } +// Commission defines a commission parameters for a given validator. +type Commission struct { + Rate sdk.Dec `json:"rate"` // the commission rate charged to delegators + MaxRate sdk.Dec `json:"max_rate"` // maximum commission rate which this validator can ever charge + MaxChangeRate sdk.Dec `json:"max_change_rate"` // maximum daily increase of the validator commission + UpdateTime time.Time `json:"update_time"` // the last time the commission rate was changed +} - // CommissionMsg defines a commission message to be used for creating a - // validator. - CommissionMsg struct { - Rate sdk.Dec `json:"rate"` // the commission rate charged to delegators - MaxRate sdk.Dec `json:"max_rate"` // maximum commission rate which validator can ever charge - MaxChangeRate sdk.Dec `json:"max_change_rate"` // maximum daily increase of the validator commission - } -) +// CommissionMsg defines a commission message to be used for creating a +// validator. +type CommissionMsg struct { + Rate sdk.Dec `json:"rate"` // the commission rate charged to delegators + MaxRate sdk.Dec `json:"max_rate"` // maximum commission rate which validator can ever charge + MaxChangeRate sdk.Dec `json:"max_change_rate"` // maximum daily increase of the validator commission +} // NewCommissionMsg returns an initialized validator commission message. func NewCommissionMsg(rate, maxRate, maxChangeRate sdk.Dec) CommissionMsg { diff --git a/x/staking/types/genesis.go b/x/staking/types/genesis.go index 12c732781..fdc9b9677 100644 --- a/x/staking/types/genesis.go +++ b/x/staking/types/genesis.go @@ -20,7 +20,7 @@ type GenesisState struct { // Last validator power, needed for validator set update logic type LastValidatorPower struct { Address sdk.ValAddress - Power sdk.Int + Power int64 } func NewGenesisState(pool Pool, params Params, validators []Validator, bonds []Delegation) GenesisState { diff --git a/x/staking/types/validator.go b/x/staking/types/validator.go index ea8dafe2b..03f253d2c 100644 --- a/x/staking/types/validator.go +++ b/x/staking/types/validator.go @@ -3,6 +3,7 @@ package types import ( "bytes" "fmt" + "math/big" "strings" "time" @@ -264,7 +265,7 @@ func (d Description) EnsureLength() (Description, sdk.Error) { func (v Validator) ABCIValidatorUpdate() abci.ValidatorUpdate { return abci.ValidatorUpdate{ PubKey: tmtypes.TM2PB.PubKey(v.ConsPubKey), - Power: v.BondedTokens().Int64(), + Power: v.TendermintPower(), } } @@ -400,7 +401,7 @@ func (v Validator) DelegatorShareExRate() sdk.Dec { return sdk.NewDecFromInt(v.Tokens).Quo(v.DelegatorShares) } -// Get the bonded tokens which the validator holds +// get the bonded tokens which the validator holds func (v Validator) BondedTokens() sdk.Int { if v.Status == sdk.Bonded { return v.Tokens @@ -408,6 +409,34 @@ func (v Validator) BondedTokens() sdk.Int { return sdk.ZeroInt() } +// get the Tendermint Power +// a reduction of 10^9 from validator tokens is applied +func (v Validator) TendermintPower() int64 { + if v.Status == sdk.Bonded { + return v.PotentialTendermintPower() + } + return 0 +} + +var powerReduction = sdk.NewIntFromBigInt(new(big.Int).Exp(big.NewInt(10), big.NewInt(6), nil)) + +// potential Tendermint power +func (v Validator) PotentialTendermintPower() int64 { + return (v.Tokens.Div(powerReduction)).Int64() +} + +// utility functions + +// TokensToTendermintPower - convert input tokens to potential tendermint power +func TokensToTendermintPower(tokens sdk.Int) int64 { + return (tokens.Div(powerReduction)).Int64() +} + +// TokensFromTendermintPower - convert input power to tokens +func TokensFromTendermintPower(power int64) sdk.Int { + return sdk.NewInt(power).Mul(powerReduction) +} + //______________________________________________________________________ // ensure fulfills the sdk validator types @@ -420,8 +449,9 @@ func (v Validator) GetStatus() sdk.BondStatus { return v.Status } func (v Validator) GetOperator() sdk.ValAddress { return v.OperatorAddr } func (v Validator) GetConsPubKey() crypto.PubKey { return v.ConsPubKey } func (v Validator) GetConsAddr() sdk.ConsAddress { return sdk.ConsAddress(v.ConsPubKey.Address()) } -func (v Validator) GetPower() sdk.Int { return v.BondedTokens() } func (v Validator) GetTokens() sdk.Int { return v.Tokens } +func (v Validator) GetBondedTokens() sdk.Int { return v.BondedTokens() } +func (v Validator) GetTendermintPower() int64 { return v.TendermintPower() } func (v Validator) GetCommission() sdk.Dec { return v.Commission.Rate } func (v Validator) GetDelegatorShares() sdk.Dec { return v.DelegatorShares } func (v Validator) GetBondHeight() int64 { return v.BondHeight } From 37b8e37567397fdbccb6ad43a0ac120886671901 Mon Sep 17 00:00:00 2001 From: gamarin2 Date: Wed, 6 Feb 2019 17:50:03 +0100 Subject: [PATCH 63/99] Merge PR #3444: docs, delegator CLI guide --- docs/gaia/delegator-guide-cli.md | 417 +++++++++++++++++++++++++++++++ test2.json | 0 2 files changed, 417 insertions(+) create mode 100644 docs/gaia/delegator-guide-cli.md create mode 100644 test2.json diff --git a/docs/gaia/delegator-guide-cli.md b/docs/gaia/delegator-guide-cli.md new file mode 100644 index 000000000..bcba25f35 --- /dev/null +++ b/docs/gaia/delegator-guide-cli.md @@ -0,0 +1,417 @@ +# Delegator Guide (CLI) + +This document contains all the necessary information for delegators to interact with the Cosmos Hub through the Command-Line Interface (CLI). + +It also contains instructions on how to manage accounts, restore accounts from the fundraiser and use a ledger nano device. + +## Table of contents + +- [Installing `gaiacli`](#installing-gaiacli) +- [Cosmos Accounts](#cosmos-accounts) + + [Restoring an account from the fundrasier](#restoring-an-account-from-the-fundraiser) + + [Creating an account](#creating-an-account) +- [Accessing the Cosmos Hub network](#accessing-the-cosmos-hub-network) + + [Running your own full-node](#running-your-own-full-node) + + [Connecting to a remote full-node](#connecting-to-a-remote-full-node) +- [Setting up `gaiacli`](#setting-up-gaiacli) +- [Querying the state](querying-the-state) +- [Bonding Atoms and Withdrawing rewards](bonding-atoms-and-withdrawing-rewards) +- [Participating in Governance](participating-in-governance) +- [Signing transactions from an offline computer](#signing-transactions-from-an-offline-computer) + +## Installing `gaiacli` + +`gaiacli`: This is the command-line interface to interact with a `gaiad` full-node. + +::: warning +**Please check that you download the latest stable release of `gaiacli` that is available** +::: + +[**Download the binaries**] + +[**Install from source**](https://cosmos.network/docs/gaia/installation.html) + +## Cosmos Accounts + +At the core of every Cosmos account, there is a seed, which takes the form of a 12 or 24-words mnemonic. From this mnemonic, it is possible to create any number of Cosmos accounts, i.e. pairs of private key/public key. This is called an HD wallet (see [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) for more information on the HD wallet specification). + +``` + Account 0 Account 1 Account 2 + ++------------------+ +------------------+ +------------------+ +| | | | | | +| Address 0 | | Address 1 | | Address 2 | +| ^ | | ^ | | ^ | +| | | | | | | | | +| | | | | | | | | +| | | | | | | | | +| + | | + | | + | +| Public key 0 | | Public key 1 | | Public key 2 | +| ^ | | ^ | | ^ | +| | | | | | | | | +| | | | | | | | | +| | | | | | | | | +| + | | + | | + | +| Private key 0 | | Private key 1 | | Private key 2 | +| ^ | | ^ | | ^ | ++------------------+ +------------------+ +------------------+ + | | | + | | | + | | | + +--------------------------------------------------------------------+ + | + | + +---------+---------+ + | | + | Mnemonic (Seed) | + | | + +-------------------+ +``` + +The funds stored in an account are controlled by the private key. This private key is generated using a one-way function from the mnemonic. If you lose the private key, you can retrieve it using the mnemonic. However, if you lose the mnemonic, you will lose access to all the derived private keys. Likewise, if someone gains access to your mnemonic, they gain access to all the associated accounts. + +::: danger +**Do not lose or share your 12 words with anyone. To prevent theft or loss of funds, it is best to ensure that you keep multiple copies of your mnemonic, and store it in a safe, secure place and that only you know how to access. If someone is able to gain access to your mnemonic, they will be able to gain access to your private keys and control the accounts associated with them.** +::: + +The address is a public string with a human-readable prefix (e.g. `cosmos10snjt8dmpr5my0h76xj48ty80uzwhraqalu4eg`) that identifies your account. When someone wants to send you funds, they send it to your address. It is computationally infeasible to find the private key associated with a given address. + +### Restoring an account from the fundraiser + +::: tip +*NOTE: This section only concerns fundraiser participants* +::: + +If you participated in the fundraiser, you should be in possession of a 12-words mnemonic. Newly generated mnemonics use 24 words, but 12-word mnemonics are also compatible with all the Cosmos tools. + +#### On a ledger device + +At the core of a ledger device, there is a mnemonic used to generate accounts on multiple blockchains (including the Cosmos Hub). Usually, you will create a new mnemonic when you initialize your ledger device. However, it is possible to tell the ledger device to use a mnemonic provided by the user instead. Let us go ahead and see how you can input the mnemonic you obtained during the fundraiser as the seed of your ledger device. + +::: warning +*NOTE: To do this, **it is preferable to use a brand new ledger device.**. Indeed, there can be only one mnemonic per ledger device. If, however, you want to use a ledger that is already initialized with a seed, you can reset it by going in `Settings`>`Device`>`Reset All`. **Please note that this will wipe out the seed currently stored on the device. If you have not properly secured the associated mnemonic, you could lose your funds!!!*** +::: + +The following steps need to be performed on an un-initialized ledger device: + +1. Connect your ledger device to the computer via USB +2. Press both buttons +3. Do **NOT** choose the "Config as a new device" option. Instead, choose "Restore Configuration" +4. Choose a PIN +5. Choose the 12 words option +6. Input each of the words you got during the fundraiser, in the correct order. + +Your ledger is now correctly set up with your fundraiser mnemonic! Do not lose this mnemonic! If your ledger is compromised, you can always restore a new device again using the same mnemonic. + +Next, click [here](#using-a-ledger-device) to learn how to generate an account. + +#### On a computer + +::: warning +**NOTE: It is more secure to perform this action on an offline computer** +::: + +To restore an account using a fundraiser mnemonic and store the associated encrypted private key on a computer, use the following command: + +```bash +gaiacli keys add --recover +``` + +You will be prompted to input a passphrase that is used to encrypt the private key of account `0` on disk. Each time you want to send a transaction, this password will be required. If you lose the password, you can always recover the private key with the mnemonic. + +- `` is the name of the account. It is a reference to the account number used to derive the key pair from the mnemonic. You will use this name to identify your account when you want to send a transaction. +- You can add the optional `--account` flag to specify the path (`0`, `1`, `2`, ...) you want to use to generate your account. By default, account `0` is generated. + +### Creating an account + +To create an account, you just need to have `gaiacli` installed. Before creating it, you need to know where you intend to store and interract with your private keys. The best options are to store them in an offline dedicated computer or a ledger device. Storing them on your regular online computer involves more risk, since anyone who infiltrates your computer through the internet could exfiltrate your private keys and steal your funds. + +#### Using a ledger device + +::: warning +**Only use Ledger devices that you bought factory new or trust fully** +::: + +When you initialize your ledger, a 24-word mnemonic is generated and stored in the device. This mnemonic is compatible with Cosmos and Cosmos accounts can be derived from it. Therefore, all you have to do is make your ledger compatible with `gaiacli`. To do so, you need to go through the following steps: + +1. Download the Ledger Live app [here](https://www.ledger.com/pages/ledger-live) +2. Connect your ledger via USB and update to the latest firmware +3. Go to the ledger live app store, and download the "Cosmos" application (this can take a while) +4. Navigate to the Cosmos app on your ledger device + +Then, to create an account, use the following command: + +```bash +gaiacli keys add --ledger +``` + +- `` is the name of the account. It is a reference to the account number used to derive the key pair from the mnemonic. You will use this name to identify your account when you want to send a transaction. +- You can add the optional `--account` flag to specify the path (`0`, `1`, `2`, ...) you want to use to generate your account. By default, account `0` is generated. + +#### Using a computer + +::: warning +**NOTE: It is more secure to perform this action on an offline computer** +::: + +To generate an account, just use the following command: + +```bash +gaiacli keys add +``` + +The command will generate a 24-words mnemonic and save the private and public keys for account `0` at the same time. You will be prompted to input a passphrase that is used to encrypt the private key of account `0` on disk. Each time you want to send a transaction, this password will be required. If you lose the password, you can always recover the private key with the mnemonic. + +::: danger +**Do not lose or share your 12 words with anyone. To prevent theft or loss of funds, it is best to ensure that you keep multiple copies of your mnemonic, and store it in a safe, secure place and that only you know how to access. If someone is able to gain access to your mnemonic, they will be able to gain access to your private keys and control the accounts associated with them.** +::: + +::: warning +After you have secured your mnemonic (triple check!), you can delete bash history to ensure no one can retrieve it: + +```bash +history -c +rm ~/.bash_history +``` +::: + +- `` is the name of the account. It is a reference to the account number used to derive the key pair from the mnemonic. You will use this name to identify your account when you want to send a transaction. +- You can add the optional `--account` flag to specify the path (`0`, `1`, `2`, ...) you want to use to generate your account. By default, account `0` is generated. + + +You can generate more accounts from the same mnemonic using the following command: + +```bash +gaiacli keys add --recover --account 1 +``` + +This command will prompt you to input a passphrase as well as your mnemonic. Change the account number to generate a different account. + + +## Accessing the Cosmos Hub network + +In order to query the state and send transactions, you need a way to access the network. To do so, you can either run your own full-node, or connect to someone else's. + +::: danger +**NOTE: Do not share your mnemonic (12 or 24 words) with anyone. The only person who should ever need to know it is you. This is especially important if you are ever approached via email or direct message by someone requesting that you share your mnemonic for any kind of blockchain services or support. No one from Cosmos, the Tendermint team or the Interchain Foundation will ever send an email that asks for you to share any kind of account credentials or your mnemonic."**. +::: + +### Running your own full-node + +This is the most secure option, but comes with relatively high resource requirements. In order to run your own full-node, you need good bandwidth and at least 1TB of disk space. + +You will find the tutorial on how to install `gaiad` [here](https://cosmos.network/docs/gaia/installation.html), and the guide to run a full-node [here](https://cosmos.network/docs/gaia/join-testnet.html). + +### Connecting to a remote full-node + +If you do not want or cannot run your own node, you can connect to someone else's full-node. You should pick an operator you trust, because a malicious operator could return incorrect query results or censor your transactions. However, they will never be able to steal your funds, as your private keys are stored locally on your computer or ledger device. Possible options of full-node operators include validators, wallet providers or exchanges. + +In order to connect to the full-node, you will need an address of the following form: `https://77.87.106.33:26657` (*Note: This is a placeholder*). This address has to be communicated by the full-node operator you choose to trust. You will use this address in the [following section](#setting-up-gaiacli). + +## Setting up `gaiacli` + +::: warning +**Please check that you are always using the latest stable release of `gaiacli`** +::: + +`gaiacli` is the tool that enables you to interact with the node that runs on the Cosmos Hub network, whether you run it yourself or not (see [accessing the cosmos hub network](#accession-the-cosmos-hub-network)). Let us set it up properly. + +In order to set up `gaiacli`, use the following command: + +```bash +gaiacli config +``` + +It allows you to set a default value for each given flag. + +First, set up the address of the full-node you want to connect to: + +```bash +gaiacli config node : + +// query all delegations made from a delegator given their address (e.g. cosmos10snjt8dmpr5my0h76xj48ty80uzwhraqalu4eg) +gaiacli query delegations + +// query a specific delegation made from a delegator to a validator given their addresses +gaiacli query delegations + +// query the rewards of a delegator given a delegator address (e.g. cosmos10snjt8dmpr5my0h76xj48ty80uzwhraqalu4eg) +gaiacli query distr rewards + +// query all proposals currently open for depositing +gaiacli query proposals --status deposit_period + +// query all proposals currently open for voting +gaiacli query proposals --status voting_period + +// query a proposal given its proposalID +gaiacli query proposal +``` + +For more commands, just type: + +```bash +gaiacli query +``` + +For each command, you can use the `-h` or `--help` flag to get more information. + +## Bonding Atoms and Withdrawing rewards + +::: warning +**Before bonding Atoms, please read the [delegator faq](https://cosmos.network/resources/delegators) to understand the risk and responsabilities involved with delegating** +::: + +::: warning +**Note: These commands need to be run on an online computer. It is more secure to perform them commands using a ledger device. For the offline procedure, click [here](#signing-transactions-from-an-offline-computer).** +::: + +```bash +// Bond Atoms +// ex value for flags: =10000stake, =cosmosvaloper18thamkhnj9wz8pa4nhnp9rldprgant57pk2m8s, =0.001stake + +gaiacli tx staking --amount --validator --from --gas auto --gas-prices + +// Withdraw rewards + +gaiacli tx distr withdraw-rewards --from +``` + +::: tip +If you use a connected Ledger, you will be asked to confirm the transaction on the device before it is signed and broadcast to the network +::: + +To confirm that your transaction went through, you can use the following queries: + +```bash +// your balance should change after you bond Atoms or withdraw rewards +gaiacli query account + +// you should have delegations after you bond Atom +gaiacli query delegations + +// this returns your tx if it has been included +// use the tx hash that was displayed when you created the tx +gaiacli query tx + +``` + +Double check with a block explorer if you interact with the network through a trusted full-node. + +### A note on gas and fees + +Transactions on the Cosmos Hub network need to include a transaction fee in order to be processed. This fee pays for the gas required to run the transaction. The formula is the following: + +``` +fees = gas * gasPrices +``` + +The `gas` is dependent on the transaction. Different transaction require different amount of `gas`. The `gas` amount for a transaction is calculated as it is being processed, but there is a way to estimate it beforehand by using the `auto` value for the `gas` flag. Of course, this only gives an estimate. You can adjust this estimate with the flag `--gas-adjustment` (default `1.0`) if you want to be sure you provide enough `gas` for the transaction. + +The `gasPrice` is the price of each unit of `gas`. Each validator sets a `min-gas-price` value, and will only include transactions that have a `gasPrice` greater than their `min-gas-price`. + +The transaction `fees` are the product of `gas` and `gasPrice`. As a user, you have to input 2 out of 3. The higher the `gasPrice`, the higher the chance that your transaction will get included in a block. + +## Participating in governance + +### Primer on governance + +The Cosmos Hub has a built-in governance system that lets bonded Atom holders vote on proposals. There are three types of proposal: + +- `Text Proposals`: These are the most basic type of proposals. They can be used to get the opinion of the network on a given topic. +- `Parameter Proposals`: These are used to update the value of an existing parameter. +- `Software Upgrade Proposal`: These are used to propose an upgrade of the Hub's software. + +Any Atom holder can submit a proposal. In order for the proposal to be open for voting, it needs to come with a `deposit` that is greater than a parameter called `minDeposit`. The `deposit` need not be provided in its entirety by the submitter. If the initial proposer's `deposit` is not sufficient, the proposal enters the `deposit_period` status. Then, any Atom holder can increase the deposit by sending a `depositTx`. + +Once the `deposit` reaches `minDeposit`, the proposal enters the `voting_period`, which lasts 2 weeks. Any **bonded** Atom holder can then cast a vote on this proposal. The options are `Yes`, `No`, `NoWithVeto` and `Abstain`. The weight of the vote is based on the amount of bonded Atoms of the sender. If they don't vote, delegator inherit the vote of their validator. However, delegators can override their validator's vote by sending a vote themselves. + +At the end of the voting period, the proposal is accepted if there are more than 50% `Yes` votes (excluding `Abstain ` votes) and less than 33.33% of `NoWithVeto` votes (excluding `Abstain` votes). + +### In practice + +::: warning +**Note: These commands need to be run on an online computer. It is more secure to perform them commands using a ledger device. For the offline procedure, click [here](#signing-transactions-from-an-offline-computer).** +::: + +```bash +// Submit a Proposal +// =text/parameter_change/software_upgrade +// ex value for flag: =0.0001stake + +gaiacli tx gov submit-proposal --title "Test Proposal" --description "My awesome proposal" --type --deposit=10stake --gas auto --gas-prices --from + +// Increase deposit of a proposal +// Retrieve proposalID from $gaiacli query gov proposals --status deposit_period +// ex value for parameter: =1stake + +gaiacli tx gov deposit --gas auto --gas-prices --from + +// Vote on a proposal +// Retrieve proposalID from $gaiacli query gov proposals --status voting_period +//