From bb624b36aa15e5b2ef28499ef16fc2b11b6d3f00 Mon Sep 17 00:00:00 2001 From: ValarDragon Date: Sat, 22 Sep 2018 20:20:53 -0700 Subject: [PATCH 1/9] simulation: Use a simulation.Account struct This removes privkeys and addresses from function signatures. This comes with a 11% performance improvement to the simulator, as we no longer keep recomputing the pubkeys. Once we move the transient store clearing to endblock, we can further raise the size of make test_sim_gaia_fast concretely, `make test_sim_gaia_fast` went from 16.8 seconds to 13.5 seconds on my system. --- PENDING.md | 1 + cmd/gaia/app/sim_test.go | 9 +-- x/bank/simulation/msgs.go | 21 +++--- x/bank/simulation/sim_test.go | 6 +- x/gov/simulation/msgs.go | 41 +++++------ x/gov/simulation/sim_test.go | 7 +- x/mock/simulation/random_simulate_blocks.go | 38 +++++----- x/mock/simulation/types.go | 18 ++++- x/mock/simulation/util.go | 40 ++++++++-- x/slashing/simulation/msgs.go | 8 +- x/stake/simulation/msgs.go | 81 ++++++++++----------- x/stake/simulation/sim_test.go | 5 +- 12 files changed, 151 insertions(+), 124 deletions(-) diff --git a/PENDING.md b/PENDING.md index ab0a42d45..543010eb1 100644 --- a/PENDING.md +++ b/PENDING.md @@ -44,6 +44,7 @@ BREAKING CHANGES * [types] [\#2119](https://github.com/cosmos/cosmos-sdk/issues/2119) Parsed error messages and ABCI log errors to make them more human readable. * [simulation] Rename TestAndRunTx to Operation [#2153](https://github.com/cosmos/cosmos-sdk/pull/2153) * [simulation] Remove log and testing.TB from Operation and Invariants, in favor of using errors \#2282 + * [simulation] Remove usage of keys and addrs in the types, in favor of simulation.Account \#2384 * [tools] Removed gocyclo [#2211](https://github.com/cosmos/cosmos-sdk/issues/2211) * [baseapp] Remove `SetTxDecoder` in favor of requiring the decoder be set in baseapp initialization. [#1441](https://github.com/cosmos/cosmos-sdk/issues/1441) * [baseapp] [\#1921](https://github.com/cosmos/cosmos-sdk/issues/1921) Add minimumFees field to BaseApp. diff --git a/cmd/gaia/app/sim_test.go b/cmd/gaia/app/sim_test.go index 078eece11..6097b5a08 100644 --- a/cmd/gaia/app/sim_test.go +++ b/cmd/gaia/app/sim_test.go @@ -10,7 +10,6 @@ import ( "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" @@ -42,14 +41,14 @@ func init() { flag.BoolVar(&commit, "SimulationCommit", false, "Have the simulation commit") } -func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage { +func appStateFn(r *rand.Rand, accs []simulation.Account) json.RawMessage { var genesisAccounts []GenesisAccount // Randomly generate some genesis accounts for _, acc := range accs { coins := sdk.Coins{sdk.Coin{"steak", sdk.NewInt(100)}} genesisAccounts = append(genesisAccounts, GenesisAccount{ - Address: acc, + Address: acc.Address, Coins: coins, }) } @@ -61,10 +60,10 @@ func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json // XXX Try different numbers of initially bonded validators numInitiallyBonded := int64(50) for i := 0; i < int(numInitiallyBonded); i++ { - validator := stake.NewValidator(sdk.ValAddress(accs[i]), keys[i].PubKey(), stake.Description{}) + validator := stake.NewValidator(sdk.ValAddress(accs[i].Address), accs[i].PubKey, stake.Description{}) validator.Tokens = sdk.NewDec(100) validator.DelegatorShares = sdk.NewDec(100) - delegation := stake.Delegation{accs[i], sdk.ValAddress(accs[i]), sdk.NewDec(100), 0} + delegation := stake.Delegation{accs[i].Address, sdk.ValAddress(accs[i].Address), sdk.NewDec(100), 0} validators = append(validators, validator) delegations = append(delegations, delegation) } diff --git a/x/bank/simulation/msgs.go b/x/bank/simulation/msgs.go index cefa2a460..0b8ec3026 100644 --- a/x/bank/simulation/msgs.go +++ b/x/bank/simulation/msgs.go @@ -18,19 +18,18 @@ import ( // SimulateSingleInputMsgSend tests and runs a single msg send, with one input and one output, where both // accounts already exist. func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation { - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { - fromKey := simulation.RandomKey(r, keys) - fromAddr := sdk.AccAddress(fromKey.PubKey().Address()) - toKey := simulation.RandomKey(r, keys) + 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 := simulation.RandomAcc(r, accs) + toAcc := simulation.RandomAcc(r, accs) // Disallow sending money to yourself for { - if !fromKey.Equals(toKey) { + if !fromAcc.PubKey.Equals(toAcc.PubKey) { break } - toKey = simulation.RandomKey(r, keys) + toAcc = simulation.RandomAcc(r, accs) } - toAddr := sdk.AccAddress(toKey.PubKey().Address()) - initFromCoins := mapper.GetAccount(ctx, fromAddr).GetCoins() + toAddr := toAcc.Address + initFromCoins := mapper.GetAccount(ctx, fromAcc.Address).GetCoins() if len(initFromCoins) == 0 { return "skipping, no coins at all", nil, nil @@ -43,7 +42,7 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation } action = fmt.Sprintf("%s is sending %s %s to %s", - fromAddr.String(), + fromAcc.Address.String(), amt.String(), initFromCoins[denomIndex].Denom, toAddr.String(), @@ -51,10 +50,10 @@ func SimulateSingleInputMsgSend(mapper auth.AccountMapper) simulation.Operation coins := sdk.Coins{{initFromCoins[denomIndex].Denom, amt}} var msg = bank.MsgSend{ - Inputs: []bank.Input{bank.NewInput(fromAddr, coins)}, + Inputs: []bank.Input{bank.NewInput(fromAcc.Address, coins)}, Outputs: []bank.Output{bank.NewOutput(toAddr, coins)}, } - goErr = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromKey}) + goErr = sendAndVerifyMsgSend(app, mapper, msg, ctx, []crypto.PrivKey{fromAcc.PrivKey}) if goErr != nil { return "", nil, goErr } diff --git a/x/bank/simulation/sim_test.go b/x/bank/simulation/sim_test.go index 811535507..819289845 100644 --- a/x/bank/simulation/sim_test.go +++ b/x/bank/simulation/sim_test.go @@ -5,8 +5,6 @@ import ( "math/rand" "testing" - "github.com/tendermint/tendermint/crypto" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" "github.com/cosmos/cosmos-sdk/x/mock" @@ -26,8 +24,8 @@ func TestBankWithRandomMessages(t *testing.T) { panic(err) } - appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage { - mock.RandomSetGenesis(r, mapp, accs, []string{"stake"}) + appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage { + simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"}) return json.RawMessage("{}") } diff --git a/x/gov/simulation/msgs.go b/x/gov/simulation/msgs.go index f46fe995e..d5ff1881e 100644 --- a/x/gov/simulation/msgs.go +++ b/x/gov/simulation/msgs.go @@ -6,8 +6,6 @@ import ( "math/rand" "time" - "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/gov" @@ -45,9 +43,9 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe }) statePercentageArray := []float64{1, .9, .75, .4, .15, 0} curNumVotesState := 1 - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { // 1) submit proposal now - sender := simulation.RandomKey(r, keys) + sender := simulation.RandomAcc(r, accs) msg, err := simulationCreateMsgSubmitProposal(r, sender) if err != nil { return "", nil, err @@ -61,16 +59,16 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe // 2) Schedule operations for votes // 2.1) first pick a number of people to vote. curNumVotesState = numVotesTransitionMatrix.NextState(r, curNumVotesState) - numVotes := int(math.Ceil(float64(len(keys)) * statePercentageArray[curNumVotesState])) + numVotes := int(math.Ceil(float64(len(accs)) * statePercentageArray[curNumVotesState])) // 2.2) select who votes and when - whoVotes := r.Perm(len(keys)) + whoVotes := r.Perm(len(accs)) // didntVote := whoVotes[numVotes:] whoVotes = whoVotes[:numVotes] votingPeriod := k.GetVotingProcedure(ctx).VotingPeriod fops := make([]simulation.FutureOperation, numVotes+1) for i := 0; i < numVotes; i++ { whenVote := ctx.BlockHeader().Time.Add(time.Duration(r.Int63n(int64(votingPeriod.Seconds()))) * time.Second) - fops[i] = simulation.FutureOperation{BlockTime: whenVote, Op: operationSimulateMsgVote(k, sk, keys[whoVotes[i]], proposalID)} + fops[i] = simulation.FutureOperation{BlockTime: whenVote, Op: operationSimulateMsgVote(k, sk, accs[whoVotes[i]], proposalID)} } // 3) Make an operation to ensure slashes were done correctly. (Really should be a future invariant) // TODO: Find a way to check if a validator was slashed other than just checking their balance a block @@ -84,8 +82,8 @@ func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keepe // Note: Currently doesn't ensure that the proposal txt is in JSON form func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operation { handler := gov.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { - sender := simulation.RandomKey(r, keys) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOps []simulation.FutureOperation, err error) { + sender := simulation.RandomAcc(r, accs) msg, err := simulationCreateMsgSubmitProposal(r, sender) if err != nil { return "", nil, err @@ -111,14 +109,13 @@ func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, sk stake.Keeper, return } -func simulationCreateMsgSubmitProposal(r *rand.Rand, sender crypto.PrivKey) (msg gov.MsgSubmitProposal, err error) { - addr := sdk.AccAddress(sender.PubKey().Address()) +func simulationCreateMsgSubmitProposal(r *rand.Rand, sender simulation.Account) (msg gov.MsgSubmitProposal, err error) { deposit := randomDeposit(r) msg = gov.NewMsgSubmitProposal( simulation.RandStringOfLength(r, 5), simulation.RandStringOfLength(r, 5), gov.ProposalTypeText, - addr, + sender.Address, deposit, ) if msg.ValidateBasic() != nil { @@ -129,15 +126,14 @@ func simulationCreateMsgSubmitProposal(r *rand.Rand, sender crypto.PrivKey) (msg // SimulateMsgDeposit func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation { - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { - key := simulation.RandomKey(r, keys) - addr := sdk.AccAddress(key.PubKey().Address()) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + acc := simulation.RandomAcc(r, accs) proposalID, ok := randomProposalID(r, k, ctx) if !ok { return "no-operation", nil, nil } deposit := randomDeposit(r) - msg := gov.NewMsgDeposit(addr, proposalID, deposit) + msg := gov.NewMsgDeposit(acc.Address, proposalID, deposit) if msg.ValidateBasic() != nil { return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) } @@ -159,14 +155,14 @@ func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation { // SimulateMsgVote // nolint: unparam func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation { - return operationSimulateMsgVote(k, sk, nil, -1) + return operationSimulateMsgVote(k, sk, simulation.Account{}, -1) } // nolint: unparam -func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey, proposalID int64) simulation.Operation { - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { - if key == nil { - key = simulation.RandomKey(r, keys) +func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, acc simulation.Account, proposalID int64) simulation.Operation { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + if acc.Equals(simulation.Account{}) { + acc = simulation.RandomAcc(r, accs) } var ok bool @@ -177,10 +173,9 @@ func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey, return "no-operation", nil, nil } } - addr := sdk.AccAddress(key.PubKey().Address()) option := randomVotingOption(r) - msg := gov.NewMsgVote(addr, proposalID, option) + msg := gov.NewMsgVote(acc.Address, proposalID, option) if msg.ValidateBasic() != nil { return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) } diff --git a/x/gov/simulation/sim_test.go b/x/gov/simulation/sim_test.go index 35a11fb07..b0317d116 100644 --- a/x/gov/simulation/sim_test.go +++ b/x/gov/simulation/sim_test.go @@ -6,7 +6,6 @@ import ( "testing" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" @@ -43,12 +42,12 @@ func TestGovWithRandomMessages(t *testing.T) { panic(err) } - appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage { - mock.RandomSetGenesis(r, mapp, accs, []string{"stake"}) + appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage { + simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"}) return json.RawMessage("{}") } - setup := func(r *rand.Rand, privKeys []crypto.PrivKey) { + setup := func(r *rand.Rand, accs []simulation.Account) { ctx := mapp.NewContext(false, abci.Header{}) stake.InitGenesis(ctx, stakeKeeper, stake.DefaultGenesisState()) gov.InitGenesis(ctx, govKeeper, gov.DefaultGenesisState()) diff --git a/x/mock/simulation/random_simulate_blocks.go b/x/mock/simulation/random_simulate_blocks.go index 96dbff572..25748e15b 100644 --- a/x/mock/simulation/random_simulate_blocks.go +++ b/x/mock/simulation/random_simulate_blocks.go @@ -15,17 +15,15 @@ import ( "time" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/mock" ) // Simulate tests application by sending random messages. func Simulate(t *testing.T, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, + appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, ops []WeightedOperation, setups []RandSetup, invariants []Invariant, numBlocks int, blockSize int, commit bool) error { @@ -33,16 +31,16 @@ func Simulate(t *testing.T, app *baseapp.BaseApp, return SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit) } -func initChain(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress, setups []RandSetup, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage) (validators map[string]mockValidator) { - res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, keys, accs)}) +func initChain(r *rand.Rand, accounts []Account, setups []RandSetup, app *baseapp.BaseApp, + appStateFn func(r *rand.Rand, accounts []Account) json.RawMessage) (validators map[string]mockValidator) { + res := app.InitChain(abci.RequestInitChain{AppStateBytes: appStateFn(r, accounts)}) validators = make(map[string]mockValidator) for _, validator := range res.Validators { validators[string(validator.Address)] = mockValidator{validator, GetMemberOfInitialState(r, initialLivenessWeightings)} } for i := 0; i < len(setups); i++ { - setups[i](r, keys) + setups[i](r, accounts) } return @@ -56,7 +54,7 @@ func randTimestamp(r *rand.Rand) time.Time { // SimulateFromSeed tests an application by running the provided // operations, testing the provided invariants, but using the provided seed. func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, - appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage, + appStateFn func(r *rand.Rand, accs []Account) json.RawMessage, seed int64, ops []WeightedOperation, setups []RandSetup, invariants []Invariant, numBlocks int, blockSize int, commit bool) (simError error) { @@ -69,7 +67,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, fmt.Printf("Starting the simulation from time %v, unixtime %v\n", timestamp.UTC().Format(time.UnixDate), timestamp.Unix()) timeDiff := maxTimePerBlock - minTimePerBlock - keys, accs := mock.GeneratePrivKeyAddressPairsFromRand(r, numKeys) + accs := RandomAccounts(r, numKeys) // Setup event stats events := make(map[string]uint) @@ -77,7 +75,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, events[what]++ } - validators := initChain(r, keys, accs, setups, app, appStateFn) + validators := initChain(r, accs, setups, app, appStateFn) header := abci.Header{Height: 0, Time: timestamp} opCount := 0 @@ -139,10 +137,10 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, thisBlockSize := getBlockSize(r, blockSize) // Run queued operations. Ignores blocksize if blocksize is too small - numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, keys, logWriter, displayLogs, event) - numQueuedTimeOpsRan := runQueuedTimeOperations(timeOperationQueue, header.Time, tb, r, app, ctx, keys, logWriter, displayLogs, event) + numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, accs, logWriter, displayLogs, event) + numQueuedTimeOpsRan := runQueuedTimeOperations(timeOperationQueue, header.Time, tb, r, app, ctx, accs, logWriter, displayLogs, event) thisBlockSize = thisBlockSize - numQueuedOpsRan - numQueuedTimeOpsRan - operations := blockSimulator(thisBlockSize, r, app, ctx, keys, header, logWriter) + operations := blockSimulator(thisBlockSize, r, app, ctx, accs, header, logWriter) opCount += operations + numQueuedOpsRan + numQueuedTimeOpsRan res := app.EndBlock(abci.RequestEndBlock{}) @@ -176,7 +174,7 @@ func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp, // Returns a function to simulate blocks. Written like this to avoid constant parameters being passed everytime, to minimize // memory overhead func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, timeOperationQueue []FutureOperation, totalNumBlocks int, displayLogs func()) func( - blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, privKeys []crypto.PrivKey, header abci.Header, logWriter func(string)) (opCount int) { + blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { totalOpWeight := 0 for i := 0; i < len(ops); i++ { totalOpWeight += ops[i].Weight @@ -193,9 +191,9 @@ func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event f return ops[0].Op } return func(blocksize int, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - keys []crypto.PrivKey, header abci.Header, logWriter func(string)) (opCount int) { + accounts []Account, header abci.Header, logWriter func(string)) (opCount int) { for j := 0; j < blocksize; j++ { - logUpdate, futureOps, err := selectOp(r)(r, app, ctx, keys, event) + logUpdate, futureOps, err := selectOp(r)(r, app, ctx, accounts, event) if err != nil { displayLogs() tb.Fatalf("error on operation %d within block %d, %v", header.Height, opCount, err) @@ -264,14 +262,14 @@ func queueOperations(queuedOperations map[int][]Operation, queuedTimeOperations // nolint: errcheck func runQueuedOperations(queueOperations map[int][]Operation, height int, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - privKeys []crypto.PrivKey, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { + accounts []Account, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { if queuedOps, ok := queueOperations[height]; ok { numOps := len(queuedOps) for i := 0; i < numOps; i++ { // For now, queued operations cannot queue more operations. // If a need arises for us to support queued messages to queue more messages, this can // be changed. - logUpdate, _, err := queuedOps[i](r, app, ctx, privKeys, event) + logUpdate, _, err := queuedOps[i](r, app, ctx, accounts, event) logWriter(logUpdate) if err != nil { displayLogs() @@ -285,14 +283,14 @@ func runQueuedOperations(queueOperations map[int][]Operation, height int, tb tes } func runQueuedTimeOperations(queueOperations []FutureOperation, currentTime time.Time, tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - privKeys []crypto.PrivKey, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { + accounts []Account, logWriter func(string), displayLogs func(), event func(string)) (numOpsRan int) { numOpsRan = 0 for len(queueOperations) > 0 && currentTime.After(queueOperations[0].BlockTime) { // For now, queued operations cannot queue more operations. // If a need arises for us to support queued messages to queue more messages, this can // be changed. - logUpdate, _, err := queueOperations[0].Op(r, app, ctx, privKeys, event) + logUpdate, _, err := queueOperations[0].Op(r, app, ctx, accounts, event) logWriter(logUpdate) if err != nil { displayLogs() diff --git a/x/mock/simulation/types.go b/x/mock/simulation/types.go index a32edea6d..2e68a428e 100644 --- a/x/mock/simulation/types.go +++ b/x/mock/simulation/types.go @@ -23,11 +23,11 @@ type ( // Operations can optionally provide a list of "FutureOperations" to run later // These will be ran at the beginning of the corresponding block. Operation func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, - privKeys []crypto.PrivKey, event func(string), + accounts []Account, event func(string), ) (action string, futureOperations []FutureOperation, err error) // RandSetup performs the random setup the mock module needs. - RandSetup func(r *rand.Rand, privKeys []crypto.PrivKey) + RandSetup func(r *rand.Rand, accounts []Account) // An Invariant is a function which tests a particular invariant. // If the invariant has been broken, it should return an error @@ -35,6 +35,15 @@ type ( // The simulator will then halt and print the logs. Invariant func(app *baseapp.BaseApp) error + // Account contains a privkey, pubkey, address tuple + // eventually more useful data can be placed in here. + // (e.g. number of coins) + Account struct { + PrivKey crypto.PrivKey + PubKey crypto.PubKey + Address sdk.AccAddress + } + mockValidator struct { val abci.Validator livenessState int @@ -70,3 +79,8 @@ func PeriodicInvariant(invariant Invariant, period int, offset int) Invariant { return nil } } + +// nolint +func (acc Account) Equals(acc2 Account) bool { + return acc.Address.Equals(acc2.Address) +} diff --git a/x/mock/simulation/util.go b/x/mock/simulation/util.go index a46d6dd54..54bfabc73 100644 --- a/x/mock/simulation/util.go +++ b/x/mock/simulation/util.go @@ -9,10 +9,12 @@ import ( "testing" "time" - "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mock" ) // shamelessly copied from https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-golang#31832326 @@ -56,10 +58,10 @@ func DisplayEvents(events map[string]uint) { } } -// Pick a random key from an array -func RandomKey(r *rand.Rand, keys []crypto.PrivKey) crypto.PrivKey { - return keys[r.Intn( - len(keys), +// RandomAcc pick a random account from an array +func RandomAcc(r *rand.Rand, accs []Account) Account { + return accs[r.Intn( + len(accs), )] } @@ -68,6 +70,25 @@ func RandomAmount(r *rand.Rand, max sdk.Int) sdk.Int { return sdk.NewInt(int64(r.Intn(int(max.Int64())))) } +// RandomAccounts generates n random accounts +func RandomAccounts(r *rand.Rand, n int) []Account { + accs := make([]Account, n) + for i := 0; i < n; i++ { + // don't need that much entropy for simulation + privkeySeed := make([]byte, 15) + r.Read(privkeySeed) + useSecp := r.Int63()%2 == 0 + if useSecp { + accs[i].PrivKey = secp256k1.GenPrivKeySecp256k1(privkeySeed) + } else { + accs[i].PrivKey = ed25519.GenPrivKeyFromSecret(privkeySeed) + } + accs[i].PubKey = accs[i].PrivKey.PubKey() + accs[i].Address = sdk.AccAddress(accs[i].PubKey.Address()) + } + return accs +} + // Builds a function to add logs for this particular block func addLogMessage(testingmode bool, blockLogBuilders []*strings.Builder, height int) func(string) { if testingmode { @@ -92,6 +113,15 @@ func assertAllInvariants(t *testing.T, app *baseapp.BaseApp, invariants []Invari } } +// RandomSetGenesis wraps mock.RandomSetGenesis, but using simulation accounts +func RandomSetGenesis(r *rand.Rand, app *mock.App, accs []Account, denoms []string) { + addrs := make([]sdk.AccAddress, len(accs)) + for i := 0; i < len(accs); i++ { + addrs[i] = accs[i].Address + } + mock.RandomSetGenesis(r, app, addrs, denoms) +} + // Creates a function to print out the logs func logPrinter(testingmode bool, logs []*strings.Builder) func() { if testingmode { diff --git a/x/slashing/simulation/msgs.go b/x/slashing/simulation/msgs.go index da9340baf..2b09226f2 100644 --- a/x/slashing/simulation/msgs.go +++ b/x/slashing/simulation/msgs.go @@ -4,8 +4,6 @@ import ( "fmt" "math/rand" - "github.com/tendermint/tendermint/crypto" - "github.com/cosmos/cosmos-sdk/baseapp" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/mock/simulation" @@ -14,9 +12,9 @@ import ( // SimulateMsgUnjail func SimulateMsgUnjail(k slashing.Keeper) simulation.Operation { - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { - key := simulation.RandomKey(r, keys) - address := sdk.ValAddress(key.PubKey().Address()) + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + acc := simulation.RandomAcc(r, accs) + address := sdk.ValAddress(acc.Address) msg := slashing.NewMsgUnjail(address) if msg.ValidateBasic() != nil { return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes()) diff --git a/x/stake/simulation/msgs.go b/x/stake/simulation/msgs.go index 5b2bc1ee8..906716ed1 100644 --- a/x/stake/simulation/msgs.go +++ b/x/stake/simulation/msgs.go @@ -11,22 +11,20 @@ import ( "github.com/cosmos/cosmos-sdk/x/mock/simulation" "github.com/cosmos/cosmos-sdk/x/stake" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" ) // SimulateMsgCreateValidator func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { denom := k.GetParams(ctx).BondDenom description := stake.Description{ Moniker: simulation.RandStringOfLength(r, 10), } - key := simulation.RandomKey(r, keys) - pubkey := key.PubKey() - address := sdk.ValAddress(pubkey.Address()) - amount := m.GetAccount(ctx, sdk.AccAddress(address)).GetCoins().AmountOf(denom) + acc := simulation.RandomAcc(r, accs) + address := sdk.ValAddress(acc.Address) + amount := m.GetAccount(ctx, acc.Address).GetCoins().AmountOf(denom) if amount.GT(sdk.ZeroInt()) { amount = simulation.RandomAmount(r, amount) } @@ -36,8 +34,8 @@ func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation msg := stake.MsgCreateValidator{ Description: description, ValidatorAddr: address, - DelegatorAddr: sdk.AccAddress(address), - PubKey: pubkey, + DelegatorAddr: acc.Address, + PubKey: acc.PubKey, Delegation: sdk.NewCoin(denom, amount), } if msg.ValidateBasic() != nil { @@ -58,7 +56,7 @@ func SimulateMsgCreateValidator(m auth.AccountMapper, k stake.Keeper) simulation // SimulateMsgEditValidator func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { description := stake.Description{ Moniker: simulation.RandStringOfLength(r, 10), @@ -66,9 +64,8 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation { Website: simulation.RandStringOfLength(r, 10), Details: simulation.RandStringOfLength(r, 10), } - key := simulation.RandomKey(r, keys) - pubkey := key.PubKey() - address := sdk.ValAddress(pubkey.Address()) + acc := simulation.RandomAcc(r, accs) + address := sdk.ValAddress(acc.Address) msg := stake.MsgEditValidator{ Description: description, ValidatorAddr: address, @@ -90,13 +87,13 @@ func SimulateMsgEditValidator(k stake.Keeper) simulation.Operation { // SimulateMsgDelegate func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { denom := k.GetParams(ctx).BondDenom - validatorKey := simulation.RandomKey(r, keys) - validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address()) - delegatorKey := simulation.RandomKey(r, keys) - delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address()) + validatorAcc := simulation.RandomAcc(r, accs) + validatorAddress := sdk.ValAddress(validatorAcc.Address) + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom) if amount.GT(sdk.ZeroInt()) { amount = simulation.RandomAmount(r, amount) @@ -126,13 +123,13 @@ func SimulateMsgDelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operat // SimulateMsgBeginUnbonding func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { denom := k.GetParams(ctx).BondDenom - validatorKey := simulation.RandomKey(r, keys) - validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address()) - delegatorKey := simulation.RandomKey(r, keys) - delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address()) + validatorAcc := simulation.RandomAcc(r, accs) + validatorAddress := sdk.ValAddress(validatorAcc.Address) + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom) if amount.GT(sdk.ZeroInt()) { amount = simulation.RandomAmount(r, amount) @@ -162,12 +159,12 @@ func SimulateMsgBeginUnbonding(m auth.AccountMapper, k stake.Keeper) simulation. // SimulateMsgCompleteUnbonding func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { - validatorKey := simulation.RandomKey(r, keys) - validatorAddress := sdk.ValAddress(validatorKey.PubKey().Address()) - delegatorKey := simulation.RandomKey(r, keys) - delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address()) + validatorAcc := simulation.RandomAcc(r, accs) + validatorAddress := sdk.ValAddress(validatorAcc.Address) + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address msg := stake.MsgCompleteUnbonding{ DelegatorAddr: delegatorAddress, ValidatorAddr: validatorAddress, @@ -189,15 +186,15 @@ func SimulateMsgCompleteUnbonding(k stake.Keeper) simulation.Operation { // SimulateMsgBeginRedelegate func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { denom := k.GetParams(ctx).BondDenom - sourceValidatorKey := simulation.RandomKey(r, keys) - sourceValidatorAddress := sdk.ValAddress(sourceValidatorKey.PubKey().Address()) - destValidatorKey := simulation.RandomKey(r, keys) - destValidatorAddress := sdk.ValAddress(destValidatorKey.PubKey().Address()) - delegatorKey := simulation.RandomKey(r, keys) - delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address()) + sourceValidatorAcc := simulation.RandomAcc(r, accs) + sourceValidatorAddress := sdk.ValAddress(sourceValidatorAcc.Address) + destValidatorAcc := simulation.RandomAcc(r, accs) + destValidatorAddress := sdk.ValAddress(destValidatorAcc.Address) + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address // TODO amount := m.GetAccount(ctx, delegatorAddress).GetCoins().AmountOf(denom) if amount.GT(sdk.ZeroInt()) { @@ -229,14 +226,14 @@ func SimulateMsgBeginRedelegate(m auth.AccountMapper, k stake.Keeper) simulation // SimulateMsgCompleteRedelegate func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.Operation { handler := stake.NewHandler(k) - return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { + return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, accs []simulation.Account, event func(string)) (action string, fOp []simulation.FutureOperation, err error) { - validatorSrcKey := simulation.RandomKey(r, keys) - validatorSrcAddress := sdk.ValAddress(validatorSrcKey.PubKey().Address()) - validatorDstKey := simulation.RandomKey(r, keys) - validatorDstAddress := sdk.ValAddress(validatorDstKey.PubKey().Address()) - delegatorKey := simulation.RandomKey(r, keys) - delegatorAddress := sdk.AccAddress(delegatorKey.PubKey().Address()) + validatorSrcAcc := simulation.RandomAcc(r, accs) + validatorSrcAddress := sdk.ValAddress(validatorSrcAcc.Address) + validatorDstAcc := simulation.RandomAcc(r, accs) + validatorDstAddress := sdk.ValAddress(validatorDstAcc.Address) + delegatorAcc := simulation.RandomAcc(r, accs) + delegatorAddress := delegatorAcc.Address msg := stake.MsgCompleteRedelegate{ DelegatorAddr: delegatorAddress, ValidatorSrcAddr: validatorSrcAddress, @@ -259,7 +256,7 @@ func SimulateMsgCompleteRedelegate(k stake.Keeper) simulation.Operation { // Setup // nolint: errcheck func Setup(mapp *mock.App, k stake.Keeper) simulation.RandSetup { - return func(r *rand.Rand, privKeys []crypto.PrivKey) { + return func(r *rand.Rand, accs []simulation.Account) { ctx := mapp.NewContext(false, abci.Header{}) gen := stake.DefaultGenesisState() gen.Params.InflationMax = sdk.NewDec(0) diff --git a/x/stake/simulation/sim_test.go b/x/stake/simulation/sim_test.go index 53b9d826c..b81555f03 100644 --- a/x/stake/simulation/sim_test.go +++ b/x/stake/simulation/sim_test.go @@ -6,7 +6,6 @@ import ( "testing" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/bank" @@ -38,8 +37,8 @@ func TestStakeWithRandomMessages(t *testing.T) { panic(err) } - appStateFn := func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage { - mock.RandomSetGenesis(r, mapp, accs, []string{"stake"}) + appStateFn := func(r *rand.Rand, accs []simulation.Account) json.RawMessage { + simulation.RandomSetGenesis(r, mapp, accs, []string{"stake"}) return json.RawMessage("{}") } From 6b595842ed43b498bb6c124db8ed6685c0915e50 Mon Sep 17 00:00:00 2001 From: Rigel Date: Tue, 25 Sep 2018 00:09:31 -0400 Subject: [PATCH 2/9] Distr-PR-1 Staking ConsPubKey -> ConsAddr index (#2369) * pulling in stuff from fee-distr PR * revert some gov changes * fix using cons address, also remove old commented distr code * doc update * val comments * remove GetValidatorByConsPubKey --- PENDING.md | 1 + docs/spec/staking/state.md | 11 +-- examples/democoin/mock/validator.go | 22 ++++-- types/address.go | 5 ++ types/stake.go | 35 ++++----- x/distribution/keeper.go | 91 ----------------------- x/distribution/keeper_test.go | 31 -------- x/distribution/movement.go | 72 ------------------- x/distribution/types.go | 107 ---------------------------- x/gov/handler.go | 2 +- x/gov/tally.go | 4 +- x/gov/tally_test.go | 2 +- x/slashing/genesis.go | 2 +- x/slashing/handler.go | 8 +-- x/slashing/hooks.go | 23 +++--- x/slashing/keeper.go | 37 +++++----- x/slashing/keeper_test.go | 41 ++++++----- x/slashing/tick_test.go | 2 +- x/stake/client/cli/flags.go | 8 +-- x/stake/genesis.go | 4 +- x/stake/handler.go | 4 +- x/stake/handler_test.go | 17 +++-- x/stake/keeper/delegation.go | 1 + x/stake/keeper/keeper.go | 28 ++++---- x/stake/keeper/key.go | 8 +-- x/stake/keeper/sdk_types.go | 23 +++--- x/stake/keeper/slash.go | 33 ++++----- x/stake/keeper/slash_test.go | 74 +++++++++---------- x/stake/keeper/validator.go | 26 +++---- x/stake/keeper/validator_test.go | 14 +++- x/stake/simulation/invariants.go | 2 +- x/stake/stake.go | 4 +- x/stake/types/codec.go | 3 +- x/stake/types/delegation.go | 2 +- x/stake/types/validator.go | 20 +++--- 35 files changed, 248 insertions(+), 519 deletions(-) delete mode 100644 x/distribution/keeper.go delete mode 100644 x/distribution/keeper_test.go delete mode 100644 x/distribution/movement.go delete mode 100644 x/distribution/types.go diff --git a/PENDING.md b/PENDING.md index ec3c7f930..344e3628d 100644 --- a/PENDING.md +++ b/PENDING.md @@ -51,6 +51,7 @@ BREAKING CHANGES * [codec] \#2324 All referrences to wire have been renamed to codec. Additionally, wire.NewCodec is now codec.New(). * [types] \#2343 Make sdk.Msg have a names field, to facilitate automatic tagging. * [baseapp] \#2366 Automatically add action tags to all messages + * [x/staking] \#2244 staking now holds a consensus-address-index instead of a consensus-pubkey-index * Tendermint diff --git a/docs/spec/staking/state.md b/docs/spec/staking/state.md index f2d6f9854..9454aca7d 100644 --- a/docs/spec/staking/state.md +++ b/docs/spec/staking/state.md @@ -43,12 +43,13 @@ type Params struct { Validators are identified according to the `OperatorAddr`, an SDK validator address for the operator of the validator. -Validators also have a `ConsPubKey`, the public key of the validator. - -Validators are indexed in the store using the following maps: +Validators also have a `ConsPubKey`, the public key of the validator used in +Tendermint consensus. The validator can be retrieved from it's `ConsPubKey` +once it can be converted into the corresponding `ConsAddr`. Validators are +indexed in the store using the following maps: - Validators: `0x02 | OperatorAddr -> amino(validator)` -- ValidatorsByPubKey: `0x03 | ConsPubKey -> OperatorAddr` +- ValidatorsByConsAddr: `0x03 | ConsAddr -> OperatorAddr` - ValidatorsByPower: `0x05 | power | blockHeight | blockTx -> OperatorAddr` `Validators` is the primary index - it ensures that each operator can have only one @@ -69,7 +70,7 @@ validator. ```golang type Validator struct { - ConsensusPubKey crypto.PubKey // Tendermint consensus pubkey of validator + ConsPubKey crypto.PubKey // Tendermint consensus pubkey of validator Jailed bool // has the validator been jailed? Status sdk.BondStatus // validator status (bonded/unbonding/unbonded) diff --git a/examples/democoin/mock/validator.go b/examples/democoin/mock/validator.go index 9f84786ad..a54cdfedf 100644 --- a/examples/democoin/mock/validator.go +++ b/examples/democoin/mock/validator.go @@ -24,7 +24,12 @@ func (v Validator) GetOperator() sdk.ValAddress { } // Implements sdk.Validator -func (v Validator) GetPubKey() crypto.PubKey { +func (v Validator) GetConsPubKey() crypto.PubKey { + return nil +} + +// Implements sdk.Validator +func (v Validator) GetConsAddr() sdk.ConsAddress { return nil } @@ -88,7 +93,12 @@ func (vs *ValidatorSet) Validator(ctx sdk.Context, addr sdk.ValAddress) sdk.Vali } // ValidatorByPubKey implements sdk.ValidatorSet -func (vs *ValidatorSet) ValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) sdk.Validator { +func (vs *ValidatorSet) ValidatorByConsPubKey(_ sdk.Context, _ crypto.PubKey) sdk.Validator { + panic("not implemented") +} + +// ValidatorByPubKey implements sdk.ValidatorSet +func (vs *ValidatorSet) ValidatorByConsAddr(_ sdk.Context, _ sdk.ConsAddress) sdk.Validator { panic("not implemented") } @@ -122,21 +132,21 @@ func (vs *ValidatorSet) RemoveValidator(addr sdk.AccAddress) { } // Implements sdk.ValidatorSet -func (vs *ValidatorSet) Slash(ctx sdk.Context, pubkey crypto.PubKey, height int64, power int64, amt sdk.Dec) { +func (vs *ValidatorSet) Slash(_ sdk.Context, _ sdk.ConsAddress, _ int64, _ int64, _ sdk.Dec) { panic("not implemented") } // Implements sdk.ValidatorSet -func (vs *ValidatorSet) Jail(ctx sdk.Context, pubkey crypto.PubKey) { +func (vs *ValidatorSet) Jail(_ sdk.Context, _ sdk.ConsAddress) { panic("not implemented") } // Implements sdk.ValidatorSet -func (vs *ValidatorSet) Unjail(ctx sdk.Context, pubkey crypto.PubKey) { +func (vs *ValidatorSet) Unjail(_ sdk.Context, _ sdk.ConsAddress) { panic("not implemented") } // Implements sdk.ValidatorSet -func (vs *ValidatorSet) Delegation(ctx sdk.Context, addrDel sdk.AccAddress, addrVal sdk.ValAddress) sdk.Delegation { +func (vs *ValidatorSet) Delegation(_ sdk.Context, _ sdk.AccAddress, _ sdk.ValAddress) sdk.Delegation { panic("not implemented") } diff --git a/types/address.go b/types/address.go index 58b694f5d..ae13b2ad0 100644 --- a/types/address.go +++ b/types/address.go @@ -292,6 +292,11 @@ func ConsAddressFromBech32(address string) (addr ConsAddress, err error) { return ConsAddress(bz), nil } +// get ConsAddress from pubkey +func GetConsAddress(pubkey crypto.PubKey) ConsAddress { + return ConsAddress(pubkey.Address()) +} + // Returns boolean for whether two ConsAddress are Equal func (ca ConsAddress) Equals(ca2 ConsAddress) bool { if ca.Empty() && ca2.Empty() { diff --git a/types/stake.go b/types/stake.go index 0b9f32b4b..e794ea734 100644 --- a/types/stake.go +++ b/types/stake.go @@ -37,22 +37,23 @@ func (b BondStatus) Equal(b2 BondStatus) bool { // validator for a delegated proof of stake system type Validator interface { - GetJailed() bool // whether the validator is jailed - GetMoniker() string // moniker of the validator - GetStatus() BondStatus // status of the validator - GetOperator() ValAddress // operator address to receive/return validators coins - GetPubKey() crypto.PubKey // validation pubkey - GetPower() Dec // validation power - GetTokens() Dec // validation tokens - GetDelegatorShares() Dec // Total out standing delegator shares - GetBondHeight() int64 // height in which the validator became active + GetJailed() bool // whether the validator is jailed + GetMoniker() string // moniker of the validator + GetStatus() BondStatus // status of the validator + GetOperator() ValAddress // operator address to receive/return validators coins + GetConsPubKey() crypto.PubKey // validation consensus pubkey + GetConsAddr() ConsAddress // validation consensus address + GetPower() Dec // validation power + GetTokens() Dec // validation tokens + GetDelegatorShares() Dec // Total out standing delegator shares + GetBondHeight() int64 // height in which the validator became active } // validator which fulfills abci validator interface for use in Tendermint func ABCIValidator(v Validator) abci.Validator { return abci.Validator{ - PubKey: tmtypes.TM2PB.PubKey(v.GetPubKey()), - Address: v.GetPubKey().Address(), + PubKey: tmtypes.TM2PB.PubKey(v.GetConsPubKey()), + Address: v.GetConsPubKey().Address(), Power: v.GetPower().RoundInt64(), } } @@ -67,14 +68,14 @@ type ValidatorSet interface { IterateValidatorsBonded(Context, func(index int64, validator Validator) (stop bool)) - Validator(Context, ValAddress) Validator // get a particular validator by operator - ValidatorByPubKey(Context, crypto.PubKey) Validator // get a particular validator by signing PubKey + Validator(Context, ValAddress) Validator // get a particular validator by operator address + ValidatorByConsAddr(Context, ConsAddress) Validator // get a particular validator by consensus address TotalPower(Context) Dec // total power of the validator set // slash the validator and delegators of the validator, specifying offence height, offence power, and slash fraction - Slash(Context, crypto.PubKey, int64, int64, Dec) - Jail(Context, crypto.PubKey) // jail a validator - Unjail(Context, crypto.PubKey) // unjail a validator + Slash(Context, ConsAddress, int64, int64, Dec) + Jail(Context, ConsAddress) // jail a validator + Unjail(Context, ConsAddress) // unjail a validator // Delegation allows for getting a particular delegation for a given validator // and delegator outside the scope of the staking module. @@ -87,7 +88,7 @@ type ValidatorSet interface { type Delegation interface { GetDelegator() AccAddress // delegator AccAddress for the bond GetValidator() ValAddress // validator operator address - GetBondShares() Dec // amount of validator's shares + GetShares() Dec // amount of validator's shares held in this delegation } // properties for the set of all delegations for a particular diff --git a/x/distribution/keeper.go b/x/distribution/keeper.go deleted file mode 100644 index 937a4674c..000000000 --- a/x/distribution/keeper.go +++ /dev/null @@ -1,91 +0,0 @@ -package stake - -//// keeper of the staking store -//type Keeper struct { -//storeKey sdk.StoreKey -//cdc *codec.Codec -//bankKeeper bank.Keeper - -//// codespace -//codespace sdk.CodespaceType -//} - -//func NewKeeper(cdc *codec.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper { -//keeper := Keeper{ -//storeKey: key, -//cdc: cdc, -//bankKeeper: ck, -//codespace: codespace, -//} -//return keeper -//} - -////_________________________________________________________________________ - -//// cummulative power of the non-absent prevotes -//func (k Keeper) GetTotalPrecommitVotingPower(ctx sdk.Context) sdk.Dec { -//store := ctx.KVStore(k.storeKey) - -//// get absent prevote indexes -//absents := ctx.AbsentValidators() - -//TotalPower := sdk.ZeroDec() -//i := int32(0) -//iterator := store.SubspaceIterator(ValidatorsBondedKey) -//for ; iterator.Valid(); iterator.Next() { - -//skip := false -//for j, absentIndex := range absents { -//if absentIndex > i { -//break -//} - -//// if non-voting validator found, skip adding its power -//if absentIndex == i { -//absents = append(absents[:j], absents[j+1:]...) // won't need again -//skip = true -//break -//} -//} -//if skip { -//continue -//} - -//bz := iterator.Value() -//var validator Validator -//k.cdc.MustUnmarshalBinary(bz, &validator) -//TotalPower = TotalPower.Add(validator.Power) -//i++ -//} -//iterator.Close() -//return TotalPower -//} - -////_______________________________________________________________________ - -//// XXX TODO trim functionality - -//// retrieve all the power changes which occur after a height -//func (k Keeper) GetPowerChangesAfterHeight(ctx sdk.Context, earliestHeight int64) (pcs []PowerChange) { -//store := ctx.KVStore(k.storeKey) - -//iterator := store.SubspaceIterator(PowerChangeKey) //smallest to largest -//for ; iterator.Valid(); iterator.Next() { -//pcBytes := iterator.Value() -//var pc PowerChange -//k.cdc.MustUnmarshalBinary(pcBytes, &pc) -//if pc.Height < earliestHeight { -//break -//} -//pcs = append(pcs, pc) -//} -//iterator.Close() -//return -//} - -//// set a power change -//func (k Keeper) setPowerChange(ctx sdk.Context, pc PowerChange) { -//store := ctx.KVStore(k.storeKey) -//b := k.cdc.MustMarshalBinary(pc) -//store.Set(GetPowerChangeKey(pc.Height), b) -//} diff --git a/x/distribution/keeper_test.go b/x/distribution/keeper_test.go deleted file mode 100644 index 890268060..000000000 --- a/x/distribution/keeper_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package stake - -//// test if is a gotValidator from the last update -//func TestGetTotalPrecommitVotingPower(t *testing.T) { -//ctx, _, keeper := createTestInput(t, false, 0) - -//amts := []int64{10000, 1000, 100, 10, 1} -//var candidatesIn [5]Candidate -//for i, amt := range amts { -//candidatesIn[i] = NewCandidate(addrVals[i], pks[i], Description{}) -//candidatesIn[i].BondedShares = sdk.NewDec(amt) -//candidatesIn[i].DelegatorShares = sdk.NewDec(amt) -//keeper.setCandidate(ctx, candidatesIn[i]) -//} - -//// test that an empty gotValidator set doesn't have any gotValidators -//gotValidators := keeper.GetValidators(ctx) -//require.Equal(t, 5, len(gotValidators)) - -//totPow := keeper.GetTotalPrecommitVotingPower(ctx) -//exp := sdk.NewDec(11111) -//require.True(t, exp.Equal(totPow), "exp %v, got %v", exp, totPow) - -//// set absent gotValidators to be the 1st and 3rd record sorted by pubKey address -//ctx = ctx.WithAbsentValidators([]int32{1, 3}) -//totPow = keeper.GetTotalPrecommitVotingPower(ctx) - -//// XXX verify that this order should infact exclude these two records -//exp = sdk.NewDec(11100) -//require.True(t, exp.Equal(totPow), "exp %v, got %v", exp, totPow) -//} diff --git a/x/distribution/movement.go b/x/distribution/movement.go deleted file mode 100644 index 399a25a68..000000000 --- a/x/distribution/movement.go +++ /dev/null @@ -1,72 +0,0 @@ -package stake - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// burn burn burn -func BurnFeeHandler(ctx sdk.Context, _ sdk.Tx, collectedFees sdk.Coins) {} - -//// Handle fee distribution to the validators and delegators -//func (k Keeper) FeeHandler(ctx sdk.Context, collectedFees sdk.Coins) { -//pool := k.GetPool(ctx) -//params := k.GetParams(ctx) - -//// XXX determine -//candidate := NewCandidate(addrs[0], pks[0], Description{}) - -//// calculate the proposer reward -//precommitPower := k.GetTotalPrecommitVotingPower(ctx) -//toProposer := coinsMulRat(collectedFees, (sdk.NewDec(1, 100).Add(sdk.NewDec(4, 100).Mul(precommitPower).Quo(pool.BondedShares)))) -//candidate.ProposerRewardPool = candidate.ProposerRewardPool.Plus(toProposer) - -//toReservePool := coinsMulRat(collectedFees, params.ReservePoolFee) -//pool.FeeReservePool = pool.FeeReservePool.Plus(toReservePool) - -//distributedReward := (collectedFees.Minus(toProposer)).Minus(toReservePool) -//pool.FeePool = pool.FeePool.Plus(distributedReward) -//pool.FeeSumReceived = pool.FeeSumReceived.Plus(distributedReward) -//pool.FeeRecent = distributedReward - -//// lastly update the FeeRecent term -//pool.FeeRecent = collectedFees - -//k.setPool(ctx, pool) -//} - -//func coinsMulRat(coins sdk.Coins, rat sdk.Dec) sdk.Coins { -//var res sdk.Coins -//for _, coin := range coins { -//coinMulAmt := rat.Mul(sdk.NewDec(coin.Amount)).Evaluate() -//coinMul := sdk.Coins{{coin.Denom, coinMulAmt}} -//res = res.Plus(coinMul) -//} -//return res -//} - -////____________________________________________________________________________- - -//// calculate adjustment changes for a candidate at a height -//func CalculateAdjustmentChange(candidate Candidate, pool Pool, denoms []string, height int64) (Candidate, Pool) { - -//heightRat := sdk.NewDec(height) -//lastHeightRat := sdk.NewDec(height - 1) -//candidateFeeCount := candidate.BondedShares.Mul(heightRat) -//poolFeeCount := pool.BondedShares.Mul(heightRat) - -//for i, denom := range denoms { -//poolFeeSumReceived := sdk.NewDec(pool.FeeSumReceived.AmountOf(denom)) -//poolFeeRecent := sdk.NewDec(pool.FeeRecent.AmountOf(denom)) -//// calculate simple and projected pools -//simplePool := candidateFeeCount.Quo(poolFeeCount).Mul(poolFeeSumReceived) -//calc1 := candidate.PrevBondedShares.Mul(lastHeightRat).Quo(pool.PrevBondedShares.Mul(lastHeightRat)).Mul(poolFeeRecent) -//calc2 := candidate.BondedShares.Quo(pool.BondedShares).Mul(poolFeeRecent) -//projectedPool := calc1.Add(calc2) - -//AdjustmentChange := simplePool.Sub(projectedPool) -//candidate.FeeAdjustments[i] = candidate.FeeAdjustments[i].Add(AdjustmentChange) -//pool.FeeAdjustments[i] = pool.FeeAdjustments[i].Add(AdjustmentChange) -//} - -//return candidate, pool -//} diff --git a/x/distribution/types.go b/x/distribution/types.go deleted file mode 100644 index 223410471..000000000 --- a/x/distribution/types.go +++ /dev/null @@ -1,107 +0,0 @@ -package stake - -//// GenesisState - all staking state that must be provided at genesis -//type GenesisState struct { -//Pool Pool `json:"pool"` -//Params Params `json:"params"` -//} - -//func NewGenesisState(pool Pool, params Params, candidates []Candidate, bonds []Delegation) GenesisState { -//return GenesisState{ -//Pool: pool, -//Params: params, -//} -//} - -//// get raw genesis raw message for testing -//func DefaultGenesisState() GenesisState { -//return GenesisState{ -//Pool: initialPool(), -//Params: defaultParams(), -//} -//} - -//// fee information for a validator -//type Validator struct { -//Adjustments []sdk.Dec `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms -//PrevBondedShares sdk.Dec `json:"prev_bonded_shares"` // total shares of a global hold pools -//} - -////_________________________________________________________________________ - -//// Params defines the high level settings for staking -//type Params struct { -//FeeDenoms []string `json:"fee_denoms"` // accepted fee denoms -//ReservePoolFee sdk.Dec `json:"reserve_pool_fee"` // percent of fees which go to reserve pool -//} - -//func (p Params) equal(p2 Params) bool { -//return p.BondDenom == p2.BondDenom && -//p.ReservePoolFee.Equal(p2.ReservePoolFee) -//} - -//func defaultParams() Params { -//return Params{ -//FeeDenoms: []string{"steak"}, -//ReservePoolFee: sdk.NewDec(5, 100), -//} -//} - -////_________________________________________________________________________ - -//// Pool - dynamic parameters of the current state -//type Pool struct { -//FeeReservePool sdk.Coins `json:"fee_reserve_pool"` // XXX reserve pool of collected fees for use by governance -//FeePool sdk.Coins `json:"fee_pool"` // XXX fee pool for all the fee shares which have already been distributed -//FeeSumReceived sdk.Coins `json:"fee_sum_received"` // XXX sum of all fees received, post reserve pool `json:"fee_sum_received"` -//FeeRecent sdk.Coins `json:"fee_recent"` // XXX most recent fee collected -//FeeAdjustments []sdk.Dec `json:"fee_adjustments"` // XXX Adjustment factors for lazy fee accounting, couples with Params.BondDenoms -//PrevBondedShares sdk.Dec `json:"prev_bonded_shares"` // XXX last recorded bonded shares -//} - -//func (p Pool) equal(p2 Pool) bool { -//return p.FeeReservePool.IsEqual(p2.FeeReservePool) && -//p.FeePool.IsEqual(p2.FeePool) && -//p.FeeSumReceived.IsEqual(p2.FeeSumReceived) && -//p.FeeRecent.IsEqual(p2.FeeRecent) && -//sdk.DecsEqual(p.FeeAdjustments, p2.FeeAdjustments) && -//p.PrevBondedShares.Equal(p2.PrevBondedShares) -//} - -//// initial pool for testing -//func initialPool() Pool { -//return Pool{ -//FeeReservePool: sdk.Coins(nil), -//FeePool: sdk.Coins(nil), -//FeeSumReceived: sdk.Coins(nil), -//FeeRecent: sdk.Coins(nil), -//FeeAdjustments: []sdk.Dec{sdk.ZeroDec()}, -//PrevBondedShares: sdk.ZeroDec(), -//} -//} - -////_________________________________________________________________________ - -//// Used in calculation of fee shares, added to a queue for each block where a power change occures -//type PowerChange struct { -//Height int64 `json:"height"` // block height at change -//Power sdk.Dec `json:"power"` // total power at change -//PrevPower sdk.Dec `json:"prev_power"` // total power at previous height-1 -//FeesIn sdk.Coins `json:"fees_in"` // fees in at block height -//PrevFeePool sdk.Coins `json:"prev_fee_pool"` // total fees in at previous block height -//} - -////_________________________________________________________________________ -//// KEY MANAGEMENT - -//var ( -//// Keys for store prefixes -//PowerChangeKey = []byte{0x09} // prefix for power change object -//) - -//// get the key for the accumulated update validators -//func GetPowerChangeKey(height int64) []byte { -//heightBytes := make([]byte, binary.MaxVarintLen64) -//binary.BigEndian.PutUint64(heightBytes, ^uint64(height)) // invert height (older validators first) -//return append(PowerChangeKey, heightBytes...) -//} diff --git a/x/gov/handler.go b/x/gov/handler.go index e8cdf49aa..00eaf3744 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -155,7 +155,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { for _, valAddr := range nonVotingVals { val := keeper.ds.GetValidatorSet().Validator(ctx, valAddr) keeper.ds.GetValidatorSet().Slash(ctx, - val.GetPubKey(), + val.GetConsAddr(), ctx.BlockHeight(), val.GetPower().RoundInt64(), keeper.GetTallyingProcedure(ctx).GovernancePenalty) diff --git a/x/gov/tally.go b/x/gov/tally.go index a756eaf92..55fd81da2 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -53,10 +53,10 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall valAddrStr := delegation.GetValidator().String() if val, ok := currValidators[valAddrStr]; ok { - val.Minus = val.Minus.Add(delegation.GetBondShares()) + val.Minus = val.Minus.Add(delegation.GetShares()) currValidators[valAddrStr] = val - delegatorShare := delegation.GetBondShares().Quo(val.DelegatorShares) + delegatorShare := delegation.GetShares().Quo(val.DelegatorShares) votingPower := val.Power.Mul(delegatorShare) results[vote.Option] = results[vote.Option].Add(votingPower) diff --git a/x/gov/tally_test.go b/x/gov/tally_test.go index 71983bf59..d8e3e290c 100644 --- a/x/gov/tally_test.go +++ b/x/gov/tally_test.go @@ -444,7 +444,7 @@ func TestTallyJailedValidator(t *testing.T) { val2, found := sk.GetValidator(ctx, sdk.ValAddress(addrs[1])) require.True(t, found) - sk.Jail(ctx, val2.ConsPubKey) + sk.Jail(ctx, sdk.ConsAddress(val2.ConsPubKey.Address())) proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) proposalID := proposal.GetProposalID() diff --git a/x/slashing/genesis.go b/x/slashing/genesis.go index 6e2809bfc..43ae6b0d0 100644 --- a/x/slashing/genesis.go +++ b/x/slashing/genesis.go @@ -8,7 +8,7 @@ import ( // InitGenesis initializes the keeper's address to pubkey map. func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) { for _, validator := range data.Validators { - keeper.addPubkey(ctx, validator.GetPubKey()) + keeper.addPubkey(ctx, validator.GetConsPubKey()) } return } diff --git a/x/slashing/handler.go b/x/slashing/handler.go index c43ed6be6..740166d2a 100644 --- a/x/slashing/handler.go +++ b/x/slashing/handler.go @@ -34,9 +34,9 @@ func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result { return ErrValidatorNotJailed(k.codespace).Result() } - addr := sdk.ConsAddress(validator.GetPubKey().Address()) + consAddr := sdk.ConsAddress(validator.GetConsPubKey().Address()) - info, found := k.getValidatorSigningInfo(ctx, addr) + info, found := k.getValidatorSigningInfo(ctx, consAddr) if !found { return ErrNoValidatorForAddress(k.codespace).Result() } @@ -49,9 +49,9 @@ func handleMsgUnjail(ctx sdk.Context, msg MsgUnjail, k Keeper) sdk.Result { // update the starting height so the validator can't be immediately jailed // again info.StartHeight = ctx.BlockHeight() - k.setValidatorSigningInfo(ctx, addr, info) + k.setValidatorSigningInfo(ctx, consAddr, info) - k.validatorSet.Unjail(ctx, validator.GetPubKey()) + k.validatorSet.Unjail(ctx, consAddr) tags := sdk.NewTags("action", []byte("unjail"), "validator", []byte(msg.ValidatorAddr.String())) diff --git a/x/slashing/hooks.go b/x/slashing/hooks.go index f5f3cc48c..92c4cf85f 100644 --- a/x/slashing/hooks.go +++ b/x/slashing/hooks.go @@ -22,25 +22,26 @@ func (k Keeper) onValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddre k.addOrUpdateValidatorSlashingPeriod(ctx, slashingPeriod) } -// Wrapper struct for sdk.ValidatorHooks -type ValidatorHooks struct { +//_________________________________________________________________________________________ + +// Wrapper struct +type Hooks struct { k Keeper } -// Assert implementation -var _ sdk.ValidatorHooks = ValidatorHooks{} +var _ sdk.ValidatorHooks = Hooks{} -// Return a sdk.ValidatorHooks interface over the wrapper struct -func (k Keeper) ValidatorHooks() sdk.ValidatorHooks { - return ValidatorHooks{k} +// Return the wrapper struct +func (k Keeper) ValidatorHooks() Hooks { + return Hooks{k} } // Implements sdk.ValidatorHooks -func (v ValidatorHooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) { - v.k.onValidatorBonded(ctx, address) +func (h Hooks) OnValidatorBonded(ctx sdk.Context, address sdk.ConsAddress) { + h.k.onValidatorBonded(ctx, address) } // Implements sdk.ValidatorHooks -func (v ValidatorHooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress) { - v.k.onValidatorBeginUnbonding(ctx, address) +func (h Hooks) OnValidatorBeginUnbonding(ctx sdk.Context, address sdk.ConsAddress) { + h.k.onValidatorBeginUnbonding(ctx, address) } diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index 5a008ccec..e639182e1 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -40,10 +40,10 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio logger := ctx.Logger().With("module", "x/slashing") time := ctx.BlockHeader().Time age := time.Sub(timestamp) - address := sdk.ConsAddress(addr) + consAddr := sdk.ConsAddress(addr) pubkey, err := k.getPubkey(ctx, addr) if err != nil { - panic(fmt.Sprintf("Validator address %v not found", addr)) + panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr)) } // Double sign too old @@ -59,37 +59,38 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio // Cap the amount slashed to the penalty for the worst infraction // within the slashing period when this infraction was committed fraction := k.SlashFractionDoubleSign(ctx) - revisedFraction := k.capBySlashingPeriod(ctx, address, fraction, infractionHeight) + revisedFraction := k.capBySlashingPeriod(ctx, consAddr, fraction, infractionHeight) logger.Info(fmt.Sprintf("Fraction slashed capped by slashing period from %v to %v", fraction, revisedFraction)) // Slash validator - k.validatorSet.Slash(ctx, pubkey, infractionHeight, power, revisedFraction) + k.validatorSet.Slash(ctx, consAddr, infractionHeight, power, revisedFraction) // Jail validator - k.validatorSet.Jail(ctx, pubkey) + k.validatorSet.Jail(ctx, consAddr) // Set validator jail duration - signInfo, found := k.getValidatorSigningInfo(ctx, address) + signInfo, found := k.getValidatorSigningInfo(ctx, consAddr) if !found { - panic(fmt.Sprintf("Expected signing info for validator %s but not found", address)) + panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr)) } signInfo.JailedUntil = time.Add(k.DoubleSignUnbondDuration(ctx)) - k.setValidatorSigningInfo(ctx, address, signInfo) + k.setValidatorSigningInfo(ctx, consAddr, signInfo) } // handle a validator signature, must be called once per validator per block +// TODO refactor to take in a consensus address, additionally should maybe just take in the pubkey too // nolint gocyclo func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, power int64, signed bool) { logger := ctx.Logger().With("module", "x/slashing") height := ctx.BlockHeight() - address := sdk.ConsAddress(addr) + consAddr := sdk.ConsAddress(addr) pubkey, err := k.getPubkey(ctx, addr) if err != nil { - panic(fmt.Sprintf("Validator address %v not found", addr)) + panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr)) } // Local index, so counts blocks validator *should* have signed // Will use the 0-value default signing info if not present, except for start height - signInfo, found := k.getValidatorSigningInfo(ctx, address) + signInfo, found := k.getValidatorSigningInfo(ctx, consAddr) if !found { // If this validator has never been seen before, construct a new SigningInfo with the correct start height signInfo = NewValidatorSigningInfo(height, 0, time.Unix(0, 0), 0) @@ -100,16 +101,16 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p // Update signed block bit array & counter // This counter just tracks the sum of the bit array // That way we avoid needing to read/write the whole array each time - previous := k.getValidatorSigningBitArray(ctx, address, index) + previous := k.getValidatorSigningBitArray(ctx, consAddr, index) if previous == signed { // Array value at this index has not changed, no need to update counter } else if previous && !signed { // Array value has changed from signed to unsigned, decrement counter - k.setValidatorSigningBitArray(ctx, address, index, false) + k.setValidatorSigningBitArray(ctx, consAddr, index, false) signInfo.SignedBlocksCounter-- } else if !previous && signed { // Array value has changed from unsigned to signed, increment counter - k.setValidatorSigningBitArray(ctx, address, index, true) + k.setValidatorSigningBitArray(ctx, consAddr, index, true) signInfo.SignedBlocksCounter++ } @@ -118,13 +119,13 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p } minHeight := signInfo.StartHeight + k.SignedBlocksWindow(ctx) if height > minHeight && signInfo.SignedBlocksCounter < k.MinSignedPerWindow(ctx) { - validator := k.validatorSet.ValidatorByPubKey(ctx, pubkey) + validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr) if validator != nil && !validator.GetJailed() { // Downtime confirmed: slash and jail the validator logger.Info(fmt.Sprintf("Validator %s past min height of %d and below signed blocks threshold of %d", pubkey.Address(), minHeight, k.MinSignedPerWindow(ctx))) - k.validatorSet.Slash(ctx, pubkey, height, power, k.SlashFractionDowntime(ctx)) - k.validatorSet.Jail(ctx, pubkey) + k.validatorSet.Slash(ctx, consAddr, height, power, k.SlashFractionDowntime(ctx)) + k.validatorSet.Jail(ctx, consAddr) signInfo.JailedUntil = ctx.BlockHeader().Time.Add(k.DowntimeUnbondDuration(ctx)) } else { // Validator was (a) not found or (b) already jailed, don't slash @@ -134,7 +135,7 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p } // Set the updated signing info - k.setValidatorSigningInfo(ctx, address, signInfo) + k.setValidatorSigningInfo(ctx, consAddr, signInfo) } // AddValidators adds the validators to the keepers validator addr to pubkey mapping. diff --git a/x/slashing/keeper_test.go b/x/slashing/keeper_test.go index ff50a594e..d4b1af3d5 100644 --- a/x/slashing/keeper_test.go +++ b/x/slashing/keeper_test.go @@ -18,8 +18,11 @@ func init() { defaultDoubleSignUnbondDuration = 60 * 60 } +// ______________________________________________________________ + // Test that a validator is slashed correctly // when we discover evidence of infraction +// TODO fix this test to not be using the same pubkey/address for signing and operating, it's confusing func TestHandleDoubleSign(t *testing.T) { // initial setup @@ -43,7 +46,7 @@ func TestHandleDoubleSign(t *testing.T) { // should be jailed require.True(t, sk.Validator(ctx, addr).GetJailed()) // unjail to measure power - sk.Unjail(ctx, val) + sk.Unjail(ctx, sdk.ConsAddress(addr)) // TODO distinguish cons address // power should be reduced require.Equal( t, sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))), @@ -61,14 +64,16 @@ func TestHandleDoubleSign(t *testing.T) { // Test that the amount a validator is slashed for multiple double signs // is correctly capped by the slashing period in which they were committed +// TODO properly distinguish between consensus and operator address is variable names func TestSlashingPeriodCap(t *testing.T) { // initial setup ctx, ck, sk, _, keeper := createTestInput(t) sk = sk.WithValidatorHooks(keeper.ValidatorHooks()) amtInt := int64(100) - addr, val, amt := addrs[0], pks[0], sdk.NewInt(amtInt) - got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, val, amt)) + addr, amt := addrs[0], sdk.NewInt(amtInt) + valConsPubKey, valConsAddr := pks[0], sdk.ConsAddress(pks[0].Address()) + got := stake.NewHandler(sk)(ctx, newTestMsgCreateValidator(addr, valConsPubKey, amt)) require.True(t, got.IsOK()) validatorUpdates := stake.EndBlocker(ctx, sk) keeper.AddValidators(ctx, validatorUpdates) @@ -76,39 +81,39 @@ func TestSlashingPeriodCap(t *testing.T) { require.True(t, sdk.NewDecFromInt(amt).Equal(sk.Validator(ctx, addr).GetPower())) // handle a signature to set signing info - keeper.handleValidatorSignature(ctx, val.Address(), amtInt, true) + keeper.handleValidatorSignature(ctx, valConsPubKey.Address(), amtInt, true) // double sign less than max age - keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt) + keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt) // should be jailed require.True(t, sk.Validator(ctx, addr).GetJailed()) // update block height ctx = ctx.WithBlockHeight(int64(1)) // unjail to measure power - sk.Unjail(ctx, val) + sk.Unjail(ctx, valConsAddr) // power should be reduced expectedPower := sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))) require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower()) // double sign again, same slashing period - keeper.handleDoubleSign(ctx, val.Address(), 0, time.Unix(0, 0), amtInt) + keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 0, time.Unix(0, 0), amtInt) // should be jailed require.True(t, sk.Validator(ctx, addr).GetJailed()) // update block height ctx = ctx.WithBlockHeight(int64(2)) // unjail to measure power - sk.Unjail(ctx, val) + sk.Unjail(ctx, valConsAddr) // power should be equal, no more should have been slashed expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(19).Quo(sdk.NewDec(20))) require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower()) // double sign again, new slashing period - keeper.handleDoubleSign(ctx, val.Address(), 2, time.Unix(0, 0), amtInt) + keeper.handleDoubleSign(ctx, valConsPubKey.Address(), 2, time.Unix(0, 0), amtInt) // should be jailed require.True(t, sk.Validator(ctx, addr).GetJailed()) // unjail to measure power - sk.Unjail(ctx, val) + sk.Unjail(ctx, valConsAddr) // power should be reduced expectedPower = sdk.NewDecFromInt(amt).Mul(sdk.NewDec(18).Quo(sdk.NewDec(20))) require.Equal(t, expectedPower, sk.Validator(ctx, addr).GetPower()) @@ -162,7 +167,7 @@ func TestHandleAbsentValidator(t *testing.T) { require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx), info.SignedBlocksCounter) // validator should be bonded still - validator, _ := sk.GetValidatorByPubKey(ctx, val) + validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Bonded, validator.GetStatus()) pool := sk.GetPool(ctx) require.Equal(t, amtInt, pool.BondedTokens.RoundInt64()) @@ -176,7 +181,7 @@ func TestHandleAbsentValidator(t *testing.T) { require.Equal(t, keeper.SignedBlocksWindow(ctx)-keeper.MinSignedPerWindow(ctx)-1, info.SignedBlocksCounter) // validator should have been jailed - validator, _ = sk.GetValidatorByPubKey(ctx, val) + validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Unbonding, validator.GetStatus()) // unrevocation should fail prior to jail expiration @@ -189,7 +194,7 @@ func TestHandleAbsentValidator(t *testing.T) { require.True(t, got.IsOK()) // validator should be rebonded now - validator, _ = sk.GetValidatorByPubKey(ctx, val) + validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Bonded, validator.GetStatus()) // validator should have been slashed @@ -207,7 +212,7 @@ func TestHandleAbsentValidator(t *testing.T) { height++ ctx = ctx.WithBlockHeight(height) keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) - validator, _ = sk.GetValidatorByPubKey(ctx, val) + validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Bonded, validator.GetStatus()) // 500 signed blocks @@ -223,7 +228,7 @@ func TestHandleAbsentValidator(t *testing.T) { ctx = ctx.WithBlockHeight(height) keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) } - validator, _ = sk.GetValidatorByPubKey(ctx, val) + validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Unbonding, validator.GetStatus()) } @@ -258,7 +263,7 @@ func TestHandleNewValidator(t *testing.T) { require.Equal(t, time.Unix(0, 0).UTC(), info.JailedUntil) // validator should be bonded still, should not have been jailed or slashed - validator, _ := sk.GetValidatorByPubKey(ctx, val) + 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.RoundInt64()) @@ -292,7 +297,7 @@ func TestHandleAlreadyJailed(t *testing.T) { } // validator should have been jailed and slashed - validator, _ := sk.GetValidatorByPubKey(ctx, val) + validator, _ := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, sdk.Unbonding, validator.GetStatus()) // validator should have been slashed @@ -303,7 +308,7 @@ func TestHandleAlreadyJailed(t *testing.T) { keeper.handleValidatorSignature(ctx, val.Address(), amtInt, false) // validator should not have been slashed twice - validator, _ = sk.GetValidatorByPubKey(ctx, val) + validator, _ = sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(val)) require.Equal(t, amtInt-1, validator.GetTokens().RoundInt64()) } diff --git a/x/slashing/tick_test.go b/x/slashing/tick_test.go index 81003a968..8225c9634 100644 --- a/x/slashing/tick_test.go +++ b/x/slashing/tick_test.go @@ -78,7 +78,7 @@ func TestBeginBlocker(t *testing.T) { } // validator should be jailed - validator, found := sk.GetValidatorByPubKey(ctx, pk) + validator, found := sk.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(pk)) require.True(t, found) require.Equal(t, sdk.Unbonding, validator.GetStatus()) } diff --git a/x/stake/client/cli/flags.go b/x/stake/client/cli/flags.go index 1228bec2a..bec76298f 100644 --- a/x/stake/client/cli/flags.go +++ b/x/stake/client/cli/flags.go @@ -58,8 +58,8 @@ func init() { fsDescriptionEdit.String(FlagIdentity, types.DoNotModifyDesc, "optional identity signature (ex. UPort or Keybase)") fsDescriptionEdit.String(FlagWebsite, types.DoNotModifyDesc, "optional website") fsDescriptionEdit.String(FlagDetails, types.DoNotModifyDesc, "optional details") - fsValidator.String(FlagAddressValidator, "", "hex address of the validator") - fsDelegator.String(FlagAddressDelegator, "", "hex address of the delegator") - fsRedelegation.String(FlagAddressValidatorSrc, "", "hex address of the source validator") - fsRedelegation.String(FlagAddressValidatorDst, "", "hex address of the destination validator") + fsValidator.String(FlagAddressValidator, "", "bech address of the validator") + fsDelegator.String(FlagAddressDelegator, "", "bech address of the delegator") + fsRedelegation.String(FlagAddressValidatorSrc, "", "bech address of the source validator") + fsRedelegation.String(FlagAddressValidatorDst, "", "bech address of the destination validator") } diff --git a/x/stake/genesis.go b/x/stake/genesis.go index 7a004bccd..58b7ed1b4 100644 --- a/x/stake/genesis.go +++ b/x/stake/genesis.go @@ -32,7 +32,7 @@ func InitGenesis(ctx sdk.Context, keeper Keeper, data types.GenesisState) (res [ } // Manually set indexes for the first time - keeper.SetValidatorByPubKeyIndex(ctx, validator) + keeper.SetValidatorByConsAddr(ctx, validator) keeper.SetValidatorByPowerIndex(ctx, validator, data.Pool) if validator.Status == sdk.Bonded { @@ -75,7 +75,7 @@ func WriteGenesis(ctx sdk.Context, keeper Keeper) types.GenesisState { func WriteValidators(ctx sdk.Context, keeper Keeper) (vals []tmtypes.GenesisValidator) { keeper.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) (stop bool) { vals = append(vals, tmtypes.GenesisValidator{ - PubKey: validator.GetPubKey(), + PubKey: validator.GetConsPubKey(), Power: validator.GetPower().RoundInt64(), Name: validator.GetMoniker(), }) diff --git a/x/stake/handler.go b/x/stake/handler.go index aaf372af2..0524b2c11 100644 --- a/x/stake/handler.go +++ b/x/stake/handler.go @@ -68,7 +68,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k return ErrValidatorOwnerExists(k.Codespace()).Result() } - _, found = k.GetValidatorByPubKey(ctx, msg.PubKey) + _, found = k.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(msg.PubKey)) if found { return ErrValidatorPubKeyExists(k.Codespace()).Result() } @@ -89,7 +89,7 @@ func handleMsgCreateValidator(ctx sdk.Context, msg types.MsgCreateValidator, k k } k.SetValidator(ctx, validator) - k.SetValidatorByPubKeyIndex(ctx, validator) + k.SetValidatorByConsAddr(ctx, validator) // move coins from the msg.Address account to a (self-delegation) delegator account // the validator account and global shares are updated within here diff --git a/x/stake/handler_test.go b/x/stake/handler_test.go index 845844fe2..5780bf543 100644 --- a/x/stake/handler_test.go +++ b/x/stake/handler_test.go @@ -84,8 +84,9 @@ func TestValidatorByPowerIndex(t *testing.T) { require.True(t, got.IsOK(), "expected create-validator to be ok, got %v", got) // slash and jail the first validator - keeper.Slash(ctx, keep.PKs[0], 0, initBond, sdk.NewDecWithPrec(5, 1)) - keeper.Jail(ctx, keep.PKs[0]) + consAddr0 := sdk.ConsAddress(keep.PKs[0].Address()) + keeper.Slash(ctx, consAddr0, 0, initBond, sdk.NewDecWithPrec(5, 1)) + keeper.Jail(ctx, consAddr0) validator, found = keeper.GetValidator(ctx, validatorAddr) require.True(t, found) require.Equal(t, sdk.Unbonding, validator.Status) // ensure is unbonding @@ -201,11 +202,12 @@ func TestLegacyValidatorDelegations(t *testing.T) { setInstantUnbondPeriod(keeper, ctx) bondAmount := int64(10) - valAddr, valPubKey := sdk.ValAddress(keep.Addrs[0]), keep.PKs[0] + valAddr := sdk.ValAddress(keep.Addrs[0]) + valConsPubKey, valConsAddr := keep.PKs[0], sdk.ConsAddress(keep.PKs[0].Address()) delAddr := keep.Addrs[1] // create validator - msgCreateVal := newTestMsgCreateValidator(valAddr, valPubKey, bondAmount) + msgCreateVal := newTestMsgCreateValidator(valAddr, valConsPubKey, bondAmount) got := handleMsgCreateValidator(ctx, msgCreateVal, keeper) require.True(t, got.IsOK(), "expected create validator msg to be ok, got %v", got) @@ -267,7 +269,7 @@ func TestLegacyValidatorDelegations(t *testing.T) { require.Equal(t, bondAmount*2, validator.Tokens.RoundInt64()) // unjail the validator now that is has non-zero self-delegated shares - keeper.Unjail(ctx, valPubKey) + keeper.Unjail(ctx, valConsAddr) // verify the validator can now accept delegations msgDelegate = newTestMsgDelegate(delAddr, valAddr, bondAmount) @@ -914,6 +916,7 @@ func TestCliffValidator(t *testing.T) { func TestBondUnbondRedelegateSlashTwice(t *testing.T) { ctx, _, keeper := keep.CreateTestInput(t, false, 1000) 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) got := handleMsgCreateValidator(ctx, msgCreateValidator, keeper) @@ -947,7 +950,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { require.Equal(t, sdk.NewDec(6), delegation.Shares) // slash the validator by half - keeper.Slash(ctx, keep.PKs[0], 0, 20, sdk.NewDecWithPrec(5, 1)) + keeper.Slash(ctx, consAddr0, 0, 20, sdk.NewDecWithPrec(5, 1)) // unbonding delegation should have been slashed by half unbonding, found := keeper.GetUnbondingDelegation(ctx, del, valA) @@ -971,7 +974,7 @@ func TestBondUnbondRedelegateSlashTwice(t *testing.T) { // slash the validator for an infraction committed after the unbonding and redelegation begin ctx = ctx.WithBlockHeight(3) - keeper.Slash(ctx, keep.PKs[0], 2, 10, sdk.NewDecWithPrec(5, 1)) + keeper.Slash(ctx, consAddr0, 2, 10, sdk.NewDecWithPrec(5, 1)) // unbonding delegation should be unchanged unbonding, found = keeper.GetUnbondingDelegation(ctx, del, valA) diff --git a/x/stake/keeper/delegation.go b/x/stake/keeper/delegation.go index ef09a307e..6efa4c8ed 100644 --- a/x/stake/keeper/delegation.go +++ b/x/stake/keeper/delegation.go @@ -66,6 +66,7 @@ func (k Keeper) SetDelegation(ctx sdk.Context, delegation types.Delegation) { // remove a delegation from store func (k Keeper) RemoveDelegation(ctx sdk.Context, delegation types.Delegation) { + store := ctx.KVStore(k.storeKey) store.Delete(GetDelegationKey(delegation.DelegatorAddr, delegation.ValidatorAddr)) } diff --git a/x/stake/keeper/keeper.go b/x/stake/keeper/keeper.go index a3754f750..0f700f9ab 100644 --- a/x/stake/keeper/keeper.go +++ b/x/stake/keeper/keeper.go @@ -10,11 +10,11 @@ import ( // keeper of the stake store type Keeper struct { - storeKey sdk.StoreKey - storeTKey sdk.StoreKey - cdc *codec.Codec - bankKeeper bank.Keeper - validatorHooks sdk.ValidatorHooks + storeKey sdk.StoreKey + storeTKey sdk.StoreKey + cdc *codec.Codec + bankKeeper bank.Keeper + hooks sdk.ValidatorHooks // codespace codespace sdk.CodespaceType @@ -22,22 +22,22 @@ type Keeper struct { func NewKeeper(cdc *codec.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper { keeper := Keeper{ - storeKey: key, - storeTKey: tkey, - cdc: cdc, - bankKeeper: ck, - validatorHooks: nil, - codespace: codespace, + storeKey: key, + storeTKey: tkey, + cdc: cdc, + bankKeeper: ck, + hooks: nil, + codespace: codespace, } return keeper } // Set the validator hooks -func (k Keeper) WithValidatorHooks(v sdk.ValidatorHooks) Keeper { - if k.validatorHooks != nil { +func (k Keeper) WithValidatorHooks(sh sdk.ValidatorHooks) Keeper { + if k.hooks != nil { panic("cannot set validator hooks twice") } - k.validatorHooks = v + k.hooks = sh return k } diff --git a/x/stake/keeper/key.go b/x/stake/keeper/key.go index c445e2552..91e6a6970 100644 --- a/x/stake/keeper/key.go +++ b/x/stake/keeper/key.go @@ -3,8 +3,6 @@ package keeper import ( "encoding/binary" - "github.com/tendermint/tendermint/crypto" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/types" ) @@ -17,7 +15,7 @@ var ( ParamKey = []byte{0x00} // key for parameters relating to staking PoolKey = []byte{0x01} // key for the staking pools ValidatorsKey = []byte{0x02} // prefix for each key to a validator - ValidatorsByPubKeyIndexKey = []byte{0x03} // prefix for each key to a validator index, by pubkey + ValidatorsByConsAddrKey = []byte{0x03} // prefix for each key to a validator index, by pubkey ValidatorsBondedIndexKey = []byte{0x04} // prefix for each key to a validator index, for bonded validators ValidatorsByPowerIndexKey = []byte{0x05} // prefix for each key to a validator index, sorted by power ValidatorCliffIndexKey = []byte{0x06} // key for the validator index of the cliff validator @@ -44,8 +42,8 @@ func GetValidatorKey(operatorAddr sdk.ValAddress) []byte { // gets the key for the validator with pubkey // VALUE: validator operator address ([]byte) -func GetValidatorByPubKeyIndexKey(pubkey crypto.PubKey) []byte { - return append(ValidatorsByPubKeyIndexKey, pubkey.Bytes()...) +func GetValidatorByConsAddrKey(addr sdk.ConsAddress) []byte { + return append(ValidatorsByConsAddrKey, addr.Bytes()...) } // gets the key for the current validator group diff --git a/x/stake/keeper/sdk_types.go b/x/stake/keeper/sdk_types.go index e4c7a1c19..d702e845d 100644 --- a/x/stake/keeper/sdk_types.go +++ b/x/stake/keeper/sdk_types.go @@ -3,8 +3,6 @@ package keeper import ( "fmt" - "github.com/tendermint/tendermint/crypto" - sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/types" ) @@ -60,8 +58,8 @@ func (k Keeper) Validator(ctx sdk.Context, address sdk.ValAddress) sdk.Validator } // get the sdk.validator for a particular pubkey -func (k Keeper) ValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) sdk.Validator { - val, found := k.GetValidatorByPubKey(ctx, pubkey) +func (k Keeper) ValidatorByConsAddr(ctx sdk.Context, addr sdk.ConsAddress) sdk.Validator { + val, found := k.GetValidatorByConsAddr(ctx, addr) if !found { return nil } @@ -95,15 +93,16 @@ func (k Keeper) Delegation(ctx sdk.Context, addrDel sdk.AccAddress, addrVal sdk. return bond } -// iterate through the active validator set and perform the provided function -func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.AccAddress, fn func(index int64, delegation sdk.Delegation) (stop bool)) { +// iterate through all of the delegations from a delegator +func (k Keeper) IterateDelegations(ctx sdk.Context, delAddr sdk.AccAddress, + fn func(index int64, del sdk.Delegation) (stop bool)) { + store := ctx.KVStore(k.storeKey) - key := GetDelegationsKey(delAddr) - iterator := sdk.KVStorePrefixIterator(store, key) - i := int64(0) - for ; iterator.Valid(); iterator.Next() { - delegation := types.MustUnmarshalDelegation(k.cdc, iterator.Key(), iterator.Value()) - stop := fn(i, delegation) // XXX is this safe will the fields be able to get written to? + delegatorPrefixKey := GetDelegationsKey(delAddr) + iterator := sdk.KVStorePrefixIterator(store, delegatorPrefixKey) //smallest to largest + for i := int64(0); iterator.Valid(); iterator.Next() { + del := types.MustUnmarshalDelegation(k.cdc, iterator.Key(), iterator.Value()) + stop := fn(i, del) if stop { break } diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index 52dbd21c7..29adec06a 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -5,7 +5,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" types "github.com/cosmos/cosmos-sdk/x/stake/types" - "github.com/tendermint/tendermint/crypto" ) // Slash a validator for an infraction committed at a known height @@ -24,7 +23,7 @@ import ( // not at a height in the future // // nolint: gocyclo -func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight int64, power int64, slashFactor sdk.Dec) { +func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeight int64, power int64, slashFactor sdk.Dec) { logger := ctx.Logger().With("module", "x/stake") if slashFactor.LT(sdk.ZeroDec()) { @@ -36,7 +35,7 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in // ref https://github.com/cosmos/cosmos-sdk/issues/1348 // ref https://github.com/cosmos/cosmos-sdk/issues/1471 - validator, found := k.GetValidatorByPubKey(ctx, pubkey) + validator, found := k.GetValidatorByConsAddr(ctx, consAddr) if !found { // If not found, the validator must have been overslashed and removed - so we don't need to do anything // NOTE: Correctness dependent on invariant that unbonding delegations / redelegations must also have been completely @@ -44,7 +43,7 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in // Log the slash attempt for future reference (maybe we should tag it too) logger.Error(fmt.Sprintf( "WARNING: Ignored attempt to slash a nonexistent validator with address %s, we recommend you investigate immediately", - pubkey.Address())) + consAddr)) return } @@ -125,36 +124,28 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in } // jail a validator -func (k Keeper) Jail(ctx sdk.Context, pubkey crypto.PubKey) { - k.setJailed(ctx, pubkey, true) - validatorAddr, err := sdk.ValAddressFromHex(pubkey.Address().String()) - if err != nil { - panic(err.Error()) - } +func (k Keeper) Jail(ctx sdk.Context, consAddr sdk.ConsAddress) { + k.setJailed(ctx, consAddr, true) logger := ctx.Logger().With("module", "x/stake") - logger.Info(fmt.Sprintf("validator %s jailed", validatorAddr)) + logger.Info(fmt.Sprintf("validator %s jailed", consAddr)) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return } // unjail a validator -func (k Keeper) Unjail(ctx sdk.Context, pubkey crypto.PubKey) { - k.setJailed(ctx, pubkey, false) - validatorAddr, err := sdk.ValAddressFromHex(pubkey.Address().String()) - if err != nil { - panic(err.Error()) - } +func (k Keeper) Unjail(ctx sdk.Context, consAddr sdk.ConsAddress) { + k.setJailed(ctx, consAddr, false) logger := ctx.Logger().With("module", "x/stake") - logger.Info(fmt.Sprintf("validator %s unjailed", validatorAddr)) + logger.Info(fmt.Sprintf("validator %s unjailed", consAddr)) // TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803 return } // set the jailed flag on a validator -func (k Keeper) setJailed(ctx sdk.Context, pubkey crypto.PubKey, isJailed bool) { - validator, found := k.GetValidatorByPubKey(ctx, pubkey) +func (k Keeper) setJailed(ctx sdk.Context, consAddr sdk.ConsAddress, isJailed bool) { + validator, found := k.GetValidatorByConsAddr(ctx, consAddr) if !found { - panic(fmt.Errorf("validator with pubkey %s not found, cannot set jailed to %v", pubkey, isJailed)) + panic(fmt.Errorf("validator with consensus-Address %s not found, cannot set jailed to %v", consAddr, isJailed)) } validator.Jailed = isJailed k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it diff --git a/x/stake/keeper/slash_test.go b/x/stake/keeper/slash_test.go index 65bac2d80..0a6cccac2 100644 --- a/x/stake/keeper/slash_test.go +++ b/x/stake/keeper/slash_test.go @@ -14,6 +14,7 @@ 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) { + // setup ctx, _, keeper := CreateTestInput(t, false, amt) params := keeper.GetParams(ctx) @@ -27,7 +28,7 @@ func setupHelper(t *testing.T, amt int64) (sdk.Context, Keeper, types.Params) { validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(amt)) keeper.SetPool(ctx, pool) validator = keeper.UpdateValidator(ctx, validator) - keeper.SetValidatorByPubKeyIndex(ctx, validator) + keeper.SetValidatorByConsAddr(ctx, validator) } pool = keeper.GetPool(ctx) @@ -42,7 +43,7 @@ func TestRevocation(t *testing.T) { // setup ctx, keeper, _ := setupHelper(t, 10) addr := addrVals[0] - pk := PKs[0] + consAddr := sdk.ConsAddress(PKs[0].Address()) // initial state val, found := keeper.GetValidator(ctx, addr) @@ -50,13 +51,13 @@ func TestRevocation(t *testing.T) { require.False(t, val.GetJailed()) // test jail - keeper.Jail(ctx, pk) + keeper.Jail(ctx, consAddr) val, found = keeper.GetValidator(ctx, addr) require.True(t, found) require.True(t, val.GetJailed()) // test unjail - keeper.Unjail(ctx, pk) + keeper.Unjail(ctx, consAddr) val, found = keeper.GetValidator(ctx, addr) require.True(t, found) require.False(t, val.GetJailed()) @@ -179,24 +180,24 @@ func TestSlashRedelegation(t *testing.T) { // tests Slash at a future height (must panic) func TestSlashAtFutureHeight(t *testing.T) { ctx, keeper, _ := setupHelper(t, 10) - pk := PKs[0] + consAddr := sdk.ConsAddress(PKs[0].Address()) fraction := sdk.NewDecWithPrec(5, 1) - require.Panics(t, func() { keeper.Slash(ctx, pk, 1, 10, fraction) }) + require.Panics(t, func() { keeper.Slash(ctx, consAddr, 1, 10, fraction) }) } // tests Slash at the current height func TestSlashValidatorAtCurrentHeight(t *testing.T) { ctx, keeper, _ := setupHelper(t, 10) - pk := PKs[0] + consAddr := sdk.ConsAddress(PKs[0].Address()) fraction := sdk.NewDecWithPrec(5, 1) oldPool := keeper.GetPool(ctx) - validator, found := keeper.GetValidatorByPubKey(ctx, pk) + validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - keeper.Slash(ctx, pk, ctx.BlockHeight(), 10, fraction) + keeper.Slash(ctx, consAddr, ctx.BlockHeight(), 10, fraction) // read updated state - validator, found = keeper.GetValidatorByPubKey(ctx, pk) + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) newPool := keeper.GetPool(ctx) @@ -209,7 +210,7 @@ func TestSlashValidatorAtCurrentHeight(t *testing.T) { // tests Slash at a previous height with an unbonding delegation func TestSlashWithUnbondingDelegation(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) - pk := PKs[0] + consAddr := sdk.ConsAddress(PKs[0].Address()) fraction := sdk.NewDecWithPrec(5, 1) // set an unbonding delegation @@ -227,9 +228,9 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // slash validator for the first time ctx = ctx.WithBlockHeight(12) oldPool := keeper.GetPool(ctx) - validator, found := keeper.GetValidatorByPubKey(ctx, pk) + validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - keeper.Slash(ctx, pk, 10, 10, fraction) + keeper.Slash(ctx, consAddr, 10, 10, fraction) // read updating unbonding delegation ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) @@ -241,7 +242,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // bonded tokens burned require.Equal(t, int64(3), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) // read updated validator - validator, found = keeper.GetValidatorByPubKey(ctx, pk) + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) // power decreased by 3 - 6 stake originally bonded at the time of infraction // was still bonded at the time of discovery and was slashed by half, 4 stake @@ -251,7 +252,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // slash validator again ctx = ctx.WithBlockHeight(13) - keeper.Slash(ctx, pk, 9, 10, fraction) + keeper.Slash(ctx, consAddr, 9, 10, fraction) ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) // balance decreased again @@ -261,7 +262,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // bonded tokens burned again require.Equal(t, int64(6), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) // read updated validator - validator, found = keeper.GetValidatorByPubKey(ctx, pk) + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) // power decreased by 3 again require.Equal(t, sdk.NewDec(4), validator.GetPower()) @@ -271,7 +272,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // on the unbonding delegation, but it will slash stake bonded since the infraction // this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440 ctx = ctx.WithBlockHeight(13) - keeper.Slash(ctx, pk, 9, 10, fraction) + keeper.Slash(ctx, consAddr, 9, 10, fraction) ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) // balance unchanged @@ -281,7 +282,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // bonded tokens burned again require.Equal(t, int64(9), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) // read updated validator - validator, found = keeper.GetValidatorByPubKey(ctx, pk) + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) // power decreased by 3 again require.Equal(t, sdk.NewDec(1), validator.GetPower()) @@ -291,7 +292,7 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // on the unbonding delegation, but it will slash stake bonded since the infraction // this may not be the desirable behaviour, ref https://github.com/cosmos/cosmos-sdk/issues/1440 ctx = ctx.WithBlockHeight(13) - keeper.Slash(ctx, pk, 9, 10, fraction) + keeper.Slash(ctx, consAddr, 9, 10, fraction) ubd, found = keeper.GetUnbondingDelegation(ctx, addrDels[0], addrVals[0]) require.True(t, found) // balance unchanged @@ -303,14 +304,14 @@ func TestSlashWithUnbondingDelegation(t *testing.T) { // read updated validator // power decreased by 1 again, validator is out of stake // ergo validator should have been removed from the store - _, found = keeper.GetValidatorByPubKey(ctx, pk) + _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.False(t, found) } // tests Slash at a previous height with a redelegation func TestSlashWithRedelegation(t *testing.T) { ctx, keeper, params := setupHelper(t, 10) - pk := PKs[0] + consAddr := sdk.ConsAddress(PKs[0].Address()) fraction := sdk.NewDecWithPrec(5, 1) // set a redelegation @@ -343,9 +344,9 @@ func TestSlashWithRedelegation(t *testing.T) { // slash validator ctx = ctx.WithBlockHeight(12) oldPool := keeper.GetPool(ctx) - validator, found := keeper.GetValidatorByPubKey(ctx, pk) + validator, found := keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - keeper.Slash(ctx, pk, 10, 10, fraction) + keeper.Slash(ctx, consAddr, 10, 10, fraction) // read updating redelegation rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) @@ -357,7 +358,7 @@ func TestSlashWithRedelegation(t *testing.T) { // bonded tokens burned require.Equal(t, int64(5), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) // read updated validator - validator, found = keeper.GetValidatorByPubKey(ctx, pk) + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) // power decreased by 2 - 4 stake originally bonded at the time of infraction // was still bonded at the time of discovery and was slashed by half, 4 stake @@ -367,9 +368,9 @@ func TestSlashWithRedelegation(t *testing.T) { // slash the validator again ctx = ctx.WithBlockHeight(12) - validator, found = keeper.GetValidatorByPubKey(ctx, pk) + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - require.NotPanics(t, func() { keeper.Slash(ctx, pk, 10, 10, sdk.OneDec()) }) + require.NotPanics(t, func() { keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) }) // read updating redelegation rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) @@ -381,16 +382,16 @@ func TestSlashWithRedelegation(t *testing.T) { // seven bonded tokens burned require.Equal(t, int64(12), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) // read updated validator - validator, found = keeper.GetValidatorByPubKey(ctx, pk) + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) // power decreased by 4 require.Equal(t, sdk.NewDec(4), validator.GetPower()) // slash the validator again, by 100% ctx = ctx.WithBlockHeight(12) - validator, found = keeper.GetValidatorByPubKey(ctx, pk) + validator, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.True(t, found) - keeper.Slash(ctx, pk, 10, 10, sdk.OneDec()) + keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) // read updating redelegation rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) @@ -403,16 +404,16 @@ func TestSlashWithRedelegation(t *testing.T) { require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) // read updated validator // validator decreased to zero power, should have been removed from the store - _, found = keeper.GetValidatorByPubKey(ctx, pk) + _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.False(t, found) // slash the validator again, by 100% // no stake remains to be slashed ctx = ctx.WithBlockHeight(12) // validator no longer in the store - _, found = keeper.GetValidatorByPubKey(ctx, pk) + _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.False(t, found) - keeper.Slash(ctx, pk, 10, 10, sdk.OneDec()) + keeper.Slash(ctx, consAddr, 10, 10, sdk.OneDec()) // read updating redelegation rd, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) @@ -425,7 +426,7 @@ func TestSlashWithRedelegation(t *testing.T) { require.Equal(t, int64(16), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) // read updated validator // power still zero, still not in the store - _, found = keeper.GetValidatorByPubKey(ctx, pk) + _, found = keeper.GetValidatorByConsAddr(ctx, consAddr) require.False(t, found) } @@ -472,9 +473,10 @@ func TestSlashBoth(t *testing.T) { // slash validator ctx = ctx.WithBlockHeight(12) oldPool := keeper.GetPool(ctx) - validator, found := keeper.GetValidatorByPubKey(ctx, PKs[0]) + validator, found := keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) require.True(t, found) - keeper.Slash(ctx, PKs[0], 10, 10, fraction) + consAddr0 := sdk.ConsAddress(PKs[0].Address()) + keeper.Slash(ctx, consAddr0, 10, 10, fraction) // read updating redelegation rdA, found = keeper.GetRedelegation(ctx, addrDels[0], addrVals[0], addrVals[1]) @@ -488,7 +490,7 @@ func TestSlashBoth(t *testing.T) { // bonded tokens burned require.Equal(t, int64(3), oldPool.BondedTokens.Sub(newPool.BondedTokens).RoundInt64()) // read updated validator - validator, found = keeper.GetValidatorByPubKey(ctx, PKs[0]) + validator, found = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) require.True(t, found) // power not decreased, all stake was bonded since require.Equal(t, sdk.NewDec(10), validator.GetPower()) diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index de4c4a187..64d2ad021 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -6,7 +6,6 @@ import ( "fmt" abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/stake/types" @@ -58,14 +57,14 @@ func (k Keeper) GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator ty return validator, true } -// get a single validator by pubkey -func (k Keeper) GetValidatorByPubKey(ctx sdk.Context, pubkey crypto.PubKey) (validator types.Validator, found bool) { +// get a single validator by consensus address +func (k Keeper) GetValidatorByConsAddr(ctx sdk.Context, consAddr sdk.ConsAddress) (validator types.Validator, found bool) { store := ctx.KVStore(k.storeKey) - addr := store.Get(GetValidatorByPubKeyIndexKey(pubkey)) - if addr == nil { + opAddr := store.Get(GetValidatorByConsAddrKey(consAddr)) + if opAddr == nil { return validator, false } - return k.GetValidator(ctx, addr) + return k.GetValidator(ctx, opAddr) } // set the main record holding validator details @@ -76,9 +75,10 @@ func (k Keeper) SetValidator(ctx sdk.Context, validator types.Validator) { } // validator index -func (k Keeper) SetValidatorByPubKeyIndex(ctx sdk.Context, validator types.Validator) { +func (k Keeper) SetValidatorByConsAddr(ctx sdk.Context, validator types.Validator) { store := ctx.KVStore(k.storeKey) - store.Set(GetValidatorByPubKeyIndexKey(validator.ConsPubKey), validator.OperatorAddr) + consAddr := sdk.ConsAddress(validator.ConsPubKey.Address()) + store.Set(GetValidatorByConsAddrKey(consAddr), validator.OperatorAddr) } // validator index @@ -622,8 +622,8 @@ func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validat store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr)) // call the unbond hook if present - if k.validatorHooks != nil { - k.validatorHooks.OnValidatorBeginUnbonding(ctx, validator.ConsAddress()) + if k.hooks != nil { + k.hooks.OnValidatorBeginUnbonding(ctx, validator.ConsAddress()) } // return updated validator @@ -657,8 +657,8 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types. tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI) // call the bond hook if present - if k.validatorHooks != nil { - k.validatorHooks.OnValidatorBonded(ctx, validator.ConsAddress()) + if k.hooks != nil { + k.hooks.OnValidatorBonded(ctx, validator.ConsAddress()) } // return updated validator @@ -678,7 +678,7 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) { store := ctx.KVStore(k.storeKey) pool := k.GetPool(ctx) store.Delete(GetValidatorKey(address)) - store.Delete(GetValidatorByPubKeyIndexKey(validator.ConsPubKey)) + store.Delete(GetValidatorByConsAddrKey(sdk.ConsAddress(validator.ConsPubKey.Address()))) store.Delete(GetValidatorsByPowerIndexKey(validator, pool)) // delete from the current and power weighted validator groups if the validator diff --git a/x/stake/keeper/validator_test.go b/x/stake/keeper/validator_test.go index 1a5cc3fee..73a391acc 100644 --- a/x/stake/keeper/validator_test.go +++ b/x/stake/keeper/validator_test.go @@ -262,12 +262,13 @@ func TestSlashToZeroPowerRemoved(t *testing.T) { require.Equal(t, sdk.Unbonded, validator.Status) require.Equal(t, int64(100), validator.Tokens.RoundInt64()) keeper.SetPool(ctx, pool) - keeper.SetValidatorByPubKeyIndex(ctx, validator) + keeper.SetValidatorByConsAddr(ctx, validator) validator = keeper.UpdateValidator(ctx, validator) require.Equal(t, int64(100), validator.Tokens.RoundInt64(), "\nvalidator %v\npool %v", validator, pool) // slash the validator by 100% - keeper.Slash(ctx, PKs[0], 0, 100, sdk.OneDec()) + consAddr0 := sdk.ConsAddress(PKs[0].Address()) + keeper.Slash(ctx, consAddr0, 0, 100, sdk.OneDec()) // validator should have been deleted _, found := keeper.GetValidator(ctx, addrVals[0]) require.False(t, found) @@ -306,10 +307,19 @@ func TestValidatorBasics(t *testing.T) { // set and retrieve a record validators[0] = keeper.UpdateValidator(ctx, validators[0]) + keeper.SetValidatorByConsAddr(ctx, validators[0]) resVal, found := keeper.GetValidator(ctx, addrVals[0]) require.True(t, found) assert.True(ValEq(t, validators[0], resVal)) + // retrieve from consensus + resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.ConsAddress(PKs[0].Address())) + require.True(t, found) + assert.True(ValEq(t, validators[0], resVal)) + resVal, found = keeper.GetValidatorByConsAddr(ctx, sdk.GetConsAddress(PKs[0])) + require.True(t, found) + assert.True(ValEq(t, validators[0], resVal)) + resVals = keeper.GetValidatorsBonded(ctx) require.Equal(t, 1, len(resVals)) assert.True(ValEq(t, validators[0], resVals[0])) diff --git a/x/stake/simulation/invariants.go b/x/stake/simulation/invariants.go index 2ab071704..cdd80a8c4 100644 --- a/x/stake/simulation/invariants.go +++ b/x/stake/simulation/invariants.go @@ -80,7 +80,7 @@ func PositivePowerInvariant(k stake.Keeper) simulation.Invariant { var err error k.IterateValidatorsBonded(ctx, func(_ int64, validator sdk.Validator) bool { if !validator.GetPower().GT(sdk.ZeroDec()) { - err = fmt.Errorf("validator with non-positive power stored. (pubkey %v)", validator.GetPubKey()) + err = fmt.Errorf("validator with non-positive power stored. (pubkey %v)", validator.GetConsPubKey()) return true } return false diff --git a/x/stake/stake.go b/x/stake/stake.go index b421a5d25..7e60b3113 100644 --- a/x/stake/stake.go +++ b/x/stake/stake.go @@ -36,7 +36,7 @@ var ( NewKeeper = keeper.NewKeeper GetValidatorKey = keeper.GetValidatorKey - GetValidatorByPubKeyIndexKey = keeper.GetValidatorByPubKeyIndexKey + GetValidatorByConsAddrKey = keeper.GetValidatorByConsAddrKey GetValidatorsBondedIndexKey = keeper.GetValidatorsBondedIndexKey GetValidatorsByPowerIndexKey = keeper.GetValidatorsByPowerIndexKey GetTendermintUpdatesTKey = keeper.GetTendermintUpdatesTKey @@ -45,7 +45,7 @@ var ( ParamKey = keeper.ParamKey PoolKey = keeper.PoolKey ValidatorsKey = keeper.ValidatorsKey - ValidatorsByPubKeyIndexKey = keeper.ValidatorsByPubKeyIndexKey + ValidatorsByConsAddrKey = keeper.ValidatorsByConsAddrKey ValidatorsBondedIndexKey = keeper.ValidatorsBondedIndexKey ValidatorsByPowerIndexKey = keeper.ValidatorsByPowerIndexKey ValidatorCliffIndexKey = keeper.ValidatorCliffIndexKey diff --git a/x/stake/types/codec.go b/x/stake/types/codec.go index aa4f64f8a..4921cdf8e 100644 --- a/x/stake/types/codec.go +++ b/x/stake/types/codec.go @@ -22,6 +22,5 @@ func init() { cdc := codec.New() RegisterCodec(cdc) codec.RegisterCrypto(cdc) - MsgCdc = cdc - //MsgCdc = cdc.Seal() //TODO use when upgraded to go-amino 0.9.10 + MsgCdc = cdc.Seal() } diff --git a/x/stake/types/delegation.go b/x/stake/types/delegation.go index 38a94c369..57ac70a57 100644 --- a/x/stake/types/delegation.go +++ b/x/stake/types/delegation.go @@ -89,7 +89,7 @@ var _ sdk.Delegation = Delegation{} // nolint - for sdk.Delegation func (d Delegation) GetDelegator() sdk.AccAddress { return d.DelegatorAddr } func (d Delegation) GetValidator() sdk.ValAddress { return d.ValidatorAddr } -func (d Delegation) GetBondShares() sdk.Dec { return d.Shares } +func (d Delegation) GetShares() sdk.Dec { return d.Shares } // HumanReadableString returns a human readable string representation of a // Delegation. An error is returned if the Delegation's delegator or validator diff --git a/x/stake/types/validator.go b/x/stake/types/validator.go index f124ab595..051ffa9e5 100644 --- a/x/stake/types/validator.go +++ b/x/stake/types/validator.go @@ -450,12 +450,14 @@ func (v Validator) IsUnbonded(ctx sdk.Context) bool { var _ sdk.Validator = Validator{} // nolint - for sdk.Validator -func (v Validator) GetJailed() bool { return v.Jailed } -func (v Validator) GetMoniker() string { return v.Description.Moniker } -func (v Validator) GetStatus() sdk.BondStatus { return v.Status } -func (v Validator) GetOperator() sdk.ValAddress { return v.OperatorAddr } -func (v Validator) GetPubKey() crypto.PubKey { return v.ConsPubKey } -func (v Validator) GetPower() sdk.Dec { return v.BondedTokens() } -func (v Validator) GetTokens() sdk.Dec { return v.Tokens } -func (v Validator) GetDelegatorShares() sdk.Dec { return v.DelegatorShares } -func (v Validator) GetBondHeight() int64 { return v.BondHeight } +func (v Validator) GetJailed() bool { return v.Jailed } +func (v Validator) GetMoniker() string { return v.Description.Moniker } +func (v Validator) GetStatus() sdk.BondStatus { return v.Status } +func (v Validator) GetOperator() sdk.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.Dec { return v.BondedTokens() } +func (v Validator) GetTokens() sdk.Dec { return v.Tokens } +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 de30281afa9229afd1e88b5140deee41b22437bd Mon Sep 17 00:00:00 2001 From: Rigel Date: Tue, 25 Sep 2018 00:18:18 -0400 Subject: [PATCH 3/9] Distr-PR-2 Truncate Decimal Functionality (#2379) --- PENDING.md | 1 + types/decimal.go | 26 ++++++++++++++++++++++++++ types/decimal_test.go | 26 ++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/PENDING.md b/PENDING.md index 344e3628d..f0e87eb72 100644 --- a/PENDING.md +++ b/PENDING.md @@ -129,6 +129,7 @@ IMPROVEMENTS * [simulation] Logs get written to file if large, and also get printed on panics \#2285 * [gaiad] \#1992 Add optional flag to `gaiad testnet` to make config directory of daemon (default `gaiad`) and cli (default `gaiacli`) configurable * [x/stake] Add stake `Queriers` for Gaia-lite endpoints. This increases the staking endpoints performance by reusing the staking `keeper` logic for queries. [#2249](https://github.com/cosmos/cosmos-sdk/pull/2149) + * [types/decimal] \#2378 - Added truncate functionality to decimal * Tendermint diff --git a/types/decimal.go b/types/decimal.go index 564d6322c..13a494ba0 100644 --- a/types/decimal.go +++ b/types/decimal.go @@ -325,6 +325,32 @@ func (d Dec) RoundInt() Int { //___________________________________________________________________________________ +// similar to chopPrecisionAndRound, but always rounds down +func chopPrecisionAndTruncate(d *big.Int) *big.Int { + return d.Quo(d, precisionReuse) +} + +func chopPrecisionAndTruncateNonMutative(d *big.Int) *big.Int { + tmp := new(big.Int).Set(d) + return chopPrecisionAndTruncate(tmp) +} + +// TruncateInt64 truncates the decimals from the number and returns an int64 +func (d Dec) TruncateInt64() int64 { + chopped := chopPrecisionAndTruncateNonMutative(d.Int) + if !chopped.IsInt64() { + panic("Int64() out of bound") + } + return chopped.Int64() +} + +// TruncateInt truncates the decimals from the number and returns an Int +func (d Dec) TruncateInt() Int { + return NewIntFromBigInt(chopPrecisionAndTruncateNonMutative(d.Int)) +} + +//___________________________________________________________________________________ + // reuse nil values var ( nilAmino string diff --git a/types/decimal_test.go b/types/decimal_test.go index 779460c25..161215467 100644 --- a/types/decimal_test.go +++ b/types/decimal_test.go @@ -202,6 +202,32 @@ func TestBankerRoundChop(t *testing.T) { } } +func TestTruncate(t *testing.T) { + tests := []struct { + d1 Dec + exp int64 + }{ + {mustNewDecFromStr(t, "0"), 0}, + {mustNewDecFromStr(t, "0.25"), 0}, + {mustNewDecFromStr(t, "0.75"), 0}, + {mustNewDecFromStr(t, "1"), 1}, + {mustNewDecFromStr(t, "1.5"), 1}, + {mustNewDecFromStr(t, "7.5"), 7}, + {mustNewDecFromStr(t, "7.6"), 7}, + {mustNewDecFromStr(t, "7.4"), 7}, + {mustNewDecFromStr(t, "100.1"), 100}, + {mustNewDecFromStr(t, "1000.1"), 1000}, + } + + for tcIndex, tc := range tests { + resNeg := tc.d1.Neg().TruncateInt64() + require.Equal(t, -1*tc.exp, resNeg, "negative tc %d", tcIndex) + + resPos := tc.d1.TruncateInt64() + require.Equal(t, tc.exp, resPos, "positive tc %d", tcIndex) + } +} + func TestToLeftPadded(t *testing.T) { tests := []struct { dec Dec From 611e2873750600347d7d411a433fbdbc2741d96f Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 25 Sep 2018 17:45:04 +0000 Subject: [PATCH 4/9] Merge PR #2401: Remove remnants of gocyclo --- client/keys/add.go | 1 - tools/Makefile | 15 --------------- types/errors.go | 1 - x/auth/ante.go | 1 - x/bank/client/rest/sendtx.go | 1 - x/gov/client/cli/tx.go | 1 - x/gov/client/rest/rest.go | 3 --- x/gov/keeper.go | 1 - x/ibc/client/cli/relay.go | 1 - x/ibc/client/rest/transfer.go | 1 - x/slashing/client/rest/tx.go | 1 - x/slashing/keeper.go | 1 - x/stake/client/rest/query.go | 1 - x/stake/client/rest/tx.go | 1 - x/stake/keeper/slash.go | 2 -- x/stake/keeper/validator.go | 4 ---- x/stake/types/inflation_test.go | 1 - 17 files changed, 37 deletions(-) diff --git a/client/keys/add.go b/client/keys/add.go index c42172819..be2825a55 100644 --- a/client/keys/add.go +++ b/client/keys/add.go @@ -46,7 +46,6 @@ phrase, otherwise, a new key will be generated.`, return cmd } -// nolint: gocyclo // TODO remove the above when addressing #1446 func runAddCmd(cmd *cobra.Command, args []string) error { var kb keys.Keybase diff --git a/tools/Makefile b/tools/Makefile index 87544107c..c24e886ff 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -12,7 +12,6 @@ INEFFASSIGN = github.com/gordonklaus/ineffassign MISSPELL = github.com/client9/misspell/cmd/misspell ERRCHECK = github.com/kisielk/errcheck UNPARAM = mvdan.cc/unparam -GOCYCLO = github.com/alecthomas/gocyclo DEP_CHECK := $(shell command -v dep 2> /dev/null) GOLINT_CHECK := $(shell command -v golint 2> /dev/null) @@ -22,7 +21,6 @@ INEFFASSIGN_CHECK := $(shell command -v ineffassign 2> /dev/null) MISSPELL_CHECK := $(shell command -v misspell 2> /dev/null) ERRCHECK_CHECK := $(shell command -v errcheck 2> /dev/null) UNPARAM_CHECK := $(shell command -v unparam 2> /dev/null) -# GOCYCLO_CHECK := $(shell command -v gocyclo 2> /dev/null) check_tools: ifndef DEP_CHECK @@ -68,11 +66,6 @@ ifndef UNPARAM_CHECK else @echo "Found unparam in path." endif -ifndef GOCYCLO_CHECK - @echo "No gocyclo in path. Install with 'make get_tools'." -else - @echo "Found gocyclo in path." -endif get_tools: ifdef DEP_CHECK @@ -126,12 +119,6 @@ else @echo "Installing unparam" go get -v $(UNPARAM) endif -# ifdef GOCYCLO_CHECK -# @echo "gocyclo is already installed. Run 'make update_tools' to update." -# else -# @echo "Installing gocyclo" -# go get -v $(GOCYCLO) -# endif update_tools: @echo "Updating dep" @@ -153,8 +140,6 @@ update_dev_tools: go get -u -v $(ERRCHECK) @echo "Updating unparam" go get -u -v $(UNPARAM) - # @echo "Updating goyclo" - # go get -u -v $(GOCYCLO) # To avoid unintended conflicts with file names, always add to .PHONY # unless there is a reason not to. diff --git a/types/errors.go b/types/errors.go index 1d4900d3c..46bf748f4 100644 --- a/types/errors.go +++ b/types/errors.go @@ -73,7 +73,6 @@ func unknownCodeMsg(code CodeType) string { } // NOTE: Don't stringer this, we'll put better messages in later. -// nolint: gocyclo func CodeToDefaultMsg(code CodeType) string { switch code { case CodeInternal: diff --git a/x/auth/ante.go b/x/auth/ante.go index 5773e6b1f..f423c9465 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -23,7 +23,6 @@ const ( // NewAnteHandler returns an AnteHandler that checks // and increments sequence numbers, checks signatures & account numbers, // and deducts fees from the first signer. -// nolint: gocyclo func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler { return func( diff --git a/x/bank/client/rest/sendtx.go b/x/bank/client/rest/sendtx.go index 242614d11..2a1e51f0d 100644 --- a/x/bank/client/rest/sendtx.go +++ b/x/bank/client/rest/sendtx.go @@ -43,7 +43,6 @@ func init() { } // SendRequestHandlerFn - http request handler to send coins to a address -// nolint: gocyclo func SendRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // collect data diff --git a/x/gov/client/cli/tx.go b/x/gov/client/cli/tx.go index 77364665a..1bd01b58a 100644 --- a/x/gov/client/cli/tx.go +++ b/x/gov/client/cli/tx.go @@ -285,7 +285,6 @@ func GetCmdQueryProposal(queryRoute string, cdc *codec.Codec) *cobra.Command { return cmd } -// nolint: gocyclo // GetCmdQueryProposals implements a query proposals command. func GetCmdQueryProposals(queryRoute string, cdc *codec.Codec) *cobra.Command { cmd := &cobra.Command{ diff --git a/x/gov/client/rest/rest.go b/x/gov/client/rest/rest.go index 059c0bb1c..3f62691c7 100644 --- a/x/gov/client/rest/rest.go +++ b/x/gov/client/rest/rest.go @@ -334,7 +334,6 @@ func queryVoteHandlerFn(cdc *codec.Codec) http.HandlerFunc { } } -// nolint: gocyclo // todo: Split this functionality into helper functions to remove the above func queryVotesOnProposalHandlerFn(cdc *codec.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { @@ -373,7 +372,6 @@ func queryVotesOnProposalHandlerFn(cdc *codec.Codec) http.HandlerFunc { } } -// nolint: gocyclo // todo: Split this functionality into helper functions to remove the above func queryProposalsWithParameterFn(cdc *codec.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { @@ -439,7 +437,6 @@ func queryProposalsWithParameterFn(cdc *codec.Codec) http.HandlerFunc { } } -// nolint: gocyclo // todo: Split this functionality into helper functions to remove the above func queryTallyOnProposalHandlerFn(cdc *codec.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 2a462cfb4..80e5a205c 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -106,7 +106,6 @@ func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposal Proposal) { store.Delete(KeyProposal(proposal.GetProposalID())) } -// nolint: gocyclo // Get Proposal from store by ProposalID func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, voterAddr sdk.AccAddress, depositerAddr sdk.AccAddress, status ProposalStatus, numLatest int64) []Proposal { diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index a2b626b2c..a57fac705 100644 --- a/x/ibc/client/cli/relay.go +++ b/x/ibc/client/cli/relay.go @@ -91,7 +91,6 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) { } // This is nolinted as someone is in the process of refactoring this to remove the goto -// nolint: gocyclo func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) { cliCtx := context.NewCLIContext() diff --git a/x/ibc/client/rest/transfer.go b/x/ibc/client/rest/transfer.go index e44ea240b..d151f8aa5 100644 --- a/x/ibc/client/rest/transfer.go +++ b/x/ibc/client/rest/transfer.go @@ -35,7 +35,6 @@ type transferBody struct { // TransferRequestHandler - http request handler to transfer coins to a address // on a different chain via IBC -// nolint: gocyclo func TransferRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) diff --git a/x/slashing/client/rest/tx.go b/x/slashing/client/rest/tx.go index 45b1b6471..94537c2eb 100644 --- a/x/slashing/client/rest/tx.go +++ b/x/slashing/client/rest/tx.go @@ -38,7 +38,6 @@ type UnjailBody struct { ValidatorAddr string `json:"validator_addr"` } -// nolint: gocyclo func unjailRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var m UnjailBody diff --git a/x/slashing/keeper.go b/x/slashing/keeper.go index e639182e1..6a5fa3014 100644 --- a/x/slashing/keeper.go +++ b/x/slashing/keeper.go @@ -79,7 +79,6 @@ func (k Keeper) handleDoubleSign(ctx sdk.Context, addr crypto.Address, infractio // handle a validator signature, must be called once per validator per block // TODO refactor to take in a consensus address, additionally should maybe just take in the pubkey too -// nolint gocyclo func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, power int64, signed bool) { logger := ctx.Logger().With("module", "x/slashing") height := ctx.BlockHeight() diff --git a/x/stake/client/rest/query.go b/x/stake/client/rest/query.go index 8d55bed8c..a8fbfecf4 100644 --- a/x/stake/client/rest/query.go +++ b/x/stake/client/rest/query.go @@ -120,7 +120,6 @@ func delegatorHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.Handle } } -// nolint gocyclo // HTTP request handler to query all staking txs (msgs) from a delegator func delegatorTxsHandlerFn(cliCtx context.CLIContext, cdc *codec.Codec) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { diff --git a/x/stake/client/rest/tx.go b/x/stake/client/rest/tx.go index b71684360..324e0c914 100644 --- a/x/stake/client/rest/tx.go +++ b/x/stake/client/rest/tx.go @@ -69,7 +69,6 @@ type EditDelegationsBody struct { CompleteRedelegates []msgCompleteRedelegateInput `json:"complete_redelegates"` } -// nolint: gocyclo // TODO: Split this up into several smaller functions, and remove the above nolint // TODO: use sdk.ValAddress instead of sdk.AccAddress for validators in messages func delegationsRequestHandlerFn(cdc *codec.Codec, kb keys.Keybase, cliCtx context.CLIContext) http.HandlerFunc { diff --git a/x/stake/keeper/slash.go b/x/stake/keeper/slash.go index 29adec06a..486dc3286 100644 --- a/x/stake/keeper/slash.go +++ b/x/stake/keeper/slash.go @@ -21,8 +21,6 @@ import ( // CONTRACT: // Infraction committed at the current height or at a past height, // not at a height in the future -// -// nolint: gocyclo func (k Keeper) Slash(ctx sdk.Context, consAddr sdk.ConsAddress, infractionHeight int64, power int64, slashFactor sdk.Dec) { logger := ctx.Logger().With("module", "x/stake") diff --git a/x/stake/keeper/validator.go b/x/stake/keeper/validator.go index 64d2ad021..9d8fb4362 100644 --- a/x/stake/keeper/validator.go +++ b/x/stake/keeper/validator.go @@ -232,7 +232,6 @@ func (k Keeper) GetValidTendermintUpdates(ctx sdk.Context) (updates []abci.Valid // It may kick out validators if a new validator is entering the bonded validator // group. // -// nolint: gocyclo // TODO: Remove above nolint, function needs to be simplified! func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator { tstore := ctx.TransientStore(k.storeTKey) @@ -422,9 +421,6 @@ func (k Keeper) updateValidatorPower(ctx sdk.Context, oldFound bool, oldValidato // updated in store with the ValidatorsBondedIndexKey. This store is used to // determine if a validator is a validator without needing to iterate over all // validators. -// -// nolint: gocyclo -// TODO: Remove the above golint func (k Keeper) UpdateBondedValidators( ctx sdk.Context, affectedValidator types.Validator) ( updatedVal types.Validator, updated bool) { diff --git a/x/stake/types/inflation_test.go b/x/stake/types/inflation_test.go index fd181af3c..159ecb4c4 100644 --- a/x/stake/types/inflation_test.go +++ b/x/stake/types/inflation_test.go @@ -107,7 +107,6 @@ func updateProvisions(t *testing.T, pool Pool, params Params, hr int) (sdk.Dec, } // Checks that The inflation will correctly increase or decrease after an update to the pool -// nolint: gocyclo func checkInflation(t *testing.T, pool Pool, previousInflation, updatedInflation sdk.Dec, msg string) { inflationChange := updatedInflation.Sub(previousInflation) From a04d5cf26dd7178626c7be3baf987d6a83c3610f Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Tue, 25 Sep 2018 11:18:05 -0700 Subject: [PATCH 5/9] Merge PR #2072: WIP Bank Denom Metadata Spec * Bank Denom Metadata Spec * moved to WIP filename * val comment --- docs/spec/bank/WIP_keeper.md | 25 +++++++++++++++++++++++++ docs/spec/bank/state.md | 2 -- 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 docs/spec/bank/WIP_keeper.md delete mode 100644 docs/spec/bank/state.md diff --git a/docs/spec/bank/WIP_keeper.md b/docs/spec/bank/WIP_keeper.md new file mode 100644 index 000000000..9667fc5dd --- /dev/null +++ b/docs/spec/bank/WIP_keeper.md @@ -0,0 +1,25 @@ +WORK IN PROGRESS +See PR comments here https://github.com/cosmos/cosmos-sdk/pull/2072 + +# Keeper + +## Denom Metadata + +The BankKeeper contains a store that stores the metadata of different token denoms. Denoms are referred to by their name, same as the `denom` field in sdk.Coin. The different attributes of a denom are stored in the denom metadata store under the key `[denom name]:[attribute name]`. The default attributes in the store are explained below. However, this can be extended by the developer or through SoftwareUpgrade proposals. + +### Decimals `int8` + +- `Base Unit` = The common standard for the default "standard" size of a token. Examples: 1 Bitcoin or 1 Ether. +- `Smallest Unit` = The smallest possible denomination of a token. A fraction of the base unit. Examples: 1 satoshi or 1 wei. + +All amounts throughout the SDK are denominated in the smallest unit of a token, so that all amounts can be expressed as integers. However, UIs typically want to display token values in the base unit, so the Decimals metadata field standardizes the number of digits that come after the decimal place in the base unit. + +`1 [Base Unit] = 10^(N) [Smallest Unit]` + +### TotalSupply `sdk.Integer` + +The TotalSupply of a denom is the total amount of a token that exists (known to the chain) across all accounts and modules. It is denominated in the `smallest unit` of a denom. It can be changed by the Keeper functions `MintCoins` and `BurnCoins`. `AddCoins` and `SubtractCoins` are used when adding or subtracting coins for an account, but not removing them from total supply (for example, when moving the coins to the control of the staking module). + +### Aliases `[]string` + +Aliases is an array of strings that are "alternative names" for a token. As an example, while the Ether's denom name might be `ether`, a possible alias could be `ETH`. This field can be useful for UIs and clients. It is intended that this field can be modified by a governance mechanism. diff --git a/docs/spec/bank/state.md b/docs/spec/bank/state.md deleted file mode 100644 index 8c2c47fd4..000000000 --- a/docs/spec/bank/state.md +++ /dev/null @@ -1,2 +0,0 @@ -- SDK related specifications (ie. how multistore, signatures, etc. work). -- Basecoin (SendTx) From 15e848e43c430547081dade39ccbf7583965fe92 Mon Sep 17 00:00:00 2001 From: Sunny Aggarwal Date: Tue, 25 Sep 2018 11:45:20 -0700 Subject: [PATCH 6/9] Merge PR #2395: Remove governance slashing --- PENDING.md | 1 + docs/spec/governance/state.md | 11 ++----- x/gov/endblocker_test.go | 60 ----------------------------------- x/gov/handler.go | 14 +------- x/gov/queryable.go | 2 +- x/gov/tally.go | 17 ++++------ x/gov/tally_test.go | 29 ++++++++--------- 7 files changed, 25 insertions(+), 109 deletions(-) diff --git a/PENDING.md b/PENDING.md index f4f816b26..1b9238796 100644 --- a/PENDING.md +++ b/PENDING.md @@ -36,6 +36,7 @@ BREAKING CHANGES * `cosmosvaladdr` / `cosmosvalpub` => `cosmosvaloper` / `cosmosvaloperpub` * [x/stake] [#1013] TendermintUpdates now uses transient store * [x/gov] [#2195] Governance uses BFT Time + * [x/gov] \#2256 Removed slashing for governance non-voting validators * SDK * [core] [\#1807](https://github.com/cosmos/cosmos-sdk/issues/1807) Switch from use of rational to decimal diff --git a/docs/spec/governance/state.md b/docs/spec/governance/state.md index 0d2927d7a..5b577cec3 100644 --- a/docs/spec/governance/state.md +++ b/docs/spec/governance/state.md @@ -28,7 +28,6 @@ type TallyingProcedure struct { Threshold sdk.Dec // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 GovernancePenalty sdk.Dec // Penalty if validator does not vote - GracePeriod time.Time // If validator entered validator set in this period of blocks before vote ended, governance penalty does not apply } ``` @@ -192,14 +191,10 @@ And the pseudocode for the `ProposalProcessingQueue`: tallyingProcedure = load(GlobalParams, 'TallyingProcedure') - // Slash validators that did not vote, or update tally if they voted + // Update tally if validator voted they voted for each validator in validators - if (validator.bondTime < CurrentTime - tallyingProcedure.GracePeriod) - // only slash if validator entered validator set before grace period - if (!tmpValMap(validator).HasVoted) - slash validator by tallyingProcedure.GovernancePenalty - else - proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus)) + if tmpValMap(validator).HasVoted + proposal.updateTally(tmpValMap(validator).Vote, (validator.TotalShares - tmpValMap(validator).Minus)) diff --git a/x/gov/endblocker_test.go b/x/gov/endblocker_test.go index 4cbd1d758..27eff15f6 100644 --- a/x/gov/endblocker_test.go +++ b/x/gov/endblocker_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/require" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/x/stake" abci "github.com/tendermint/tendermint/abci/types" ) @@ -194,62 +193,3 @@ func TestTickPassedVotingPeriod(t *testing.T) { require.Equal(t, StatusRejected, keeper.GetProposal(ctx, proposalID).GetStatus()) require.True(t, keeper.GetProposal(ctx, proposalID).GetTallyResult().Equals(EmptyTallyResult())) } - -func TestSlashing(t *testing.T) { - mapp, keeper, sk, addrs, _, _ := getMockApp(t, 10) - SortAddresses(addrs) - mapp.BeginBlock(abci.RequestBeginBlock{}) - ctx := mapp.BaseApp.NewContext(false, abci.Header{}) - govHandler := NewHandler(keeper) - stakeHandler := stake.NewHandler(sk) - - valAddrs := make([]sdk.ValAddress, len(addrs[:3])) - for i, addr := range addrs[:3] { - valAddrs[i] = sdk.ValAddress(addr) - } - - createValidators(t, stakeHandler, ctx, valAddrs, []int64{25, 6, 7}) - - initTotalPower := keeper.ds.GetValidatorSet().TotalPower(ctx) - val0Initial := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[0])).GetPower().Quo(initTotalPower) - val1Initial := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[1])).GetPower().Quo(initTotalPower) - val2Initial := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[2])).GetPower().Quo(initTotalPower) - - newProposalMsg := NewMsgSubmitProposal("Test", "test", ProposalTypeText, addrs[0], sdk.Coins{sdk.NewInt64Coin("steak", 15)}) - - res := govHandler(ctx, newProposalMsg) - require.True(t, res.IsOK()) - var proposalID int64 - keeper.cdc.UnmarshalBinaryBare(res.Data, &proposalID) - - newHeader := ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) - ctx = ctx.WithBlockHeader(newHeader) - - require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, proposalID).GetStatus()) - - newVoteMsg := NewMsgVote(addrs[0], proposalID, OptionYes) - res = govHandler(ctx, newVoteMsg) - require.True(t, res.IsOK()) - - EndBlocker(ctx, keeper) - - newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod) - ctx = ctx.WithBlockHeader(newHeader) - - require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, proposalID).GetStatus()) - - EndBlocker(ctx, keeper) - - require.False(t, keeper.GetProposal(ctx, proposalID).GetTallyResult().Equals(EmptyTallyResult())) - - endTotalPower := keeper.ds.GetValidatorSet().TotalPower(ctx) - val0End := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[0])).GetPower().Quo(endTotalPower) - val1End := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[1])).GetPower().Quo(endTotalPower) - val2End := keeper.ds.GetValidatorSet().Validator(ctx, sdk.ValAddress(addrs[2])).GetPower().Quo(endTotalPower) - - require.True(t, val0End.GTE(val0Initial)) - require.True(t, val1End.LT(val1Initial)) - require.True(t, val2End.LT(val2Initial)) -} diff --git a/x/gov/handler.go b/x/gov/handler.go index 00eaf3744..3f8cd312b 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -134,7 +134,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { continue } - passes, tallyResults, nonVotingVals := tally(ctx, keeper, activeProposal) + passes, tallyResults := tally(ctx, keeper, activeProposal) proposalIDBytes := keeper.cdc.MustMarshalBinaryBare(activeProposal.GetProposalID()) var action []byte if passes { @@ -152,18 +152,6 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { logger.Info(fmt.Sprintf("proposal %d (%s) tallied; passed: %v", activeProposal.GetProposalID(), activeProposal.GetTitle(), passes)) - for _, valAddr := range nonVotingVals { - val := keeper.ds.GetValidatorSet().Validator(ctx, valAddr) - keeper.ds.GetValidatorSet().Slash(ctx, - val.GetConsAddr(), - ctx.BlockHeight(), - val.GetPower().RoundInt64(), - keeper.GetTallyingProcedure(ctx).GovernancePenalty) - - logger.Info(fmt.Sprintf("validator %s failed to vote on proposal %d; slashing", - val.GetOperator(), activeProposal.GetProposalID())) - } - resTags.AppendTag(tags.Action, action) resTags.AppendTag(tags.ProposalID, proposalIDBytes) } diff --git a/x/gov/queryable.go b/x/gov/queryable.go index 44380fe91..606f73a9c 100644 --- a/x/gov/queryable.go +++ b/x/gov/queryable.go @@ -220,7 +220,7 @@ func queryTally(ctx sdk.Context, path []string, req abci.RequestQuery, keeper Ke } else if proposal.GetStatus() == StatusPassed || proposal.GetStatus() == StatusRejected { tallyResult = proposal.GetTallyResult() } else { - _, tallyResult, _ = tally(ctx, keeper, proposal) + _, tallyResult = tally(ctx, keeper, proposal) } bz, err2 := codec.MarshalJSONIndent(keeper.cdc, tallyResult) diff --git a/x/gov/tally.go b/x/gov/tally.go index 55fd81da2..c5751258a 100644 --- a/x/gov/tally.go +++ b/x/gov/tally.go @@ -13,7 +13,7 @@ type validatorGovInfo struct { Vote VoteOption // Vote of the validator } -func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tallyResults TallyResult, nonVoting []sdk.ValAddress) { +func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tallyResults TallyResult) { results := make(map[VoteOption]sdk.Dec) results[OptionYes] = sdk.ZeroDec() results[OptionAbstain] = sdk.ZeroDec() @@ -70,12 +70,9 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall keeper.deleteVote(ctx, vote.ProposalID, vote.Voter) } - // iterate over the validators again to tally their voting power and see - // who didn't vote - nonVoting = []sdk.ValAddress{} + // iterate over the validators again to tally their voting power for _, val := range currValidators { if val.Vote == OptionEmpty { - nonVoting = append(nonVoting, val.Address) continue } @@ -98,19 +95,17 @@ func tally(ctx sdk.Context, keeper Keeper, proposal Proposal) (passes bool, tall // If no one votes, proposal fails if totalVotingPower.Sub(results[OptionAbstain]).Equal(sdk.ZeroDec()) { - return false, tallyResults, nonVoting + return false, tallyResults } // If more than 1/3 of voters veto, proposal fails if results[OptionNoWithVeto].Quo(totalVotingPower).GT(tallyingProcedure.Veto) { - return false, tallyResults, nonVoting + return false, tallyResults } // If more than 1/2 of non-abstaining voters vote Yes, proposal passes if results[OptionYes].Quo(totalVotingPower.Sub(results[OptionAbstain])).GT(tallyingProcedure.Threshold) { - return true, tallyResults, nonVoting + return true, tallyResults } // If more than 1/2 of non-abstaining voters vote No, proposal fails - SortValAddresses(nonVoting) - - return false, tallyResults, nonVoting + return false, tallyResults } diff --git a/x/gov/tally_test.go b/x/gov/tally_test.go index d8e3e290c..28fe0cb59 100644 --- a/x/gov/tally_test.go +++ b/x/gov/tally_test.go @@ -51,7 +51,7 @@ func TestTallyNoOneVotes(t *testing.T) { proposal.SetStatus(StatusVotingPeriod) keeper.SetProposal(ctx, proposal) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.True(t, tallyResults.Equals(EmptyTallyResult())) @@ -80,7 +80,7 @@ func TestTallyOnlyValidatorsAllYes(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[1], OptionYes) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.True(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -109,7 +109,7 @@ func TestTallyOnlyValidators51No(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[1], OptionNo) require.Nil(t, err) - passes, _, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) } @@ -139,7 +139,7 @@ func TestTallyOnlyValidators51Yes(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.True(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -170,7 +170,7 @@ func TestTallyOnlyValidatorsVetoed(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNoWithVeto) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -201,7 +201,7 @@ func TestTallyOnlyValidatorsAbstainPasses(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionYes) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.True(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -232,7 +232,7 @@ func TestTallyOnlyValidatorsAbstainFails(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -261,11 +261,9 @@ func TestTallyOnlyValidatorsNonVoter(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo) require.Nil(t, err) - passes, tallyResults, nonVoting := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) - require.Equal(t, 1, len(nonVoting)) - require.Equal(t, sdk.ValAddress(addrs[0]), nonVoting[0]) require.False(t, tallyResults.Equals(EmptyTallyResult())) } @@ -299,7 +297,7 @@ func TestTallyDelgatorOverride(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[3], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -333,10 +331,9 @@ func TestTallyDelgatorInherit(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionYes) require.Nil(t, err) - passes, tallyResults, nonVoting := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.True(t, passes) - require.Equal(t, 0, len(nonVoting)) require.False(t, tallyResults.Equals(EmptyTallyResult())) } @@ -372,7 +369,7 @@ func TestTallyDelgatorMultipleOverride(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[3], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -417,7 +414,7 @@ func TestTallyDelgatorMultipleInherit(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.False(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) @@ -458,7 +455,7 @@ func TestTallyJailedValidator(t *testing.T) { err = keeper.AddVote(ctx, proposalID, addrs[2], OptionNo) require.Nil(t, err) - passes, tallyResults, _ := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) + passes, tallyResults := tally(ctx, keeper, keeper.GetProposal(ctx, proposalID)) require.True(t, passes) require.False(t, tallyResults.Equals(EmptyTallyResult())) From 04487ca5db67b548f9656c49ffefb85d969b40bb Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Tue, 25 Sep 2018 18:47:28 +0000 Subject: [PATCH 7/9] Merge PR #2402: Skip LCD test TestVersion when no env var --- client/lcd/lcd_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index a45d91477..30fa314c5 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -120,6 +120,11 @@ func TestKeys(t *testing.T) { } func TestVersion(t *testing.T) { + // skip the test if the VERSION environment variable has not been set + if version.Version == "" { + t.SkipNow() + } + cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{}) defer cleanup() From 24413a395d64666d63e58b1783e925e399dc48d9 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Tue, 25 Sep 2018 13:48:38 -0700 Subject: [PATCH 8/9] Merge PR #2073: Allow --from to be a name or an address * Allow --from to be a name or an address Closes #1735. * Post-rebase fixes * Updates from code review * Updates from code review * Updates from code review * Fix merge artifacts * Fix merge conflicts * Fix integration tests * Add back GetFromName() check broken during merge * Code review updates * Fix failing test * Updates from code review --- PENDING.md | 2 + client/context/context.go | 106 +++++++++++++++++++++++++----------- client/context/query.go | 22 ++------ client/flags.go | 2 +- client/utils/utils.go | 23 ++++++-- crypto/keys/keybase.go | 39 +++++++++++-- crypto/keys/keybase_test.go | 19 ++++++- crypto/keys/types.go | 17 +++++- types/address_test.go | 2 +- x/ibc/client/cli/relay.go | 13 ++++- 10 files changed, 178 insertions(+), 67 deletions(-) diff --git a/PENDING.md b/PENDING.md index 1b9238796..95dcc7c47 100644 --- a/PENDING.md +++ b/PENDING.md @@ -18,6 +18,8 @@ BREAKING CHANGES utilize a validator's operator address must now use the new Bech32 prefix, `cosmosvaloper`. * [cli] [\#2190](https://github.com/cosmos/cosmos-sdk/issues/2190) `gaiacli init --gen-txs` is now `gaiacli init --with-txs` to reduce confusion + * [cli] \#2073 --from can now be either an address or a key name + * Gaia * Make the transient store key use a distinct store key. [#2013](https://github.com/cosmos/cosmos-sdk/pull/2013) diff --git a/client/context/context.go b/client/context/context.go index 0497761c6..31cfab705 100644 --- a/client/context/context.go +++ b/client/context/context.go @@ -16,6 +16,9 @@ import ( tmliteProxy "github.com/tendermint/tendermint/lite/proxy" rpcclient "github.com/tendermint/tendermint/rpc/client" "os" + "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/client/keys" + cskeys "github.com/cosmos/cosmos-sdk/crypto/keys" ) const ctxAccStoreName = "acc" @@ -23,22 +26,24 @@ const ctxAccStoreName = "acc" // CLIContext implements a typical CLI context created in SDK modules for // transaction handling and queries. type CLIContext struct { - Codec *codec.Codec - AccDecoder auth.AccountDecoder - Client rpcclient.Client - Logger io.Writer - Height int64 - NodeURI string - FromAddressName string - AccountStore string - TrustNode bool - UseLedger bool - Async bool - JSON bool - PrintResponse bool - Certifier tmlite.Certifier - DryRun bool - GenerateOnly bool + Codec *codec.Codec + AccDecoder auth.AccountDecoder + Client rpcclient.Client + Logger io.Writer + Height int64 + NodeURI string + From string + AccountStore string + TrustNode bool + UseLedger bool + Async bool + JSON bool + PrintResponse bool + Certifier tmlite.Certifier + DryRun bool + GenerateOnly bool + fromAddress types.AccAddress + fromName string } // NewCLIContext returns a new initialized CLIContext with parameters from the @@ -51,20 +56,25 @@ func NewCLIContext() CLIContext { rpc = rpcclient.NewHTTP(nodeURI, "/websocket") } + from := viper.GetString(client.FlagFrom) + fromAddress, fromName := fromFields(from) + return CLIContext{ - Client: rpc, - NodeURI: nodeURI, - AccountStore: ctxAccStoreName, - FromAddressName: viper.GetString(client.FlagFrom), - Height: viper.GetInt64(client.FlagHeight), - TrustNode: viper.GetBool(client.FlagTrustNode), - UseLedger: viper.GetBool(client.FlagUseLedger), - Async: viper.GetBool(client.FlagAsync), - JSON: viper.GetBool(client.FlagJson), - PrintResponse: viper.GetBool(client.FlagPrintResponse), - Certifier: createCertifier(), - DryRun: viper.GetBool(client.FlagDryRun), - GenerateOnly: viper.GetBool(client.FlagGenerateOnly), + Client: rpc, + NodeURI: nodeURI, + AccountStore: ctxAccStoreName, + From: viper.GetString(client.FlagFrom), + Height: viper.GetInt64(client.FlagHeight), + TrustNode: viper.GetBool(client.FlagTrustNode), + UseLedger: viper.GetBool(client.FlagUseLedger), + Async: viper.GetBool(client.FlagAsync), + JSON: viper.GetBool(client.FlagJson), + PrintResponse: viper.GetBool(client.FlagPrintResponse), + Certifier: createCertifier(), + DryRun: viper.GetBool(client.FlagDryRun), + GenerateOnly: viper.GetBool(client.FlagGenerateOnly), + fromAddress: fromAddress, + fromName: fromName, } } @@ -108,6 +118,37 @@ func createCertifier() tmlite.Certifier { return certifier } +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 @@ -133,10 +174,9 @@ func (ctx CLIContext) WithAccountStore(accountStore string) CLIContext { return ctx } -// WithFromAddressName returns a copy of the context with an updated from -// address. -func (ctx CLIContext) WithFromAddressName(addrName string) CLIContext { - ctx.FromAddressName = addrName +// WithFrom returns a copy of the context with an updated from address or name. +func (ctx CLIContext) WithFrom(from string) CLIContext { + ctx.From = from return ctx } diff --git a/client/context/query.go b/client/context/query.go index 9eb70fd8c..35402fbfb 100644 --- a/client/context/query.go +++ b/client/context/query.go @@ -4,7 +4,6 @@ import ( "fmt" "io" - "github.com/cosmos/cosmos-sdk/client/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" @@ -84,22 +83,13 @@ func (ctx CLIContext) GetAccount(address []byte) (auth.Account, error) { } // GetFromAddress returns the from address from the context's name. -func (ctx CLIContext) GetFromAddress() (from sdk.AccAddress, err error) { - if ctx.FromAddressName == "" { - return nil, errors.Errorf("must provide a from address name") - } +func (ctx CLIContext) GetFromAddress() (sdk.AccAddress, error) { + return ctx.fromAddress, nil +} - keybase, err := keys.GetKeyBase() - if err != nil { - return nil, err - } - - info, err := keybase.Get(ctx.FromAddressName) - if err != nil { - return nil, errors.Errorf("no key for: %s", ctx.FromAddressName) - } - - return sdk.AccAddress(info.GetPubKey().Address()), nil +// GetFromName returns the key name for the current context. +func (ctx CLIContext) GetFromName() (string, error) { + return ctx.fromName, nil } // GetAccountNumber returns the next account number for the given account diff --git a/client/flags.go b/client/flags.go index 55b29b53c..d0d783d3a 100644 --- a/client/flags.go +++ b/client/flags.go @@ -58,7 +58,7 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command { // PostCommands adds common flags for commands to post tx func PostCommands(cmds ...*cobra.Command) []*cobra.Command { for _, c := range cmds { - c.Flags().String(FlagFrom, "", "Name of private key with which to sign") + c.Flags().String(FlagFrom, "", "Name or address of private key with which to sign") c.Flags().Int64(FlagAccountNumber, 0, "AccountNumber number to sign the tx") c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx") c.Flags().String(FlagMemo, "", "Memo to send along with transaction") diff --git a/client/utils/utils.go b/client/utils/utils.go index 81fd13859..76ee91585 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -8,9 +8,9 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/client/keys" sdk "github.com/cosmos/cosmos-sdk/types" - auth "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/auth" authtxb "github.com/cosmos/cosmos-sdk/x/auth/client/txbuilder" - amino "github.com/tendermint/go-amino" + "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/libs/common" ) @@ -25,8 +25,13 @@ func SendTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) return err } + name, err := cliCtx.GetFromName() + if err != nil { + return err + } + if txBldr.SimulateGas || cliCtx.DryRun { - txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, cliCtx.FromAddressName, msgs) + txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs) if err != nil { return err } @@ -36,13 +41,13 @@ func SendTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msgs []sdk.Msg) return nil } - passphrase, err := keys.GetPassphrase(cliCtx.FromAddressName) + passphrase, err := keys.GetPassphrase(name) if err != nil { return err } // build and sign the transaction - txBytes, err := txBldr.BuildAndSign(cliCtx.FromAddressName, passphrase, msgs) + txBytes, err := txBldr.BuildAndSign(name, passphrase, msgs) if err != nil { return err } @@ -196,7 +201,13 @@ func buildUnsignedStdTx(txBldr authtxb.TxBuilder, cliCtx context.CLIContext, msg return } if txBldr.SimulateGas { - txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, cliCtx.FromAddressName, msgs) + var name string + name, err = cliCtx.GetFromName() + if err != nil { + return + } + + txBldr, err = EnrichCtxWithGas(txBldr, cliCtx, name, msgs) if err != nil { return } diff --git a/crypto/keys/keybase.go b/crypto/keys/keybase.go index 0bf7ad990..c9bfd0812 100644 --- a/crypto/keys/keybase.go +++ b/crypto/keys/keybase.go @@ -14,6 +14,7 @@ import ( "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/types" ) var _ Keybase = dbKeybase{} @@ -41,6 +42,8 @@ const ( French // Italian is currently not supported. Italian + addressSuffix = "address" + infoSuffix = "info" ) var ( @@ -179,11 +182,16 @@ func (kb dbKeybase) List() ([]Info, error) { iter := kb.db.Iterator(nil, nil) defer iter.Close() for ; iter.Valid(); iter.Next() { - info, err := readInfo(iter.Value()) - if err != nil { - return nil, err + key := string(iter.Key()) + + // need to include only keys in storage that have an info suffix + if strings.HasSuffix(key, infoSuffix) { + info, err := readInfo(iter.Value()) + if err != nil { + return nil, err + } + res = append(res, info) } - res = append(res, info) } return res, nil } @@ -197,6 +205,15 @@ func (kb dbKeybase) Get(name string) (Info, error) { return readInfo(bs) } +func (kb dbKeybase) GetByAddress(address types.AccAddress) (Info, error) { + ik := kb.db.Get(addrKey(address)) + if len(ik) == 0 { + return nil, fmt.Errorf("key with address %s not found", address) + } + bs := kb.db.Get(ik) + return readInfo(bs) +} + // Sign signs the msg with the named key. // It returns an error if the key doesn't exist or the decryption fails. func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub tmcrypto.PubKey, err error) { @@ -347,6 +364,7 @@ func (kb dbKeybase) Delete(name, passphrase string) error { if err != nil { return err } + kb.db.DeleteSync(addrKey(linfo.GetAddress())) kb.db.DeleteSync(infoKey(name)) return nil case ledgerInfo: @@ -354,9 +372,11 @@ func (kb dbKeybase) Delete(name, passphrase string) error { if passphrase != "yes" { return fmt.Errorf("enter 'yes' exactly to delete the key - this cannot be undone") } + kb.db.DeleteSync(addrKey(info.GetAddress())) kb.db.DeleteSync(infoKey(name)) return nil } + return nil } @@ -413,9 +433,16 @@ func (kb dbKeybase) writeOfflineKey(pub tmcrypto.PubKey, name string) Info { func (kb dbKeybase) writeInfo(info Info, name string) { // write the info by key - kb.db.SetSync(infoKey(name), writeInfo(info)) + key := infoKey(name) + kb.db.SetSync(key, writeInfo(info)) + // store a pointer to the infokey by address for fast lookup + kb.db.SetSync(addrKey(info.GetAddress()), key) +} + +func addrKey(address types.AccAddress) []byte { + return []byte(fmt.Sprintf("%s.%s", address.String(), addressSuffix)) } func infoKey(name string) []byte { - return []byte(fmt.Sprintf("%s.info", name)) + return []byte(fmt.Sprintf("%s.%s", name, infoSuffix)) } diff --git a/crypto/keys/keybase_test.go b/crypto/keys/keybase_test.go index 652a36bcb..2a602461a 100644 --- a/crypto/keys/keybase_test.go +++ b/crypto/keys/keybase_test.go @@ -11,6 +11,7 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" dbm "github.com/tendermint/tendermint/libs/db" + "github.com/cosmos/cosmos-sdk/types" ) func init() { @@ -20,8 +21,9 @@ func init() { // TestKeyManagement makes sure we can manipulate these keys well func TestKeyManagement(t *testing.T) { // make the storage with reasonable defaults + db := dbm.NewMemDB() cstore := New( - dbm.NewMemDB(), + db, ) algo := Secp256k1 @@ -51,6 +53,12 @@ func TestKeyManagement(t *testing.T) { require.NoError(t, err) _, err = cstore.Get(n3) require.NotNil(t, err) + _, err = cstore.GetByAddress(accAddr(i2)) + require.NoError(t, err) + addr, err := types.AccAddressFromBech32("cosmos1yq8lgssgxlx9smjhes6ryjasmqmd3ts2559g0t") + require.NoError(t, err) + _, err = cstore.GetByAddress(addr) + require.NotNil(t, err) // list shows them in order keyS, err := cstore.List() @@ -92,6 +100,11 @@ func TestKeyManagement(t *testing.T) { keyS, err = cstore.List() require.NoError(t, err) require.Equal(t, 1, len(keyS)) + + // addr cache gets nuked + err = cstore.Delete(n2, p2) + require.NoError(t, err) + require.False(t, db.Has(addrKey(i2.GetAddress()))) } // TestSignVerify does some detailed checks on how we sign and validate @@ -387,3 +400,7 @@ func ExampleNew() { // Carl // signed by Bob } + +func accAddr(info Info) types.AccAddress { + return (types.AccAddress)(info.GetPubKey().Address()) +} \ No newline at end of file diff --git a/crypto/keys/types.go b/crypto/keys/types.go index a68626489..c5e97d5fb 100644 --- a/crypto/keys/types.go +++ b/crypto/keys/types.go @@ -5,14 +5,15 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/cosmos/cosmos-sdk/crypto/keys/hd" + "github.com/cosmos/cosmos-sdk/types" ) // Keybase exposes operations on a generic keystore type Keybase interface { - // CRUD on the keystore List() ([]Info, error) Get(name string) (Info, error) + GetByAddress(address types.AccAddress) (Info, error) Delete(name, passphrase string) error // Sign some bytes, looking up the private key to use @@ -73,6 +74,8 @@ type Info interface { GetName() string // Public key GetPubKey() crypto.PubKey + // Address + GetAddress() types.AccAddress } var _ Info = &localInfo{} @@ -106,6 +109,10 @@ func (i localInfo) GetPubKey() crypto.PubKey { return i.PubKey } +func (i localInfo) GetAddress() types.AccAddress { + return i.PubKey.Address().Bytes() +} + // ledgerInfo is the public information about a Ledger key type ledgerInfo struct { Name string `json:"name"` @@ -133,6 +140,10 @@ func (i ledgerInfo) GetPubKey() crypto.PubKey { return i.PubKey } +func (i ledgerInfo) GetAddress() types.AccAddress { + return i.PubKey.Address().Bytes() +} + // offlineInfo is the public information about an offline key type offlineInfo struct { Name string `json:"name"` @@ -158,6 +169,10 @@ func (i offlineInfo) GetPubKey() crypto.PubKey { return i.PubKey } +func (i offlineInfo) GetAddress() types.AccAddress { + return i.PubKey.Address().Bytes() +} + // encoding info func writeInfo(i Info) []byte { return cdc.MustMarshalBinary(i) diff --git a/types/address_test.go b/types/address_test.go index e2ec36876..6c6c78d6e 100644 --- a/types/address_test.go +++ b/types/address_test.go @@ -10,7 +10,7 @@ import ( "github.com/tendermint/tendermint/crypto/ed25519" "github.com/cosmos/cosmos-sdk/types" -) + ) var invalidStrs = []string{ "", diff --git a/x/ibc/client/cli/relay.go b/x/ibc/client/cli/relay.go index a57fac705..ab7168aca 100644 --- a/x/ibc/client/cli/relay.go +++ b/x/ibc/client/cli/relay.go @@ -94,7 +94,11 @@ func (c relayCommander) runIBCRelay(cmd *cobra.Command, args []string) { func (c relayCommander) loop(fromChainID, fromChainNode, toChainID, toChainNode string) { cliCtx := context.NewCLIContext() - passphrase, err := keys.ReadPassphraseFromStdin(cliCtx.FromAddressName) + name, err := cliCtx.GetFromName() + if err != nil { + panic(err) + } + passphrase, err := keys.ReadPassphraseFromStdin(name) if err != nil { panic(err) } @@ -201,7 +205,12 @@ func (c relayCommander) refine(bz []byte, sequence int64, passphrase string) []b txBldr := authtxb.NewTxBuilderFromCLI().WithSequence(sequence).WithCodec(c.cdc) cliCtx := context.NewCLIContext() - res, err := txBldr.BuildAndSign(cliCtx.FromAddressName, passphrase, []sdk.Msg{msg}) + name, err := cliCtx.GetFromName() + if err != nil { + panic(err) + } + + res, err := txBldr.BuildAndSign(name, passphrase, []sdk.Msg{msg}) if err != nil { panic(err) } From 2fb3493ff57c32d13c3c20c6270d8dc694e03b54 Mon Sep 17 00:00:00 2001 From: Matthew Slipper Date: Tue, 25 Sep 2018 14:36:42 -0700 Subject: [PATCH 9/9] Merge PR #2223: Gaia CLI Config Command * Allow a gaia-cli config file to be created Closes #1613. Closes #1275. Closes #1956. * Add homedir to Gopkg.toml * Updates from code review * Post-rebase fixes * Update test * Code review refactor * Fixes from code review * Fix import * Fix broken test * Fixes from rebase * Fix formatting --- Gopkg.lock | 10 ++ Gopkg.toml | 4 + PENDING.md | 1 + client/config.go | 131 +++++++++++++++++++++++++++ client/flags.go | 9 ++ client/input.go | 18 ++++ client/lcd/root.go | 3 + client/rpc/block.go | 3 + client/rpc/root.go | 4 + client/rpc/status.go | 2 + client/rpc/validators.go | 4 + client/tx/query.go | 8 +- client/tx/search.go | 5 +- cmd/gaia/cli_test/cli_test.go | 43 +++++++++ cmd/gaia/cmd/gaiacli/main.go | 32 ++++++- types/utils.go | 25 ++++- x/auth/client/txbuilder/txbuilder.go | 2 +- x/auth/client/txbuilder/utils.go | 25 ----- 18 files changed, 298 insertions(+), 31 deletions(-) create mode 100644 client/config.go delete mode 100644 x/auth/client/txbuilder/utils.go diff --git a/Gopkg.lock b/Gopkg.lock index d64538d36..00eafa4ad 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -229,6 +229,14 @@ revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" version = "v1.0.1" +[[projects]] + digest = "1:78bbb1ba5b7c3f2ed0ea1eab57bdd3859aec7e177811563edc41198a760b06af" + name = "github.com/mitchellh/go-homedir" + packages = ["."] + pruneopts = "UT" + revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4" + version = "v1.0.0" + [[projects]] digest = "1:645110e089152bd0f4a011a2648fbb0e4df5977be73ca605781157ac297f50c4" name = "github.com/mitchellh/mapstructure" @@ -630,6 +638,8 @@ "github.com/golang/protobuf/proto", "github.com/gorilla/mux", "github.com/mattn/go-isatty", + "github.com/mitchellh/go-homedir", + "github.com/pelletier/go-toml", "github.com/pkg/errors", "github.com/spf13/cobra", "github.com/spf13/pflag", diff --git a/Gopkg.toml b/Gopkg.toml index 2b5928a89..3080e3cd3 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -70,3 +70,7 @@ [prune] go-tests = true unused-packages = true + +[[constraint]] + name = "github.com/mitchellh/go-homedir" + version = "1.0.0" diff --git a/PENDING.md b/PENDING.md index 95dcc7c47..a4abb336b 100644 --- a/PENDING.md +++ b/PENDING.md @@ -81,6 +81,7 @@ FEATURES * [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add --generate-only flag to build an unsigned transaction and write it to STDOUT. * [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) New `sign` command to sign transactions generated with the --generate-only flag. * [\#1954](https://github.com/cosmos/cosmos-sdk/issues/1954) New `broadcast` command to broadcast transactions generated offline and signed with the `sign` command. + * [cli] \#2220 Add `gaiacli config` feature to interactively create CLI config files to reduce the number of required flags * [stake][cli] [\#1672](https://github.com/cosmos/cosmos-sdk/issues/1672) Introduced new commission flags for validator commands `create-validator` and `edit-validator`. diff --git a/client/config.go b/client/config.go new file mode 100644 index 000000000..fcb252375 --- /dev/null +++ b/client/config.go @@ -0,0 +1,131 @@ +package client + +import ( + "github.com/spf13/cobra" + "github.com/mitchellh/go-homedir" + "bufio" + "path" + "os" + "io/ioutil" + "github.com/pelletier/go-toml" + "fmt" + "github.com/cosmos/cosmos-sdk/types" +) + +type cliConfig struct { + Home string `toml:"home"` + ChainID string `toml:"chain_id"` + TrustNode bool `toml:"trust_node"` + Encoding string `toml:"encoding"` + Output string `toml:"output"` + Node string `toml:"node"` + Trace bool `toml:"trace"` +} + +// ConfigCmd returns a CLI command to interactively create a +// Gaia CLI config file. +func ConfigCmd() *cobra.Command { + cfg := &cobra.Command{ + Use: "config", + Short: "Interactively creates a Gaia CLI config file", + RunE: runConfigCmd, + } + + return cfg +} + +func runConfigCmd(cmd *cobra.Command, args [] string) error { + home, err := homedir.Dir() + if err != nil { + return err + } + + stdin := BufferStdin() + gaiaCLIHome, err := handleGaiaCLIHome(home, stdin) + if err != nil { + return err + } + node, err := handleNode(stdin) + if err != nil { + return err + } + trustNode, err := handleTrustNode(stdin) + if err != nil { + return err + } + + encoding := "btc" + output := "text" + var chainID string + chainID, err = types.DefaultChainID() + if err != nil { + fmt.Println("Couldn't populate ChainID, so using an empty one.") + } + + cfg := &cliConfig{ + Home: gaiaCLIHome, + ChainID: chainID, + TrustNode: trustNode, + Encoding: encoding, + Output: output, + Node: node, + Trace: false, + } + + return createGaiaCLIConfig(cfg) +} + +func handleGaiaCLIHome(dir string, stdin *bufio.Reader) (string, error) { + dirName := ".gaiacli" + home, err := GetString(fmt.Sprintf("Where is your gaiacli home directory? (Default: ~/%s)", dirName), stdin) + if err != nil { + return "", err + } + + if home == "" { + home = path.Join(dir, dirName) + } + + return home, nil +} + +func handleNode(stdin *bufio.Reader) (string, error) { + defaultNode := "tcp://localhost:26657" + node, err := GetString(fmt.Sprintf("Where is your validator node running? (Default: %s)", defaultNode), stdin) + if err != nil { + return "", err + } + + if node == "" { + node = defaultNode + } + + return node, nil +} + +func handleTrustNode(stdin *bufio.Reader) (bool, error) { + return GetConfirmation("Do you trust this node?", stdin) +} + +func createGaiaCLIConfig(cfg *cliConfig) error { + cfgPath := path.Join(cfg.Home, "config") + err := os.MkdirAll(cfgPath, os.ModePerm) + if err != nil { + return err + } + + data, err := toml.Marshal(*cfg) + if err != nil { + return err + } + + cfgFile := path.Join(cfgPath, "config.toml") + if info, err := os.Stat(cfgFile); err == nil && !info.IsDir() { + err = os.Rename(cfgFile, path.Join(cfgPath, "config.toml-old")) + if err != nil { + return err + } + } + + return ioutil.WriteFile(cfgFile, data, os.ModePerm) +} diff --git a/client/flags.go b/client/flags.go index d0d783d3a..ff9354937 100644 --- a/client/flags.go +++ b/client/flags.go @@ -5,6 +5,7 @@ import ( "strconv" "github.com/spf13/cobra" + "github.com/spf13/viper" ) // nolint @@ -51,6 +52,10 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command { c.Flags().String(FlagChainID, "", "Chain ID of tendermint node") c.Flags().String(FlagNode, "tcp://localhost:26657", ": to tendermint rpc interface for this chain") c.Flags().Int64(FlagHeight, 0, "block height to query, omit to get most recent provable block") + viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode)) + viper.BindPFlag(FlagUseLedger, c.Flags().Lookup(FlagUseLedger)) + viper.BindPFlag(FlagChainID, c.Flags().Lookup(FlagChainID)) + viper.BindPFlag(FlagNode, c.Flags().Lookup(FlagNode)) } return cmds } @@ -76,6 +81,10 @@ func PostCommands(cmds ...*cobra.Command) []*cobra.Command { // --gas can accept integers and "simulate" c.Flags().Var(&GasFlagVar, "gas", fmt.Sprintf( "gas limit to set per-transaction; set to %q to calculate required gas automatically (default %d)", GasFlagSimulate, DefaultGasLimit)) + viper.BindPFlag(FlagTrustNode, c.Flags().Lookup(FlagTrustNode)) + viper.BindPFlag(FlagUseLedger, c.Flags().Lookup(FlagUseLedger)) + viper.BindPFlag(FlagChainID, c.Flags().Lookup(FlagChainID)) + viper.BindPFlag(FlagNode, c.Flags().Lookup(FlagNode)) } return cmds } diff --git a/client/input.go b/client/input.go index b10f65ce6..a456f1b92 100644 --- a/client/input.go +++ b/client/input.go @@ -100,6 +100,19 @@ func GetConfirmation(prompt string, buf *bufio.Reader) (bool, error) { } } +// GetString simply returns the trimmed string output of a given reader. +func GetString(prompt string, buf *bufio.Reader) (string, error) { + if inputIsTty() && prompt != "" { + PrintPrefixed(prompt) + } + + out, err := readLineFromBuf(buf) + if err != nil { + return "", err + } + return strings.TrimSpace(out), nil +} + // inputIsTty returns true iff we have an interactive prompt, // where we can disable echo and request to repeat the password. // If false, we can optimize for piped input from another command @@ -117,3 +130,8 @@ func readLineFromBuf(buf *bufio.Reader) (string, error) { } return strings.TrimSpace(pass), nil } + +// PrintPrefixed prints a string with > prefixed for use in prompts. +func PrintPrefixed(msg string) { + fmt.Printf("> %s\n", msg) +} diff --git a/client/lcd/root.go b/client/lcd/root.go index 36fbd42d0..84d3e806c 100644 --- a/client/lcd/root.go +++ b/client/lcd/root.go @@ -121,6 +121,9 @@ func ServeCommand(cdc *codec.Codec) *cobra.Command { cmd.Flags().String(client.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(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + viper.BindPFlag(client.FlagTrustNode, cmd.Flags().Lookup(client.FlagTrustNode)) + viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) return cmd } diff --git a/client/rpc/block.go b/client/rpc/block.go index 3824bc3e5..44def2d32 100644 --- a/client/rpc/block.go +++ b/client/rpc/block.go @@ -10,6 +10,7 @@ import ( "github.com/gorilla/mux" "github.com/spf13/cobra" + "github.com/spf13/viper" tmliteProxy "github.com/tendermint/tendermint/lite/proxy" ) @@ -22,7 +23,9 @@ func BlockCommand() *cobra.Command { RunE: printBlock, } 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().String(client.FlagChainID, "", "Chain ID of Tendermint node") return cmd } diff --git a/client/rpc/root.go b/client/rpc/root.go index a8171a293..74c5a69a2 100644 --- a/client/rpc/root.go +++ b/client/rpc/root.go @@ -7,6 +7,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" + "github.com/spf13/viper" ) const ( @@ -40,6 +41,9 @@ func initClientCommand() *cobra.Command { cmd.Flags().String(flagGenesis, "", "Genesis file to verify header validity") cmd.Flags().String(flagCommit, "", "File with trusted and signed header") cmd.Flags().String(flagValHash, "", "Hash of trusted validator set (hex-encoded)") + viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) + return cmd } diff --git a/client/rpc/status.go b/client/rpc/status.go index ea090d32f..6c4270175 100644 --- a/client/rpc/status.go +++ b/client/rpc/status.go @@ -10,6 +10,7 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/context" ctypes "github.com/tendermint/tendermint/rpc/core/types" + "github.com/spf13/viper" ) func statusCommand() *cobra.Command { @@ -20,6 +21,7 @@ func statusCommand() *cobra.Command { } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) return cmd } diff --git a/client/rpc/validators.go b/client/rpc/validators.go index 0583dc054..d8572d544 100644 --- a/client/rpc/validators.go +++ b/client/rpc/validators.go @@ -13,6 +13,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/context" sdk "github.com/cosmos/cosmos-sdk/types" tmtypes "github.com/tendermint/tendermint/types" + "github.com/spf13/viper" ) // TODO these next two functions feel kinda hacky based on their placement @@ -26,8 +27,11 @@ func ValidatorCommand() *cobra.Command { RunE: printValidators, } 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().String(client.FlagChainID, "", "Chain ID of Tendermint node") + viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) return cmd } diff --git a/client/tx/query.go b/client/tx/query.go index 732c11b65..f1d13b608 100644 --- a/client/tx/query.go +++ b/client/tx/query.go @@ -3,8 +3,8 @@ package tx import ( "encoding/hex" "fmt" - "github.com/tendermint/tendermint/libs/common" "net/http" + "github.com/tendermint/tendermint/libs/common" "github.com/gorilla/mux" "github.com/spf13/cobra" @@ -16,6 +16,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/spf13/viper" ) // QueryTxCmd implements the default command for a tx query. @@ -41,8 +42,11 @@ func QueryTxCmd(cdc *codec.Codec) *cobra.Command { } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") + viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) + 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)) return cmd } diff --git a/client/tx/search.go b/client/tx/search.go index 5bc8726db..8d83c44b3 100644 --- a/client/tx/search.go +++ b/client/tx/search.go @@ -62,8 +62,11 @@ $ gaiacli tendermint txs --tag test1,test2 --any } cmd.Flags().StringP(client.FlagNode, "n", "tcp://localhost:26657", "Node to connect to") - cmd.Flags().Bool(client.FlagTrustNode, false, "Trust connected full node (don't verify proofs for responses)") + viper.BindPFlag(client.FlagNode, cmd.Flags().Lookup(client.FlagNode)) cmd.Flags().String(client.FlagChainID, "", "Chain ID of Tendermint node") + viper.BindPFlag(client.FlagChainID, cmd.Flags().Lookup(client.FlagChainID)) + 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().StringSlice(flagTags, nil, "Comma-separated list of tags that must match") cmd.Flags().Bool(flagAny, false, "Return transactions that match ANY tag, rather than ALL") return cmd diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 3c847dc90..8e31c664b 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "testing" + "path" "github.com/stretchr/testify/require" @@ -515,6 +516,48 @@ func TestGaiaCLISendGenerateSignAndBroadcast(t *testing.T) { require.Equal(t, int64(40), fooAcc.GetCoins().AmountOf("steak").Int64()) } +func TestGaiaCLIConfig(t *testing.T) { + require.NoError(t, os.RemoveAll(gaiacliHome)) + require.NoError(t, os.RemoveAll(gaiadHome)) + servAddr, port, err := server.FreeTCPAddr() + require.NoError(t, err) + node := fmt.Sprintf("%s:%s", servAddr, port) + chainID := executeInit(t, fmt.Sprintf("gaiad init -o --name=foo --home=%s --home-client=%s", gaiadHome, gaiacliHome)) + executeWrite(t, fmt.Sprintf("gaiacli --home=%s config", gaiadHome), gaiacliHome, node, "y") + config, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml")) + require.NoError(t, err) + expectedConfig := fmt.Sprintf(`chain_id = "%s" +encoding = "btc" +home = "%s" +node = "%s" +output = "text" +trace = false +trust_node = true +`, chainID, gaiacliHome, node) + require.Equal(t, expectedConfig, string(config)) + // ensure a backup gets created + executeWrite(t, "gaiacli config", gaiacliHome, node, "y", "y") + configBackup, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml-old")) + require.NoError(t, err) + require.Equal(t, expectedConfig, string(configBackup)) + + require.NoError(t, os.RemoveAll(gaiadHome)) + executeWrite(t, "gaiacli config", gaiacliHome, node, "y") + + // ensure it works without an initialized gaiad state + expectedConfig = fmt.Sprintf(`chain_id = "" +encoding = "btc" +home = "%s" +node = "%s" +output = "text" +trace = false +trust_node = true +`, gaiacliHome, node) + config, err = ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml")) + require.NoError(t, err) + require.Equal(t, expectedConfig, string(config)) +} + //___________________________________________________________________________________ // helper methods diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index c61b30056..fee43dc02 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -18,6 +18,9 @@ import ( stakecmd "github.com/cosmos/cosmos-sdk/x/stake/client/cli" "github.com/cosmos/cosmos-sdk/cmd/gaia/app" + "path" + "os" + "github.com/spf13/viper" ) // rootCmd is the entry point for this binary @@ -35,6 +38,7 @@ func main() { // TODO: setup keybase, viper object, etc. to be passed into // the below functions and eliminate global vars, like we do // with the cdc + rootCmd.AddCommand(client.ConfigCmd()) // add standard rpc commands rpc.AddCommands(rootCmd) @@ -131,9 +135,35 @@ func main() { // prepare and add flags executor := cli.PrepareMainCmd(rootCmd, "GA", app.DefaultCLIHome) - err := executor.Execute() + err := initConfig(rootCmd) + if err != nil { + panic(err) + } + + err = executor.Execute() if err != nil { // handle with #870 panic(err) } } + +func initConfig(cmd *cobra.Command) error { + home, err := cmd.PersistentFlags().GetString(cli.HomeFlag) + if err != nil { + return err + } + + cfgFile := path.Join(home, "config", "config.toml") + if _, err := os.Stat(cfgFile); err == nil { + viper.SetConfigFile(cfgFile) + + if err := viper.ReadInConfig(); err != nil { + return err + } + } + + if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil { + return err + } + return viper.BindPFlag(cli.OutputFlag, cmd.PersistentFlags().Lookup(cli.OutputFlag)) +} diff --git a/types/utils.go b/types/utils.go index 2e027676a..95fc779d7 100644 --- a/types/utils.go +++ b/types/utils.go @@ -1,6 +1,10 @@ package types -import "encoding/json" +import ( + "encoding/json" + tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" + tmtypes "github.com/tendermint/tendermint/types" +) // SortedJSON takes any JSON and returns it sorted by keys. Also, all white-spaces // are removed. @@ -29,3 +33,22 @@ func MustSortJSON(toSortJSON []byte) []byte { } return js } + +// DefaultChainID returns the chain ID from the genesis file if present. An +// error is returned if the file cannot be read or parsed. +// +// TODO: This should be removed and the chainID should always be provided by +// the end user. +func DefaultChainID() (string, error) { + cfg, err := tcmd.ParseConfig() + if err != nil { + return "", err + } + + doc, err := tmtypes.GenesisDocFromFile(cfg.GenesisFile()) + if err != nil { + return "", err + } + + return doc.ChainID, nil +} \ No newline at end of file diff --git a/x/auth/client/txbuilder/txbuilder.go b/x/auth/client/txbuilder/txbuilder.go index 030ddb72a..642893b52 100644 --- a/x/auth/client/txbuilder/txbuilder.go +++ b/x/auth/client/txbuilder/txbuilder.go @@ -30,7 +30,7 @@ func NewTxBuilderFromCLI() TxBuilder { // if chain ID is not specified manually, read default chain ID chainID := viper.GetString(client.FlagChainID) if chainID == "" { - defaultChainID, err := defaultChainID() + defaultChainID, err := sdk.DefaultChainID() if err != nil { chainID = defaultChainID } diff --git a/x/auth/client/txbuilder/utils.go b/x/auth/client/txbuilder/utils.go deleted file mode 100644 index 22cc8220b..000000000 --- a/x/auth/client/txbuilder/utils.go +++ /dev/null @@ -1,25 +0,0 @@ -package context - -import ( - tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands" - tmtypes "github.com/tendermint/tendermint/types" -) - -// defaultChainID returns the chain ID from the genesis file if present. An -// error is returned if the file cannot be read or parsed. -// -// TODO: This should be removed and the chainID should always be provided by -// the end user. -func defaultChainID() (string, error) { - cfg, err := tcmd.ParseConfig() - if err != nil { - return "", err - } - - doc, err := tmtypes.GenesisDocFromFile(cfg.GenesisFile()) - if err != nil { - return "", err - } - - return doc.ChainID, nil -}