Merge remote-tracking branch 'origin/develop' into rigel/fee-distribution

This commit is contained in:
rigelrozanski
2018-09-13 02:11:07 -04:00
59 changed files with 887 additions and 502 deletions
+6 -2
View File
@@ -16,7 +16,9 @@ type TxBuilder struct {
Codec *wire.Codec
AccountNumber int64
Sequence int64
Gas int64
Gas int64 // TODO: should this turn into uint64? requires further discussion - see #2173
GasAdjustment float64
SimulateGas bool
ChainID string
Memo string
Fee string
@@ -36,9 +38,11 @@ func NewTxBuilderFromCLI() TxBuilder {
return TxBuilder{
ChainID: chainID,
Gas: viper.GetInt64(client.FlagGas),
AccountNumber: viper.GetInt64(client.FlagAccountNumber),
Gas: client.GasFlagVar.Gas,
GasAdjustment: viper.GetFloat64(client.FlagGasAdjustment),
Sequence: viper.GetInt64(client.FlagSequence),
SimulateGas: client.GasFlagVar.Simulate,
Fee: viper.GetString(client.FlagFee),
Memo: viper.GetString(client.FlagMemo),
}
+1 -1
View File
@@ -19,7 +19,7 @@ func getBenchmarkMockApp() (*mock.App, error) {
bankKeeper := NewBaseKeeper(mapp.AccountMapper)
mapp.Router().AddRoute("bank", NewHandler(bankKeeper))
err := mapp.CompleteSetup([]*sdk.KVStoreKey{})
err := mapp.CompleteSetup()
return mapp, err
}
+18 -12
View File
@@ -32,7 +32,7 @@ type sendBody struct {
ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
Gas string `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
}
@@ -81,31 +81,37 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo
return
}
txBldr := authtxb.TxBuilder{
Codec: cdc,
Gas: m.Gas,
ChainID: m.ChainID,
AccountNumber: m.AccountNumber,
Sequence: m.Sequence,
simulateGas, gas, err := cliclient.ReadGasFlag(m.Gas)
if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, cliclient.DefaultGasAdjustment)
if !ok {
return
}
cliCtx = cliCtx.WithGasAdjustment(adjustment)
txBldr := authtxb.TxBuilder{
Codec: cdc,
Gas: gas,
GasAdjustment: adjustment,
SimulateGas: simulateGas,
ChainID: m.ChainID,
AccountNumber: m.AccountNumber,
Sequence: m.Sequence,
}
if utils.HasDryRunArg(r) || m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
if utils.HasDryRunArg(r) || txBldr.SimulateGas {
newBldr, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if utils.HasDryRunArg(r) {
utils.WriteSimulationResponse(w, txBldr.Gas)
utils.WriteSimulationResponse(w, newBldr.Gas)
return
}
txBldr = newCtx
txBldr = newBldr
}
if utils.HasGenerateOnlyArg(r) {
+3 -3
View File
@@ -21,7 +21,7 @@ func TestBankWithRandomMessages(t *testing.T) {
bankKeeper := bank.NewBaseKeeper(mapper)
mapp.Router().AddRoute("bank", bank.NewHandler(bankKeeper))
err := mapp.CompleteSetup([]*sdk.KVStoreKey{})
err := mapp.CompleteSetup()
if err != nil {
panic(err)
}
@@ -33,8 +33,8 @@ func TestBankWithRandomMessages(t *testing.T) {
simulation.Simulate(
t, mapp.BaseApp, appStateFn,
[]simulation.Operation{
SimulateSingleInputMsgSend(mapper),
[]simulation.WeightedOperation{
{1, SimulateSingleInputMsgSend(mapper)},
},
[]simulation.RandSetup{},
[]simulation.Invariant{
+18 -13
View File
@@ -20,7 +20,7 @@ type baseReq struct {
ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
Gas string `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
}
@@ -69,32 +69,37 @@ func (req baseReq) baseReqValidate(w http.ResponseWriter) bool {
// TODO: Build this function out into a more generic base-request
// (probably should live in client/lcd).
func signAndBuild(w http.ResponseWriter, r *http.Request, cliCtx context.CLIContext, baseReq baseReq, msg sdk.Msg, cdc *wire.Codec) {
var err error
txBldr := authtxb.TxBuilder{
Codec: cdc,
AccountNumber: baseReq.AccountNumber,
Sequence: baseReq.Sequence,
ChainID: baseReq.ChainID,
Gas: baseReq.Gas,
simulateGas, gas, err := client.ReadGasFlag(baseReq.Gas)
if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, baseReq.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
cliCtx = cliCtx.WithGasAdjustment(adjustment)
txBldr := authtxb.TxBuilder{
Codec: cdc,
Gas: gas,
GasAdjustment: adjustment,
SimulateGas: simulateGas,
ChainID: baseReq.ChainID,
AccountNumber: baseReq.AccountNumber,
Sequence: baseReq.Sequence,
}
if utils.HasDryRunArg(r) || baseReq.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, []sdk.Msg{msg})
if utils.HasDryRunArg(r) || txBldr.SimulateGas {
newBldr, err := utils.EnrichCtxWithGas(txBldr, cliCtx, baseReq.Name, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if utils.HasDryRunArg(r) {
utils.WriteSimulationResponse(w, txBldr.Gas)
utils.WriteSimulationResponse(w, newBldr.Gas)
return
}
txBldr = newCtx
txBldr = newBldr
}
if utils.HasGenerateOnlyArg(r) {
+10 -4
View File
@@ -114,8 +114,14 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) {
resTags.AppendTag(tags.Action, tags.ActionProposalDropped)
resTags.AppendTag(tags.ProposalID, proposalIDBytes)
logger.Info(fmt.Sprintf("Proposal %d - \"%s\" - didn't mean minimum deposit (had only %s), deleted",
inactiveProposal.GetProposalID(), inactiveProposal.GetTitle(), inactiveProposal.GetTotalDeposit()))
logger.Info(
fmt.Sprintf("proposal %d (%s) didn't meet minimum deposit of %v steak (had only %v steak); deleted",
inactiveProposal.GetProposalID(),
inactiveProposal.GetTitle(),
keeper.GetDepositProcedure(ctx).MinDeposit.AmountOf("steak"),
inactiveProposal.GetTotalDeposit().AmountOf("steak"),
),
)
}
// Check if earliest Active Proposal ended voting period yet
@@ -143,7 +149,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) {
activeProposal.SetTallyResult(tallyResults)
keeper.SetProposal(ctx, activeProposal)
logger.Info(fmt.Sprintf("Proposal %d - \"%s\" - tallied, passed: %v",
logger.Info(fmt.Sprintf("proposal %d (%s) tallied; passed: %v",
activeProposal.GetProposalID(), activeProposal.GetTitle(), passes))
for _, valAddr := range nonVotingVals {
@@ -154,7 +160,7 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) {
val.GetPower().RoundInt64(),
keeper.GetTallyingProcedure(ctx).GovernancePenalty)
logger.Info(fmt.Sprintf("Validator %s failed to vote on proposal %d, slashing",
logger.Info(fmt.Sprintf("validator %s failed to vote on proposal %d; slashing",
val.GetOperator(), activeProposal.GetProposalID()))
}
+23 -12
View File
@@ -8,22 +8,33 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
)
// query endpoints supported by the governance Querier
const (
QueryProposals = "proposals"
QueryProposal = "proposal"
QueryDeposits = "deposits"
QueryDeposit = "deposit"
QueryVotes = "votes"
QueryVote = "vote"
QueryTally = "tally"
)
func NewQuerier(keeper Keeper) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
switch path[0] {
case "proposal":
return queryProposal(ctx, path[1:], req, keeper)
case "deposit":
return queryDeposit(ctx, path[1:], req, keeper)
case "vote":
return queryVote(ctx, path[1:], req, keeper)
case "deposits":
return queryDeposits(ctx, path[1:], req, keeper)
case "votes":
return queryVotes(ctx, path[1:], req, keeper)
case "proposals":
case QueryProposals:
return queryProposals(ctx, path[1:], req, keeper)
case "tally":
case QueryProposal:
return queryProposal(ctx, path[1:], req, keeper)
case QueryDeposits:
return queryDeposits(ctx, path[1:], req, keeper)
case QueryDeposit:
return queryDeposit(ctx, path[1:], req, keeper)
case QueryVotes:
return queryVotes(ctx, path[1:], req, keeper)
case QueryVote:
return queryVote(ctx, path[1:], req, keeper)
case QueryTally:
return queryTally(ctx, path[1:], req, keeper)
default:
return nil, sdk.ErrUnknownRequest("unknown gov query endpoint")
+6
View File
@@ -156,13 +156,16 @@ func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation {
return operationSimulateMsgVote(k, sk, nil, -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)
}
var ok bool
if proposalID < 0 {
proposalID, ok = randomProposalID(r, k, ctx)
if !ok {
@@ -171,15 +174,18 @@ func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey,
}
addr := sdk.AccAddress(key.PubKey().Address())
option := randomVotingOption(r)
msg := gov.NewMsgVote(addr, proposalID, option)
if msg.ValidateBasic() != nil {
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
}
ctx, write := ctx.CacheContext()
result := gov.NewHandler(k)(ctx, msg)
if result.IsOK() {
write()
}
event(fmt.Sprintf("gov/MsgVote/%v", result.IsOK()))
action = fmt.Sprintf("TestMsgVote: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
return action, nil, nil
+10 -9
View File
@@ -26,7 +26,8 @@ func TestGovWithRandomMessages(t *testing.T) {
mapper := mapp.AccountMapper
bankKeeper := bank.NewBaseKeeper(mapper)
stakeKey := sdk.NewKVStoreKey("stake")
stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, bankKeeper, stake.DefaultCodespace)
stakeTKey := sdk.NewTransientStoreKey("transient_stake")
stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, stakeTKey, bankKeeper, stake.DefaultCodespace)
paramKey := sdk.NewKVStoreKey("params")
paramKeeper := params.NewKeeper(mapp.Cdc, paramKey)
govKey := sdk.NewKVStoreKey("gov")
@@ -37,7 +38,7 @@ func TestGovWithRandomMessages(t *testing.T) {
return abci.ResponseEndBlock{}
})
err := mapp.CompleteSetup([]*sdk.KVStoreKey{stakeKey, paramKey, govKey})
err := mapp.CompleteSetup(stakeKey, stakeTKey, paramKey, govKey)
if err != nil {
panic(err)
}
@@ -56,10 +57,10 @@ func TestGovWithRandomMessages(t *testing.T) {
// Test with unscheduled votes
simulation.Simulate(
t, mapp.BaseApp, appStateFn,
[]simulation.Operation{
SimulateMsgSubmitProposal(govKeeper, stakeKeeper),
SimulateMsgDeposit(govKeeper, stakeKeeper),
SimulateMsgVote(govKeeper, stakeKeeper),
[]simulation.WeightedOperation{
{2, SimulateMsgSubmitProposal(govKeeper, stakeKeeper)},
{3, SimulateMsgDeposit(govKeeper, stakeKeeper)},
{20, SimulateMsgVote(govKeeper, stakeKeeper)},
}, []simulation.RandSetup{
setup,
}, []simulation.Invariant{
@@ -71,9 +72,9 @@ func TestGovWithRandomMessages(t *testing.T) {
// Test with scheduled votes
simulation.Simulate(
t, mapp.BaseApp, appStateFn,
[]simulation.Operation{
SimulateSubmittingVotingAndSlashingForProposal(govKeeper, stakeKeeper),
SimulateMsgDeposit(govKeeper, stakeKeeper),
[]simulation.WeightedOperation{
{10, SimulateSubmittingVotingAndSlashingForProposal(govKeeper, stakeKeeper)},
{5, SimulateMsgDeposit(govKeeper, stakeKeeper)},
}, []simulation.RandSetup{
setup,
}, []simulation.Invariant{
+3 -2
View File
@@ -28,18 +28,19 @@ func getMockApp(t *testing.T, numGenAccs int) (*mock.App, Keeper, stake.Keeper,
keyGlobalParams := sdk.NewKVStoreKey("params")
keyStake := sdk.NewKVStoreKey("stake")
tkeyStake := sdk.NewTransientStoreKey("transient_stake")
keyGov := sdk.NewKVStoreKey("gov")
pk := params.NewKeeper(mapp.Cdc, keyGlobalParams)
ck := bank.NewBaseKeeper(mapp.AccountMapper)
sk := stake.NewKeeper(mapp.Cdc, keyStake, ck, mapp.RegisterCodespace(stake.DefaultCodespace))
sk := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, ck, mapp.RegisterCodespace(stake.DefaultCodespace))
keeper := NewKeeper(mapp.Cdc, keyGov, pk.Setter(), ck, sk, DefaultCodespace)
mapp.Router().AddRoute("gov", NewHandler(keeper))
mapp.SetEndBlocker(getEndBlocker(keeper))
mapp.SetInitChainer(getInitChainer(mapp, keeper, sk))
require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyStake, keyGov, keyGlobalParams}))
require.NoError(t, mapp.CompleteSetup(keyStake, keyGov, keyGlobalParams, tkeyStake))
genAccs, addrs, pubKeys, privKeys := mock.CreateGenAccounts(numGenAccs, sdk.Coins{sdk.NewInt64Coin("steak", 42)})
+1 -1
View File
@@ -24,7 +24,7 @@ func getMockApp(t *testing.T) *mock.App {
bankKeeper := bank.NewBaseKeeper(mapp.AccountMapper)
mapp.Router().AddRoute("ibc", NewHandler(ibcMapper, bankKeeper))
require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyIBC}))
require.NoError(t, mapp.CompleteSetup(keyIBC))
return mapp
}
+15 -10
View File
@@ -29,7 +29,7 @@ type transferBody struct {
SrcChainID string `json:"src_chain_id"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
Gas string `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
}
@@ -71,21 +71,26 @@ func TransferRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.C
packet := ibc.NewIBCPacket(sdk.AccAddress(info.GetPubKey().Address()), to, m.Amount, m.SrcChainID, destChainID)
msg := ibc.IBCTransferMsg{packet}
txBldr := authtxb.TxBuilder{
Codec: cdc,
ChainID: m.SrcChainID,
AccountNumber: m.AccountNumber,
Sequence: m.Sequence,
Gas: m.Gas,
simulateGas, gas, err := client.ReadGasFlag(m.Gas)
if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
cliCtx = cliCtx.WithGasAdjustment(adjustment)
txBldr := authtxb.TxBuilder{
Codec: cdc,
Gas: gas,
GasAdjustment: adjustment,
SimulateGas: simulateGas,
ChainID: m.SrcChainID,
AccountNumber: m.AccountNumber,
Sequence: m.Sequence,
}
if utils.HasDryRunArg(r) || m.Gas == 0 {
if utils.HasDryRunArg(r) || txBldr.SimulateGas {
newCtx, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
+13 -2
View File
@@ -1,6 +1,7 @@
package mock
import (
"fmt"
"math/rand"
"os"
@@ -75,11 +76,21 @@ func NewApp() *App {
// CompleteSetup completes the application setup after the routes have been
// registered.
func (app *App) CompleteSetup(newKeys []*sdk.KVStoreKey) error {
func (app *App) CompleteSetup(newKeys ...sdk.StoreKey) error {
newKeys = append(newKeys, app.KeyMain)
newKeys = append(newKeys, app.KeyAccount)
app.MountStoresIAVL(newKeys...)
for _, key := range newKeys {
switch key.(type) {
case *sdk.KVStoreKey:
app.MountStore(key, sdk.StoreTypeIAVL)
case *sdk.TransientStoreKey:
app.MountStore(key, sdk.StoreTypeTransient)
default:
return fmt.Errorf("unsupported StoreKey: %+v", key)
}
}
err := app.LoadLatestVersion(app.KeyMain)
return err
+1 -1
View File
@@ -41,7 +41,7 @@ func getMockApp(t *testing.T) *App {
mApp := NewApp()
mApp.Router().AddRoute(msgType, func(ctx sdk.Context, msg sdk.Msg) (res sdk.Result) { return })
require.NoError(t, mApp.CompleteSetup([]*sdk.KVStoreKey{}))
require.NoError(t, mApp.CompleteSetup())
return mApp
}
+64 -32
View File
@@ -7,6 +7,7 @@ import (
"math/rand"
"os"
"os/signal"
"runtime/debug"
"sort"
"strings"
"syscall"
@@ -23,12 +24,13 @@ import (
)
// 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, ops []Operation, setups []RandSetup,
invariants []Invariant, numBlocks int, blockSize int, commit bool,
) {
func Simulate(t *testing.T, app *baseapp.BaseApp,
appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage,
ops []WeightedOperation, setups []RandSetup,
invariants []Invariant, numBlocks int, blockSize int, commit bool) error {
time := time.Now().UnixNano()
SimulateFromSeed(t, app, appStateFn, time, ops, setups, invariants, numBlocks, blockSize, commit)
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,
@@ -53,10 +55,13 @@ 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, seed int64, ops []Operation, setups []RandSetup,
invariants []Invariant, numBlocks int, blockSize int, commit bool,
) {
func SimulateFromSeed(tb testing.TB, app *baseapp.BaseApp,
appStateFn func(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json.RawMessage,
seed int64, ops []WeightedOperation, setups []RandSetup, invariants []Invariant,
numBlocks int, blockSize int, commit bool) (simError error) {
// in case we have to end early, don't os.Exit so that we can run cleanup code.
stopEarly := false
testingMode, t, b := getTestingMode(tb)
fmt.Printf("Starting SimulateFromSeed with randomness created with seed %d\n", int(seed))
r := rand.New(rand.NewSource(seed))
@@ -79,12 +84,12 @@ func SimulateFromSeed(
// Setup code to catch SIGTERM's
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
go func() {
<-c
fmt.Printf("Exiting early due to SIGTERM, on block %d, operation %d\n", header.Height, opCount)
DisplayEvents(events)
os.Exit(128 + int(syscall.SIGTERM))
receivedSignal := <-c
fmt.Printf("Exiting early due to %s, on block %d, operation %d\n", receivedSignal, header.Height, opCount)
simError = fmt.Errorf("Exited due to %s", receivedSignal)
stopEarly = true
}()
var pastTimes []time.Time
@@ -95,15 +100,27 @@ func SimulateFromSeed(
operationQueue := make(map[int][]Operation)
var blockLogBuilders []*strings.Builder
if !testingMode {
b.ResetTimer()
} else {
if testingMode {
blockLogBuilders = make([]*strings.Builder, numBlocks)
}
displayLogs := logPrinter(testingMode, blockLogBuilders)
blockSimulator := createBlockSimulator(testingMode, tb, t, event, invariants, ops, operationQueue, numBlocks, displayLogs)
if !testingMode {
b.ResetTimer()
} else {
// Recover logs in case of panic
defer func() {
if r := recover(); r != nil {
fmt.Println("Panic with err\n", r)
stackTrace := string(debug.Stack())
fmt.Println(stackTrace)
displayLogs()
simError = fmt.Errorf("Simulation halted due to panic on block %d", header.Height)
}
}()
}
for i := 0; i < numBlocks; i++ {
for i := 0; i < numBlocks && !stopEarly; i++ {
// Log the header time for future lookup
pastTimes = append(pastTimes, header.Time)
pastSigningValidators = append(pastSigningValidators, request.LastCommitInfo.Validators)
@@ -122,10 +139,9 @@ func SimulateFromSeed(
// Run queued operations. Ignores blocksize if blocksize is too small
numQueuedOpsRan := runQueuedOperations(operationQueue, int(header.Height), tb, r, app, ctx, keys, logWriter, displayLogs, event)
opCount += numQueuedOpsRan
thisBlockSize -= numQueuedOpsRan
operations := blockSimulator(thisBlockSize, r, app, ctx, keys, header, logWriter)
opCount += operations
opCount += operations + numQueuedOpsRan
res := app.EndBlock(abci.RequestEndBlock{})
header.Height++
@@ -146,19 +162,38 @@ func SimulateFromSeed(
// Update the validator set
validators = updateValidators(tb, r, validators, res.ValidatorUpdates, event)
}
if stopEarly {
DisplayEvents(events)
return
}
fmt.Printf("\nSimulation complete. Final height (blocks): %d, final time (seconds), : %v, operations ran %d\n", header.Height, header.Time, opCount)
DisplayEvents(events)
return nil
}
// 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 []Operation, operationQueue map[int][]Operation, totalNumBlocks int, displayLogs func()) func(
func createBlockSimulator(testingMode bool, tb testing.TB, t *testing.T, event func(string), invariants []Invariant, ops []WeightedOperation, operationQueue map[int][]Operation, 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) {
totalOpWeight := 0
for i := 0; i < len(ops); i++ {
totalOpWeight += ops[i].Weight
}
selectOp := func(r *rand.Rand) Operation {
x := r.Intn(totalOpWeight)
for i := 0; i < len(ops); i++ {
if x <= ops[i].Weight {
return ops[i].Op
}
x -= ops[i].Weight
}
// shouldn't happen
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) {
for j := 0; j < blocksize; j++ {
logUpdate, futureOps, err := ops[r.Intn(len(ops))](r, app, ctx, keys, event)
logUpdate, futureOps, err := selectOp(r)(r, app, ctx, keys, event)
if err != nil {
displayLogs()
tb.Fatalf("error on operation %d within block %d, %v", header.Height, opCount, err)
@@ -324,18 +359,14 @@ func RandomRequestBeginBlock(r *rand.Rand, validators map[string]mockValidator,
// updateValidators mimicks Tendermint's update logic
// nolint: unparam
func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValidator, updates []abci.Validator, event func(string)) map[string]mockValidator {
for _, update := range updates {
switch {
case update.Power == 0:
// // TEMPORARY DEBUG CODE TO PROVE THAT THE OLD METHOD WAS BROKEN
// // (i.e. didn't catch in the event of problem)
// if val, ok := tb.(*testing.T); ok {
// require.NotNil(val, current[string(update.PubKey.Data)])
// }
// // CORRECT CHECK
// if _, ok := current[string(update.PubKey.Data)]; !ok {
// tb.Fatalf("tried to delete a nonexistent validator")
// }
if _, ok := current[string(update.PubKey.Data)]; !ok {
tb.Fatalf("tried to delete a nonexistent validator")
}
event("endblock/validatorupdates/kicked")
delete(current, string(update.PubKey.Data))
default:
@@ -350,5 +381,6 @@ func updateValidators(tb testing.TB, r *rand.Rand, current map[string]mockValida
}
}
}
return current
}
+7
View File
@@ -47,6 +47,13 @@ type (
BlockHeight int
Op Operation
}
// WeightedOperation is an operation with associated weight.
// This is used to bias the selection operation within the simulator.
WeightedOperation struct {
Weight int
Op Operation
}
)
// PeriodicInvariant returns an Invariant function closure that asserts
+26 -3
View File
@@ -3,9 +3,11 @@ package simulation
import (
"fmt"
"math/rand"
"os"
"sort"
"strings"
"testing"
"time"
"github.com/tendermint/tendermint/crypto"
@@ -94,13 +96,34 @@ func assertAllInvariants(t *testing.T, app *baseapp.BaseApp, invariants []Invari
func logPrinter(testingmode bool, logs []*strings.Builder) func() {
if testingmode {
return func() {
numLoggers := 0
for i := 0; i < len(logs); i++ {
// We're passed the last created block
if logs[i] == nil {
return
numLoggers = i - 1
break
}
}
var f *os.File
if numLoggers > 10 {
fileName := fmt.Sprintf("simulation_log_%s.txt", time.Now().Format("2006-01-02 15:04:05"))
fmt.Printf("Too many logs to display, instead writing to %s\n", fileName)
f, _ = os.Create(fileName)
}
for i := 0; i < numLoggers; i++ {
if f != nil {
_, err := f.WriteString(fmt.Sprintf("Begin block %d\n", i))
if err != nil {
panic("Failed to write logs to file")
}
_, err = f.WriteString((*logs[i]).String())
if err != nil {
panic("Failed to write logs to file")
}
} else {
fmt.Printf("Begin block %d\n", i)
fmt.Println((*logs[i]).String())
}
fmt.Printf("Begin block %d\n", i)
fmt.Println((*logs[i]).String())
}
}
}
+3 -2
View File
@@ -26,11 +26,12 @@ func getMockApp(t *testing.T) (*mock.App, stake.Keeper, Keeper) {
RegisterWire(mapp.Cdc)
keyStake := sdk.NewKVStoreKey("stake")
tkeyStake := sdk.NewTransientStoreKey("transient_stake")
keySlashing := sdk.NewKVStoreKey("slashing")
keyParams := sdk.NewKVStoreKey("params")
bankKeeper := bank.NewBaseKeeper(mapp.AccountMapper)
paramsKeeper := params.NewKeeper(mapp.Cdc, keyParams)
stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, bankKeeper, mapp.RegisterCodespace(stake.DefaultCodespace))
stakeKeeper := stake.NewKeeper(mapp.Cdc, keyStake, tkeyStake, bankKeeper, mapp.RegisterCodespace(stake.DefaultCodespace))
keeper := NewKeeper(mapp.Cdc, keySlashing, stakeKeeper, paramsKeeper.Getter(), mapp.RegisterCodespace(DefaultCodespace))
mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper))
@@ -38,7 +39,7 @@ func getMockApp(t *testing.T) (*mock.App, stake.Keeper, Keeper) {
mapp.SetEndBlocker(getEndBlocker(stakeKeeper))
mapp.SetInitChainer(getInitChainer(mapp, stakeKeeper))
require.NoError(t, mapp.CompleteSetup([]*sdk.KVStoreKey{keyStake, keySlashing, keyParams}))
require.NoError(t, mapp.CompleteSetup(keyStake, keySlashing, keyParams, tkeyStake))
return mapp, stakeKeeper, keeper
}
+5 -7
View File
@@ -70,22 +70,20 @@ func unjailRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLI
return
}
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
txBldr := authtxb.TxBuilder{
Codec: cdc,
ChainID: m.ChainID,
AccountNumber: m.AccountNumber,
Sequence: m.Sequence,
Gas: m.Gas,
GasAdjustment: adjustment,
}
msg := slashing.NewMsgUnjail(valAddr)
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
cliCtx = cliCtx.WithGasAdjustment(adjustment)
if utils.HasDryRunArg(r) || m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
if err != nil {
+3 -1
View File
@@ -52,11 +52,13 @@ func createTestCodec() *wire.Codec {
func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, params.Setter, Keeper) {
keyAcc := sdk.NewKVStoreKey("acc")
keyStake := sdk.NewKVStoreKey("stake")
tkeyStake := sdk.NewTransientStoreKey("transient_stake")
keySlashing := sdk.NewKVStoreKey("slashing")
keyParams := sdk.NewKVStoreKey("params")
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(tkeyStake, sdk.StoreTypeTransient, nil)
ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keySlashing, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyParams, sdk.StoreTypeIAVL, db)
@@ -67,7 +69,7 @@ func createTestInput(t *testing.T) (sdk.Context, bank.Keeper, stake.Keeper, para
accountMapper := auth.NewAccountMapper(cdc, keyAcc, auth.ProtoBaseAccount)
ck := bank.NewBaseKeeper(accountMapper)
params := params.NewKeeper(cdc, keyParams)
sk := stake.NewKeeper(cdc, keyStake, ck, stake.DefaultCodespace)
sk := stake.NewKeeper(cdc, keyStake, tkeyStake, ck, stake.DefaultCodespace)
genesis := stake.DefaultGenesisState()
genesis.Pool.LooseTokens = sdk.NewDec(initCoins.MulRaw(int64(len(addrs))).Int64())
+3 -2
View File
@@ -34,14 +34,15 @@ func getMockApp(t *testing.T) (*mock.App, Keeper) {
RegisterWire(mApp.Cdc)
keyStake := sdk.NewKVStoreKey("stake")
tkeyStake := sdk.NewTransientStoreKey("transient_stake")
bankKeeper := bank.NewBaseKeeper(mApp.AccountMapper)
keeper := NewKeeper(mApp.Cdc, keyStake, bankKeeper, mApp.RegisterCodespace(DefaultCodespace))
keeper := NewKeeper(mApp.Cdc, keyStake, tkeyStake, bankKeeper, mApp.RegisterCodespace(DefaultCodespace))
mApp.Router().AddRoute("stake", NewHandler(keeper))
mApp.SetEndBlocker(getEndBlocker(keeper))
mApp.SetInitChainer(getInitChainer(mApp, keeper))
require.NoError(t, mApp.CompleteSetup([]*sdk.KVStoreKey{keyStake}))
require.NoError(t, mApp.CompleteSetup(keyStake, tkeyStake))
return mApp, keeper
}
+19 -15
View File
@@ -60,7 +60,7 @@ type EditDelegationsBody struct {
ChainID string `json:"chain_id"`
AccountNumber int64 `json:"account_number"`
Sequence int64 `json:"sequence"`
Gas int64 `json:"gas"`
Gas string `json:"gas"`
GasAdjustment string `json:"gas_adjustment"`
Delegations []msgDelegationsInput `json:"delegations"`
BeginUnbondings []msgBeginUnbondingInput `json:"begin_unbondings"`
@@ -263,10 +263,21 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
i++
}
simulateGas, gas, err := client.ReadGasFlag(m.Gas)
if err != nil {
utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
txBldr := authtxb.TxBuilder{
Codec: cdc,
ChainID: m.ChainID,
Gas: m.Gas,
Codec: cdc,
Gas: gas,
GasAdjustment: adjustment,
SimulateGas: simulateGas,
ChainID: m.ChainID,
}
// sign messages
@@ -275,26 +286,19 @@ func delegationsRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx contex
// increment sequence for each message
txBldr = txBldr.WithAccountNumber(m.AccountNumber)
txBldr = txBldr.WithSequence(m.Sequence)
m.Sequence++
adjustment, ok := utils.ParseFloat64OrReturnBadRequest(w, m.GasAdjustment, client.DefaultGasAdjustment)
if !ok {
return
}
cliCtx = cliCtx.WithGasAdjustment(adjustment)
if utils.HasDryRunArg(r) || m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
if utils.HasDryRunArg(r) || txBldr.SimulateGas {
newBldr, err := utils.EnrichCtxWithGas(txBldr, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}
if utils.HasDryRunArg(r) {
utils.WriteSimulationResponse(w, txBldr.Gas)
utils.WriteSimulationResponse(w, newBldr.Gas)
return
}
txBldr = newCtx
txBldr = newBldr
}
if utils.HasGenerateOnlyArg(r) {
+1 -2
View File
@@ -52,8 +52,7 @@ func EndBlocker(ctx sdk.Context, k keeper.Keeper) (ValidatorUpdates []abci.Valid
k.SetIntraTxCounter(ctx, 0)
// calculate validator set changes
ValidatorUpdates = k.GetTendermintUpdates(ctx)
k.ClearTendermintUpdates(ctx)
ValidatorUpdates = k.GetValidTendermintUpdates(ctx)
return
}
+7 -3
View File
@@ -3,6 +3,7 @@
This document provided a bit more insight as to the purpose of several related
prefixed areas of the staking store which are accessed in `x/stake/keeper.go`.
# IAVL Store
## Validators
- Prefix Key Space: ValidatorsKey
@@ -36,10 +37,13 @@ prefixed areas of the staking store which are accessed in `x/stake/keeper.go`.
through this set to determine who we've kicked out.
retrieving validator by tendermint index
# Transient Store
The transient store persists between transations but not between blocks
## Tendermint Updates
- Prefix Key Space: TendermintUpdatesKey
- Prefix Key Space: TendermintUpdatesTKey
- Key/Sort: Validator Operator Address
- Value: Tendermint ABCI Validator
- Contains: Validators are queued to affect the consensus validation set in Tendermint
- Used For: Informing Tendermint of the validator set updates, is used only intra-block, as the
updates are applied then cleared on endblock
- Used For: Informing Tendermint of the validator set updates
+3 -1
View File
@@ -11,6 +11,7 @@ import (
// keeper of the stake store
type Keeper struct {
storeKey sdk.StoreKey
storeTKey sdk.StoreKey
cdc *wire.Codec
bankKeeper bank.Keeper
validatorHooks sdk.ValidatorHooks
@@ -19,9 +20,10 @@ type Keeper struct {
codespace sdk.CodespaceType
}
func NewKeeper(cdc *wire.Codec, key sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
func NewKeeper(cdc *wire.Codec, key, tkey sdk.StoreKey, ck bank.Keeper, codespace sdk.CodespaceType) Keeper {
keeper := Keeper{
storeKey: key,
storeTKey: tkey,
cdc: cdc,
bankKeeper: ck,
validatorHooks: nil,
+12 -10
View File
@@ -22,14 +22,16 @@ var (
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
ValidatorPowerCliffKey = []byte{0x07} // key for the power of the validator on the cliff
TendermintUpdatesKey = []byte{0x08} // prefix for each key to a validator which is being updated
IntraTxCounterKey = []byte{0x09} // key for intra-block tx index
DelegationKey = []byte{0x0A} // key for a delegation
UnbondingDelegationKey = []byte{0x0B} // key for an unbonding-delegation
UnbondingDelegationByValIndexKey = []byte{0x0C} // prefix for each key for an unbonding-delegation, by validator operator
RedelegationKey = []byte{0x0D} // key for a redelegation
RedelegationByValSrcIndexKey = []byte{0x0E} // prefix for each key for an redelegation, by source validator operator
RedelegationByValDstIndexKey = []byte{0x0F} // prefix for each key for an redelegation, by destination validator operator
IntraTxCounterKey = []byte{0x08} // key for intra-block tx index
DelegationKey = []byte{0x09} // key for a delegation
UnbondingDelegationKey = []byte{0x0A} // key for an unbonding-delegation
UnbondingDelegationByValIndexKey = []byte{0x0B} // prefix for each key for an unbonding-delegation, by validator operator
RedelegationKey = []byte{0x0C} // key for a redelegation
RedelegationByValSrcIndexKey = []byte{0x0D} // prefix for each key for an redelegation, by source validator operator
RedelegationByValDstIndexKey = []byte{0x0E} // prefix for each key for an redelegation, by destination validator operator
// Keys for store prefixes (transient)
TendermintUpdatesTKey = []byte{0x00} // prefix for each key to a validator which is being updated
)
const maxDigitsForAccount = 12 // ~220,000,000 atoms created at launch
@@ -98,8 +100,8 @@ func getValidatorPowerRank(validator types.Validator, pool types.Pool) []byte {
// get the key for the accumulated update validators
// VALUE: abci.Validator
// note records using these keys should never persist between blocks
func GetTendermintUpdatesKey(operatorAddr sdk.ValAddress) []byte {
return append(TendermintUpdatesKey, operatorAddr.Bytes()...)
func GetTendermintUpdatesTKey(operatorAddr sdk.ValAddress) []byte {
return append(TendermintUpdatesTKey, operatorAddr.Bytes()...)
}
//______________________________________________________________________________
+16 -8
View File
@@ -28,7 +28,7 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
logger := ctx.Logger().With("module", "x/stake")
if slashFactor.LT(sdk.ZeroDec()) {
panic(fmt.Errorf("attempted to slash with a negative slashFactor: %v", slashFactor))
panic(fmt.Errorf("attempted to slash with a negative slash factor: %v", slashFactor))
}
// Amount of slashing = slash slashFactor * power at time of infraction
@@ -50,7 +50,7 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
// should not be slashing unbonded
if validator.IsUnbonded(ctx) {
panic(fmt.Sprintf("should not be slashing unbonded validator: %v", validator))
panic(fmt.Sprintf("should not be slashing unbonded validator: %s", validator.GetOperator()))
}
operatorAddress := validator.GetOperator()
@@ -72,7 +72,7 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
// Special-case slash at current height for efficiency - we don't need to look through unbonding delegations or redelegations
logger.Info(fmt.Sprintf(
"Slashing at current height %d, not scanning unbonding delegations & redelegations",
"slashing at current height %d, not scanning unbonding delegations & redelegations",
infractionHeight))
case infractionHeight < ctx.BlockHeight():
@@ -117,8 +117,8 @@ func (k Keeper) Slash(ctx sdk.Context, pubkey crypto.PubKey, infractionHeight in
// Log that a slash occurred!
logger.Info(fmt.Sprintf(
"Validator %s slashed by slashFactor %s, burned %v tokens",
pubkey.Address(), slashFactor.String(), tokensToBurn))
"validator %s slashed by slash factor of %s; burned %v tokens",
validator.GetOperator(), slashFactor.String(), tokensToBurn))
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
return
@@ -127,8 +127,12 @@ 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())
}
logger := ctx.Logger().With("module", "x/stake")
logger.Info(fmt.Sprintf("Validator %s jailed", pubkey.Address()))
logger.Info(fmt.Sprintf("validator %s jailed", validatorAddr))
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
return
}
@@ -136,8 +140,12 @@ func (k Keeper) Jail(ctx sdk.Context, pubkey crypto.PubKey) {
// 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())
}
logger := ctx.Logger().With("module", "x/stake")
logger.Info(fmt.Sprintf("Validator %s unjailed", pubkey.Address()))
logger.Info(fmt.Sprintf("validator %s unjailed", validatorAddr))
// TODO Return event(s), blocked on https://github.com/tendermint/tendermint/pull/1803
return
}
@@ -146,7 +154,7 @@ func (k Keeper) Unjail(ctx sdk.Context, pubkey crypto.PubKey) {
func (k Keeper) setJailed(ctx sdk.Context, pubkey crypto.PubKey, isJailed bool) {
validator, found := k.GetValidatorByPubKey(ctx, pubkey)
if !found {
panic(fmt.Errorf("Validator with pubkey %s not found, cannot set jailed to %v", pubkey, isJailed))
panic(fmt.Errorf("validator with pubkey %s not found, cannot set jailed to %v", pubkey, isJailed))
}
validator.Jailed = isJailed
k.UpdateValidator(ctx, validator) // update validator, possibly unbonding or bonding it
+3 -1
View File
@@ -90,10 +90,12 @@ func ParamsNoInflation() types.Params {
func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context, auth.AccountMapper, Keeper) {
keyStake := sdk.NewKVStoreKey("stake")
tkeyStake := sdk.NewTransientStoreKey("transient_stake")
keyAcc := sdk.NewKVStoreKey("acc")
db := dbm.NewMemDB()
ms := store.NewCommitMultiStore(db)
ms.MountStoreWithDB(tkeyStake, sdk.StoreTypeTransient, nil)
ms.MountStoreWithDB(keyStake, sdk.StoreTypeIAVL, db)
ms.MountStoreWithDB(keyAcc, sdk.StoreTypeIAVL, db)
err := ms.LoadLatestVersion()
@@ -107,7 +109,7 @@ func CreateTestInput(t *testing.T, isCheckTx bool, initCoins int64) (sdk.Context
auth.ProtoBaseAccount, // prototype
)
ck := bank.NewBaseKeeper(accountMapper)
keeper := NewKeeper(cdc, keyStake, ck, types.DefaultCodespace)
keeper := NewKeeper(cdc, keyStake, tkeyStake, ck, types.DefaultCodespace)
keeper.SetPool(ctx, types.InitialPool())
keeper.SetNewParams(ctx, types.DefaultParams())
keeper.InitIntraTxCounter(ctx)
+78 -71
View File
@@ -158,9 +158,7 @@ func (k Keeper) GetValidatorsBonded(ctx sdk.Context) (validators []types.Validat
}
address := GetAddressFromValBondedIndexKey(iterator.Key())
validator, found := k.GetValidator(ctx, address)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
}
ensureValidatorFound(found, address)
validators[i] = validator
i++
@@ -184,9 +182,8 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
}
address := iterator.Value()
validator, found := k.GetValidator(ctx, address)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", address))
}
ensureValidatorFound(found, address)
if validator.Status == sdk.Bonded {
validators[i] = validator
i++
@@ -201,32 +198,41 @@ func (k Keeper) GetValidatorsByPower(ctx sdk.Context) []types.Validator {
// Accumulated updates to the active/bonded validator set for tendermint
// get the most recently updated validators
func (k Keeper) GetTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) {
store := ctx.KVStore(k.storeKey)
//
// CONTRACT: Only validators with non-zero power or zero-power that were bonded
// at the previous block height or were removed from the validator set entirely
// are returned to Tendermint.
func (k Keeper) GetValidTendermintUpdates(ctx sdk.Context) (updates []abci.Validator) {
tstore := ctx.TransientStore(k.storeTKey)
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey) //smallest to largest
iterator := sdk.KVStorePrefixIterator(tstore, TendermintUpdatesTKey)
for ; iterator.Valid(); iterator.Next() {
valBytes := iterator.Value()
var val abci.Validator
k.cdc.MustUnmarshalBinary(valBytes, &val)
updates = append(updates, val)
var abciVal abci.Validator
abciValBytes := iterator.Value()
k.cdc.MustUnmarshalBinary(abciValBytes, &abciVal)
val, found := k.GetValidator(ctx, abciVal.GetAddress())
if found {
// The validator is new or already exists in the store and must adhere to
// Tendermint invariants.
prevBonded := val.BondHeight < ctx.BlockHeight() && val.BondHeight > val.UnbondingHeight
zeroPower := val.GetPower().Equal(sdk.ZeroDec())
if !zeroPower || zeroPower && prevBonded {
updates = append(updates, abciVal)
}
} else {
// Add the ABCI validator in such a case where the validator was removed
// from the store as it must have existed before.
updates = append(updates, abciVal)
}
}
iterator.Close()
return
}
// remove all validator update entries after applied to Tendermint
func (k Keeper) ClearTendermintUpdates(ctx sdk.Context) {
store := ctx.KVStore(k.storeKey)
// delete subspace
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesKey)
for ; iterator.Valid(); iterator.Next() {
store.Delete(iterator.Key())
}
iterator.Close()
}
//___________________________________________________________________________
// Perform all the necessary steps for when a validator changes its power. This
@@ -237,15 +243,20 @@ func (k Keeper) ClearTendermintUpdates(ctx sdk.Context) {
// nolint: gocyclo
// TODO: Remove above nolint, function needs to be simplified!
func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) types.Validator {
store := ctx.KVStore(k.storeKey)
tstore := ctx.TransientStore(k.storeTKey)
pool := k.GetPool(ctx)
oldValidator, oldFound := k.GetValidator(ctx, validator.OperatorAddr)
validator = k.updateForJailing(ctx, oldFound, oldValidator, validator)
powerIncreasing := k.getPowerIncreasing(ctx, oldFound, oldValidator, validator)
validator.BondHeight, validator.BondIntraTxCounter = k.bondIncrement(ctx, oldFound, oldValidator, validator)
validator.BondHeight, validator.BondIntraTxCounter = k.bondIncrement(ctx, oldFound, oldValidator)
valPower := k.updateValidatorPower(ctx, oldFound, oldValidator, validator, pool)
cliffPower := k.GetCliffValidatorPower(ctx)
cliffValExists := (cliffPower != nil)
var valPowerLTcliffPower bool
if cliffValExists {
valPowerLTcliffPower = (bytes.Compare(valPower, cliffPower) == -1)
}
switch {
@@ -257,9 +268,9 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
(oldFound && oldValidator.Status == sdk.Bonded):
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
store.Set(GetTendermintUpdatesKey(validator.OperatorAddr), bz)
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz)
if cliffPower != nil {
if cliffValExists {
cliffAddr := sdk.ValAddress(k.GetCliffValidator(ctx))
if bytes.Equal(cliffAddr, validator.OperatorAddr) {
k.updateCliffValidator(ctx, validator)
@@ -267,14 +278,13 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
}
// if is a new validator and the new power is less than the cliff validator
case cliffPower != nil && !oldFound &&
bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower
case cliffValExists && !oldFound && valPowerLTcliffPower:
// skip to completion
// if was unbonded and the new power is less than the cliff validator
case cliffPower != nil &&
case cliffValExists &&
(oldFound && oldValidator.Status == sdk.Unbonded) &&
bytes.Compare(valPower, cliffPower) == -1: //(valPower < cliffPower
valPowerLTcliffPower: //(valPower < cliffPower
// skip to completion
default:
@@ -293,7 +303,7 @@ func (k Keeper) UpdateValidator(ctx sdk.Context, validator types.Validator) type
// if decreased in power but still bonded, update Tendermint validator
if oldFound && oldValidator.BondedTokens().GT(validator.BondedTokens()) {
bz := k.cdc.MustMarshalBinary(validator.ABCIValidator())
store.Set(GetTendermintUpdatesKey(validator.OperatorAddr), bz)
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bz)
}
}
@@ -315,7 +325,7 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato
oldCliffVal, found := k.GetValidator(ctx, cliffAddr)
if !found {
panic(fmt.Sprintf("cliff validator record not found for address: %v\n", cliffAddr))
panic(fmt.Sprintf("cliff validator record not found for address: %X\n", cliffAddr))
}
// Create a validator iterator ranging from smallest to largest by power
@@ -327,12 +337,10 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato
if iterator.Valid() {
ownerAddr := iterator.Value()
currVal, found := k.GetValidator(ctx, ownerAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
}
ensureValidatorFound(found, ownerAddr)
if currVal.Status != sdk.Bonded || currVal.Jailed {
panic(fmt.Sprintf("unexpected jailed or unbonded validator for address: %s\n", ownerAddr))
panic(fmt.Sprintf("unexpected jailed or unbonded validator for address: %X\n", ownerAddr))
}
newCliffVal = currVal
@@ -345,13 +353,10 @@ func (k Keeper) updateCliffValidator(ctx sdk.Context, affectedVal types.Validato
newCliffValRank := GetValidatorsByPowerIndexKey(newCliffVal, pool)
if bytes.Equal(affectedVal.OperatorAddr, newCliffVal.OperatorAddr) {
// The affected validator remains the cliff validator, however, since
// the store does not contain the new power, update the new power rank.
store.Set(ValidatorPowerCliffKey, affectedValRank)
} else if bytes.Compare(affectedValRank, newCliffValRank) > 0 {
// The affected validator no longer remains the cliff validator as it's
// power is greater than the new cliff validator.
k.setCliffValidator(ctx, newCliffVal, pool)
@@ -382,18 +387,20 @@ func (k Keeper) getPowerIncreasing(ctx sdk.Context, oldFound bool, oldValidator,
// get the bond height and incremented intra-tx counter
// nolint: unparam
func (k Keeper) bondIncrement(ctx sdk.Context, oldFound bool, oldValidator,
newValidator types.Validator) (height int64, intraTxCounter int16) {
func (k Keeper) bondIncrement(
ctx sdk.Context, found bool, oldValidator types.Validator) (height int64, intraTxCounter int16) {
// if already a validator, copy the old block height and counter, else set them
if oldFound && oldValidator.Status == sdk.Bonded {
// if already a validator, copy the old block height and counter
if found && oldValidator.Status == sdk.Bonded {
height = oldValidator.BondHeight
intraTxCounter = oldValidator.BondIntraTxCounter
return
}
height = ctx.BlockHeight()
counter := k.GetIntraTxCounter(ctx)
intraTxCounter = counter
k.SetIntraTxCounter(ctx, counter+1)
return
}
@@ -454,29 +461,25 @@ func (k Keeper) UpdateBondedValidators(
} else {
var found bool
validator, found = k.GetValidator(ctx, ownerAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
}
ensureValidatorFound(found, ownerAddr)
}
// if we've reached jailed validators no further bonded validators exist
if validator.Jailed {
break
}
// increment bondedValidatorsCount / get the validator to bond
if validator.Status != sdk.Bonded {
validatorToBond = validator
if newValidatorBonded {
panic("already decided to bond a validator, can't bond another!")
if validator.Status == sdk.Bonded {
panic(fmt.Sprintf("jailed validator cannot be bonded, address: %X\n", ownerAddr))
}
newValidatorBonded = true
break
}
// increment the total number of bonded validators and potentially mark
// the validator to bond
if validator.Status != sdk.Bonded {
validatorToBond = validator
if newValidatorBonded {
panic("already decided to bond a validator, can't bond another!")
}
newValidatorBonded = true
}
@@ -502,9 +505,7 @@ func (k Keeper) UpdateBondedValidators(
if newValidatorBonded {
if oldCliffValidatorAddr != nil {
oldCliffVal, found := k.GetValidator(ctx, oldCliffValidatorAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", oldCliffValidatorAddr))
}
ensureValidatorFound(found, oldCliffValidatorAddr)
if bytes.Equal(validatorToBond.OperatorAddr, affectedValidator.OperatorAddr) {
@@ -512,7 +513,6 @@ func (k Keeper) UpdateBondedValidators(
// validator was newly bonded and has greater power
k.beginUnbondingValidator(ctx, oldCliffVal)
} else {
// otherwise begin unbonding the affected validator, which must
// have been kicked out
affectedValidator = k.beginUnbondingValidator(ctx, affectedValidator)
@@ -560,9 +560,7 @@ func (k Keeper) UpdateBondedValidatorsFull(ctx sdk.Context) {
ownerAddr := iterator.Value()
validator, found = k.GetValidator(ctx, ownerAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
}
ensureValidatorFound(found, ownerAddr)
_, found = toKickOut[string(ownerAddr)]
if found {
@@ -605,9 +603,7 @@ func kickOutValidators(k Keeper, ctx sdk.Context, toKickOut map[string]byte) {
for key := range toKickOut {
ownerAddr := []byte(key)
validator, found := k.GetValidator(ctx, ownerAddr)
if !found {
panic(fmt.Sprintf("validator record not found for address: %v\n", ownerAddr))
}
ensureValidatorFound(found, ownerAddr)
k.beginUnbondingValidator(ctx, validator)
}
}
@@ -637,7 +633,8 @@ func (k Keeper) beginUnbondingValidator(ctx sdk.Context, validator types.Validat
// add to accumulated changes for tendermint
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero())
store.Set(GetTendermintUpdatesKey(validator.OperatorAddr), bzABCI)
tstore := ctx.TransientStore(k.storeTKey)
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI)
// also remove from the Bonded types.Validators Store
store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr))
@@ -662,6 +659,8 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.
panic(fmt.Sprintf("should not already be bonded, validator: %v\n", validator))
}
validator.BondHeight = ctx.BlockHeight()
// set the status
validator, pool = validator.UpdateStatus(pool, sdk.Bonded)
k.SetPool(ctx, pool)
@@ -672,7 +671,8 @@ func (k Keeper) bondValidator(ctx sdk.Context, validator types.Validator) types.
// add to accumulated changes for tendermint
bzABCI := k.cdc.MustMarshalBinary(validator.ABCIValidator())
store.Set(GetTendermintUpdatesKey(validator.OperatorAddr), bzABCI)
tstore := ctx.TransientStore(k.storeTKey)
tstore.Set(GetTendermintUpdatesTKey(validator.OperatorAddr), bzABCI)
// call the bond hook if present
if k.validatorHooks != nil {
@@ -707,7 +707,8 @@ func (k Keeper) RemoveValidator(ctx sdk.Context, address sdk.ValAddress) {
store.Delete(GetValidatorsBondedIndexKey(validator.OperatorAddr))
bz := k.cdc.MustMarshalBinary(validator.ABCIValidatorZero())
store.Set(GetTendermintUpdatesKey(address), bz)
tstore := ctx.TransientStore(k.storeTKey)
tstore.Set(GetTendermintUpdatesTKey(address), bz)
}
//__________________________________________________________________________
@@ -738,3 +739,9 @@ func (k Keeper) clearCliffValidator(ctx sdk.Context) {
store.Delete(ValidatorPowerCliffKey)
store.Delete(ValidatorCliffIndexKey)
}
func ensureValidatorFound(found bool, ownerAddr []byte) {
if !found {
panic(fmt.Sprintf("validator record not found for address: %X\n", ownerAddr))
}
}
+210 -76
View File
@@ -6,18 +6,34 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/stake/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// for testing, remove all validator update entries after applied to Tendermint
func clearTendermintUpdates(ctx sdk.Context, k Keeper) {
store := ctx.TransientStore(k.storeTKey)
// delete subspace
iterator := sdk.KVStorePrefixIterator(store, TendermintUpdatesTKey)
for ; iterator.Valid(); iterator.Next() {
store.Delete(iterator.Key())
}
iterator.Close()
}
//_______________________________________________________
func TestSetValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 10)
pool := keeper.GetPool(ctx)
valPubKey := PKs[0]
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
// test how the validator is set from a purely unbonbed pool
validator := types.NewValidator(addrVals[0], PKs[0], types.Description{})
validator := types.NewValidator(valAddr, valPubKey, types.Description{})
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(10))
require.Equal(t, sdk.Unbonded, validator.Status)
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens))
@@ -26,14 +42,14 @@ func TestSetValidator(t *testing.T) {
keeper.UpdateValidator(ctx, validator)
// after the save the validator should be bonded
validator, found := keeper.GetValidator(ctx, addrVals[0])
validator, found := keeper.GetValidator(ctx, valAddr)
require.True(t, found)
require.Equal(t, sdk.Bonded, validator.Status)
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.Tokens))
assert.True(sdk.DecEq(t, sdk.NewDec(10), validator.DelegatorShares))
// Check each store for being saved
resVal, found := keeper.GetValidator(ctx, addrVals[0])
resVal, found := keeper.GetValidator(ctx, valAddr)
assert.True(ValEq(t, validator, resVal))
require.True(t, found)
@@ -45,7 +61,7 @@ func TestSetValidator(t *testing.T) {
require.Equal(t, 1, len(resVals))
assert.True(ValEq(t, validator, resVals[0]))
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validator.ABCIValidator(), updates[0])
}
@@ -633,67 +649,35 @@ func TestFullValidatorSetPowerChange(t *testing.T) {
assert.True(ValEq(t, validators[2], resValidators[1]))
}
// clear the tracked changes to the gotValidator set
func TestClearTendermintUpdates(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{100, 400, 200}
validators := make([]types.Validator, len(amts))
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
keeper.UpdateValidator(ctx, validators[i])
}
updates := keeper.GetTendermintUpdates(ctx)
require.Equal(t, len(amts), len(updates))
keeper.ClearTendermintUpdates(ctx)
updates = keeper.GetTendermintUpdates(ctx)
require.Equal(t, 0, len(updates))
}
func TestGetTendermintUpdatesAllNone(t *testing.T) {
func TestGetValidTendermintUpdatesAllNone(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
var validators [2]types.Validator
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i] = types.NewValidator(sdk.ValAddress(Addrs[i]), PKs[i], types.Description{})
valPubKey := PKs[i+1]
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
}
// test from nothing to something
// tendermintUpdate set: {} -> {c1, c3}
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.GetValidTendermintUpdates(ctx)
assert.Equal(t, 2, len(updates))
assert.Equal(t, validators[0].ABCIValidator(), updates[0])
assert.Equal(t, validators[1].ABCIValidator(), updates[1])
// test from something to nothing
// tendermintUpdate set: {} -> {c1, c2, c3, c4}
keeper.ClearTendermintUpdates(ctx)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
keeper.RemoveValidator(ctx, validators[0].OperatorAddr)
keeper.RemoveValidator(ctx, validators[1].OperatorAddr)
updates = keeper.GetTendermintUpdates(ctx)
assert.Equal(t, 2, len(updates))
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[0].ConsPubKey), updates[0].PubKey)
assert.Equal(t, tmtypes.TM2PB.PubKey(validators[1].ConsPubKey), updates[1].PubKey)
assert.Equal(t, int64(0), updates[0].Power)
assert.Equal(t, int64(0), updates[1].Power)
}
func TestGetTendermintUpdatesIdentical(t *testing.T) {
func TestGetValidTendermintUpdatesIdentical(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@@ -706,17 +690,17 @@ func TestGetTendermintUpdatesIdentical(t *testing.T) {
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// test identical,
// tendermintUpdate set: {} -> {}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
}
func TestGetTendermintUpdatesSingleValueChange(t *testing.T) {
func TestGetValidTendermintUpdatesSingleValueChange(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@@ -729,8 +713,8 @@ func TestGetTendermintUpdatesSingleValueChange(t *testing.T) {
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// test single value change
// tendermintUpdate set: {} -> {c1'}
@@ -738,13 +722,13 @@ func TestGetTendermintUpdatesSingleValueChange(t *testing.T) {
validators[0].Tokens = sdk.NewDec(600)
validators[0] = keeper.UpdateValidator(ctx, validators[0])
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[0].ABCIValidator(), updates[0])
}
func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) {
func TestGetValidTendermintUpdatesMultipleValueChange(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20}
@@ -757,8 +741,8 @@ func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) {
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// test multiple value change
// tendermintUpdate set: {c1, c3} -> {c1', c3'}
@@ -769,13 +753,13 @@ func TestGetTendermintUpdatesMultipleValueChange(t *testing.T) {
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 2, len(updates))
require.Equal(t, validators[0].ABCIValidator(), updates[0])
require.Equal(t, validators[1].ABCIValidator(), updates[1])
}
func TestGetTendermintUpdatesInserted(t *testing.T) {
func TestGetValidTendermintUpdatesInserted(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{10, 20, 5, 15, 25}
@@ -788,34 +772,34 @@ func TestGetTendermintUpdatesInserted(t *testing.T) {
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// test validtor added at the beginning
// tendermintUpdate set: {} -> {c0}
validators[2] = keeper.UpdateValidator(ctx, validators[2])
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[2].ABCIValidator(), updates[0])
// test validtor added at the beginning
// tendermintUpdate set: {} -> {c0}
keeper.ClearTendermintUpdates(ctx)
clearTendermintUpdates(ctx, keeper)
validators[3] = keeper.UpdateValidator(ctx, validators[3])
updates = keeper.GetTendermintUpdates(ctx)
updates = keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[3].ABCIValidator(), updates[0])
// test validtor added at the end
// tendermintUpdate set: {} -> {c0}
keeper.ClearTendermintUpdates(ctx)
clearTendermintUpdates(ctx, keeper)
validators[4] = keeper.UpdateValidator(ctx, validators[4])
updates = keeper.GetTendermintUpdates(ctx)
updates = keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[4].ABCIValidator(), updates[0])
}
func TestGetTendermintUpdatesWithCliffValidator(t *testing.T) {
func TestGetValidTendermintUpdatesWithCliffValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
params := types.DefaultParams()
params.MaxValidators = 2
@@ -831,32 +815,32 @@ func TestGetTendermintUpdatesWithCliffValidator(t *testing.T) {
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// test validator added at the end but not inserted in the valset
// tendermintUpdate set: {} -> {}
keeper.UpdateValidator(ctx, validators[2])
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 0, len(updates))
// test validator change its power and become a gotValidator (pushing out an existing)
// tendermintUpdate set: {} -> {c0, c4}
keeper.ClearTendermintUpdates(ctx)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
pool := keeper.GetPool(ctx)
validators[2], pool, _ = validators[2].AddTokensFromDel(pool, sdk.NewInt(10))
keeper.SetPool(ctx, pool)
validators[2] = keeper.UpdateValidator(ctx, validators[2])
updates = keeper.GetTendermintUpdates(ctx)
updates = keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 2, len(updates), "%v", updates)
require.Equal(t, validators[0].ABCIValidatorZero(), updates[0])
require.Equal(t, validators[2].ABCIValidator(), updates[1])
}
func TestGetTendermintUpdatesPowerDecrease(t *testing.T) {
func TestGetValidTendermintUpdatesPowerDecrease(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
amts := []int64{100, 100}
@@ -869,8 +853,8 @@ func TestGetTendermintUpdatesPowerDecrease(t *testing.T) {
}
validators[0] = keeper.UpdateValidator(ctx, validators[0])
validators[1] = keeper.UpdateValidator(ctx, validators[1])
keeper.ClearTendermintUpdates(ctx)
require.Equal(t, 0, len(keeper.GetTendermintUpdates(ctx)))
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// check initial power
require.Equal(t, sdk.NewDec(100).RoundInt64(), validators[0].GetPower().RoundInt64())
@@ -890,8 +874,158 @@ func TestGetTendermintUpdatesPowerDecrease(t *testing.T) {
require.Equal(t, sdk.NewDec(70).RoundInt64(), validators[1].GetPower().RoundInt64())
// Tendermint updates should reflect power change
updates := keeper.GetTendermintUpdates(ctx)
updates := keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 2, len(updates))
require.Equal(t, validators[0].ABCIValidator(), updates[0])
require.Equal(t, validators[1].ABCIValidator(), updates[1])
}
func TestGetValidTendermintUpdatesNewValidator(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
params := keeper.GetParams(ctx)
params.MaxValidators = uint16(3)
keeper.SetParams(ctx, params)
amts := []int64{100, 100}
var validators [2]types.Validator
// initialize some validators into the state
for i, amt := range amts {
pool := keeper.GetPool(ctx)
valPubKey := PKs[i+1]
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
}
// verify initial Tendermint updates are correct
updates := keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, len(validators), len(updates))
require.Equal(t, validators[0].ABCIValidator(), updates[0])
require.Equal(t, validators[1].ABCIValidator(), updates[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// update initial validator set
for i, amt := range amts {
pool := keeper.GetPool(ctx)
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
}
// add a new validator that goes from zero power, to non-zero power, back to
// zero power
pool := keeper.GetPool(ctx)
valPubKey := PKs[len(validators)+1]
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
amt := sdk.NewInt(100)
validator := types.NewValidator(valAddr, valPubKey, types.Description{})
validator, pool, _ = validator.AddTokensFromDel(pool, amt)
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator, pool, _ = validator.RemoveDelShares(pool, sdk.NewDecFromInt(amt))
validator = keeper.UpdateValidator(ctx, validator)
// add a new validator that increases in power
valPubKey = PKs[len(validators)+2]
valAddr = sdk.ValAddress(valPubKey.Address().Bytes())
validator = types.NewValidator(valAddr, valPubKey, types.Description{})
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(500))
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
// verify initial Tendermint updates are correct
updates = keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, len(validators)+1, len(updates))
require.Equal(t, validator.ABCIValidator(), updates[0])
require.Equal(t, validators[0].ABCIValidator(), updates[1])
require.Equal(t, validators[1].ABCIValidator(), updates[2])
}
func TestGetValidTendermintUpdatesBondTransition(t *testing.T) {
ctx, _, keeper := CreateTestInput(t, false, 1000)
params := keeper.GetParams(ctx)
params.MaxValidators = uint16(2)
keeper.SetParams(ctx, params)
amts := []int64{100, 200, 300}
var validators [3]types.Validator
// initialize some validators into the state
for i, amt := range amts {
pool := keeper.GetPool(ctx)
moniker := fmt.Sprintf("%d", i)
valPubKey := PKs[i+1]
valAddr := sdk.ValAddress(valPubKey.Address().Bytes())
validators[i] = types.NewValidator(valAddr, valPubKey, types.Description{Moniker: moniker})
validators[i], pool, _ = validators[i].AddTokensFromDel(pool, sdk.NewInt(amt))
keeper.SetPool(ctx, pool)
validators[i] = keeper.UpdateValidator(ctx, validators[i])
}
// verify initial Tendermint updates are correct
updates := keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 2, len(updates))
require.Equal(t, validators[2].ABCIValidator(), updates[0])
require.Equal(t, validators[1].ABCIValidator(), updates[1])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// delegate to validator with lowest power but not enough to bond
ctx = ctx.WithBlockHeight(1)
pool := keeper.GetPool(ctx)
validator, found := keeper.GetValidator(ctx, validators[0].OperatorAddr)
require.True(t, found)
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(1))
keeper.SetPool(ctx, pool)
validators[0] = keeper.UpdateValidator(ctx, validator)
// verify initial Tendermint updates are correct
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
// create a series of events that will bond and unbond the validator with
// lowest power in a single block context (height)
ctx = ctx.WithBlockHeight(2)
pool = keeper.GetPool(ctx)
validator, found = keeper.GetValidator(ctx, validators[1].OperatorAddr)
require.True(t, found)
validator, pool, _ = validator.RemoveDelShares(pool, validator.DelegatorShares)
keeper.SetPool(ctx, pool)
validator = keeper.UpdateValidator(ctx, validator)
validator, pool, _ = validator.AddTokensFromDel(pool, sdk.NewInt(250))
keeper.SetPool(ctx, pool)
validators[1] = keeper.UpdateValidator(ctx, validator)
// verify initial Tendermint updates are correct
updates = keeper.GetValidTendermintUpdates(ctx)
require.Equal(t, 1, len(updates))
require.Equal(t, validators[1].ABCIValidator(), updates[0])
clearTendermintUpdates(ctx, keeper)
require.Equal(t, 0, len(keeper.GetValidTendermintUpdates(ctx)))
}
+11 -10
View File
@@ -23,7 +23,8 @@ func TestStakeWithRandomMessages(t *testing.T) {
mapper := mapp.AccountMapper
bankKeeper := bank.NewBaseKeeper(mapper)
stakeKey := sdk.NewKVStoreKey("stake")
stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, bankKeeper, stake.DefaultCodespace)
stakeTKey := sdk.NewTransientStoreKey("transient_stake")
stakeKeeper := stake.NewKeeper(mapp.Cdc, stakeKey, stakeTKey, bankKeeper, stake.DefaultCodespace)
mapp.Router().AddRoute("stake", stake.NewHandler(stakeKeeper))
mapp.SetEndBlocker(func(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
validatorUpdates := stake.EndBlocker(ctx, stakeKeeper)
@@ -32,7 +33,7 @@ func TestStakeWithRandomMessages(t *testing.T) {
}
})
err := mapp.CompleteSetup([]*sdk.KVStoreKey{stakeKey})
err := mapp.CompleteSetup(stakeKey, stakeTKey)
if err != nil {
panic(err)
}
@@ -44,14 +45,14 @@ func TestStakeWithRandomMessages(t *testing.T) {
simulation.Simulate(
t, mapp.BaseApp, appStateFn,
[]simulation.Operation{
SimulateMsgCreateValidator(mapper, stakeKeeper),
SimulateMsgEditValidator(stakeKeeper),
SimulateMsgDelegate(mapper, stakeKeeper),
SimulateMsgBeginUnbonding(mapper, stakeKeeper),
SimulateMsgCompleteUnbonding(stakeKeeper),
SimulateMsgBeginRedelegate(mapper, stakeKeeper),
SimulateMsgCompleteRedelegate(stakeKeeper),
[]simulation.WeightedOperation{
{10, SimulateMsgCreateValidator(mapper, stakeKeeper)},
{5, SimulateMsgEditValidator(stakeKeeper)},
{15, SimulateMsgDelegate(mapper, stakeKeeper)},
{10, SimulateMsgBeginUnbonding(mapper, stakeKeeper)},
{3, SimulateMsgCompleteUnbonding(stakeKeeper)},
{10, SimulateMsgBeginRedelegate(mapper, stakeKeeper)},
{3, SimulateMsgCompleteRedelegate(stakeKeeper)},
}, []simulation.RandSetup{
Setup(mapp, stakeKeeper),
}, []simulation.Invariant{
+2 -2
View File
@@ -33,7 +33,7 @@ var (
GetValidatorByPubKeyIndexKey = keeper.GetValidatorByPubKeyIndexKey
GetValidatorsBondedIndexKey = keeper.GetValidatorsBondedIndexKey
GetValidatorsByPowerIndexKey = keeper.GetValidatorsByPowerIndexKey
GetTendermintUpdatesKey = keeper.GetTendermintUpdatesKey
GetTendermintUpdatesTKey = keeper.GetTendermintUpdatesTKey
GetDelegationKey = keeper.GetDelegationKey
GetDelegationsKey = keeper.GetDelegationsKey
ParamKey = keeper.ParamKey
@@ -44,7 +44,7 @@ var (
ValidatorsByPowerIndexKey = keeper.ValidatorsByPowerIndexKey
ValidatorCliffIndexKey = keeper.ValidatorCliffIndexKey
ValidatorPowerCliffKey = keeper.ValidatorPowerCliffKey
TendermintUpdatesKey = keeper.TendermintUpdatesKey
TendermintUpdatesTKey = keeper.TendermintUpdatesTKey
DelegationKey = keeper.DelegationKey
IntraTxCounterKey = keeper.IntraTxCounterKey
GetUBDKey = keeper.GetUBDKey