From d8716052411d02f9d26d022ae1a19e2ce54d1166 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 1 May 2018 02:07:06 -0400 Subject: [PATCH] staking fee distribution working commit --- cmd/gaia/app/app.go | 2 +- cmd/gaia/app/app_test.go | 4 +- cmd/gaia/app/genesis.go | 2 +- types/handler.go | 2 +- x/auth/ante.go | 4 +- x/stake/fee_distribution.go | 31 ++++++- x/stake/keeper.go | 18 ++-- x/stake/keeper_test.go | 54 ++++++------ x/stake/test_common.go | 66 -------------- x/stake/types.go | 142 +++++++++++++++++++++++++----- x/stake/view_slash_keeper_test.go | 16 ++-- 11 files changed, 202 insertions(+), 139 deletions(-) diff --git a/cmd/gaia/app/app.go b/cmd/gaia/app/app.go index 5ff532bffa..6f49b95a05 100644 --- a/cmd/gaia/app/app.go +++ b/cmd/gaia/app/app.go @@ -81,7 +81,7 @@ func NewGaiaApp(logger log.Logger, db dbm.DB) *GaiaApp { app.SetInitChainer(app.initChainer) app.SetEndBlocker(stake.NewEndBlocker(app.stakeKeeper)) app.MountStoresIAVL(app.keyMain, app.keyAccount, app.keyIBC, app.keyStake) - app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, stake.FeeHandler)) + app.SetAnteHandler(auth.NewAnteHandler(app.accountMapper, app.stakeKeeper.FeeHandler)) err := app.LoadLatestVersion(app.keyMain) if err != nil { cmn.Exit(err.Error()) diff --git a/cmd/gaia/app/app_test.go b/cmd/gaia/app/app_test.go index fd26ce27a3..694843461c 100644 --- a/cmd/gaia/app/app_test.go +++ b/cmd/gaia/app/app_test.go @@ -105,7 +105,7 @@ func setGenesis(gapp *GaiaApp, accs ...*auth.BaseAccount) error { genesisState := GenesisState{ Accounts: genaccs, - StakeData: stake.GetDefaultGenesisState(), + StakeData: stake.DefaultGenesisState(), } stateBytes, err := wire.MarshalJSONIndent(gapp.cdc, genesisState) @@ -147,7 +147,7 @@ func setGenesisAccounts(gapp *GaiaApp, accs ...*auth.BaseAccount) error { genesisState := GenesisState{ Accounts: genaccs, - StakeData: stake.GetDefaultGenesisState(), + StakeData: stake.DefaultGenesisState(), } stateBytes, err := json.MarshalIndent(genesisState, "", "\t") diff --git a/cmd/gaia/app/genesis.go b/cmd/gaia/app/genesis.go index 7cb7564dd3..ef5296a307 100644 --- a/cmd/gaia/app/genesis.go +++ b/cmd/gaia/app/genesis.go @@ -135,7 +135,7 @@ func GaiaAppGenState(cdc *wire.Codec, appGenTxs []json.RawMessage) (appState jso } // start with the default staking genesis state - stakeData := stake.GetDefaultGenesisState() + stakeData := stake.DefaultGenesisState() // get genesis flag account information genaccs := make([]GenesisAccount, len(appGenTxs)) diff --git a/types/handler.go b/types/handler.go index 679a3b1a78..6127c52d72 100644 --- a/types/handler.go +++ b/types/handler.go @@ -4,7 +4,7 @@ package types type Handler func(ctx Context, msg Msg) Result // core function variable which application runs to handle fees -type FeeHandler func(ctx Context, tx Tx, fee Coins) +type FeeHandler func(ctx Context, fee Coins) // If newCtx.IsZero(), ctx is used instead. type AnteHandler func(ctx Context, tx Tx) (newCtx Context, result Result, abort bool) diff --git a/x/auth/ante.go b/x/auth/ante.go index 248083206d..9a697b14a8 100644 --- a/x/auth/ante.go +++ b/x/auth/ante.go @@ -77,7 +77,7 @@ func NewAnteHandler(am sdk.AccountMapper, feeHandler sdk.FeeHandler) sdk.AnteHan // TODO: min fee if !fee.Amount.IsZero() { signerAcc, res = deductFees(signerAcc, fee) - feeHandler(ctx, tx, fee.Amount) + feeHandler(ctx, fee.Amount) if !res.IsOK() { return ctx, res, true } @@ -166,5 +166,5 @@ func deductFees(acc sdk.Account, fee sdk.StdFee) (sdk.Account, sdk.Result) { } // BurnFeeHandler burns all fees (decreasing total supply) -func BurnFeeHandler(ctx sdk.Context, tx sdk.Tx, fee sdk.Coins) { +func BurnFeeHandler(ctx sdk.Context, fee sdk.Coins) { } diff --git a/x/stake/fee_distribution.go b/x/stake/fee_distribution.go index 940a66e38c..0924c5e8a9 100644 --- a/x/stake/fee_distribution.go +++ b/x/stake/fee_distribution.go @@ -5,6 +5,35 @@ import ( ) // Handle fee distribution to the validators and delegators -func FeeHandler(ctx sdk.Context, tx sdk.Tx, fee sdk.Coins) { +func (k Keeper) FeeHandler(ctx sdk.Context, collectedFees sdk.Coins) { + pool := k.GetPool(ctx) + params := k.GetParams(ctx) + // XXX calculate + sumOfVotingPowerOfPrecommitValidators := sdk.NewRat(67, 100) + candidate := NewCandidate(addrs[0], pks[0], Description{}) + + toProposer := coinsMulRat(collectedFees, (sdk.NewRat(1, 100).Add(sdk.NewRat(4, 100).Mul(sumOfVotingPowerOfPrecommitValidators).Quo(pool.BondedShares)))) + candidate.ProposerRewardPool = candidate.ProposerRewardPool.Plus(toProposer) + + toReservePool := coinsMulRat(collectedFees, params.ReservePoolFee) + pool.ReservePool = pool.ReservePool.Plus(toReservePool) + + distributedReward := (collectedFees.Minus(toProposer)).Minus(toReservePool) + pool.FeePool = pool.FeePool.Plus(distributedReward) + pool.SumFeesReceived = pool.SumFeesReceived.Plus(distributedReward) + pool.RecentFee = distributedReward + + k.setPool(ctx, pool) +} + +// XXX need to introduce rat amount based coins for the pool :( +func coinsMulRat(coins sdk.Coins, rat sdk.Rat) sdk.Coins { + var res sdk.Coins + for _, coin := range coins { + coinMulAmt := rat.Mul(sdk.NewRat(coin.Amount)).Evaluate() + coinMul := sdk.Coins{{coin.Denom, coinMulAmt}} + res = res.Plus(coinMul) + } + return res } diff --git a/x/stake/keeper.go b/x/stake/keeper.go index 7b472d31de..b2e3a3635f 100644 --- a/x/stake/keeper.go +++ b/x/stake/keeper.go @@ -77,7 +77,7 @@ func (k Keeper) GetCandidate(ctx sdk.Context, addr sdk.Address) (candidate Candi // Get the set of all candidates, retrieve a maxRetrieve number of records func (k Keeper) GetCandidates(ctx sdk.Context, maxRetrieve int16) (candidates Candidates) { store := ctx.KVStore(k.storeKey) - iterator := store.Iterator(subspace(CandidatesKey)) + iterator := store.SubspaceIterator(CandidatesKey) candidates = make([]Candidate, maxRetrieve) i := 0 @@ -220,7 +220,7 @@ func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) { store := ctx.KVStore(k.storeKey) // clear the recent validators store, add to the ToKickOut Temp store - iterator := store.Iterator(subspace(RecentValidatorsKey)) + iterator := store.SubspaceIterator(RecentValidatorsKey) for ; iterator.Valid(); iterator.Next() { addr := AddrFromKey(iterator.Key()) @@ -232,7 +232,7 @@ func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) { // add the actual validator power sorted store maxValidators := k.GetParams(ctx).MaxValidators - iterator = store.ReverseIterator(subspace(ValidatorsKey)) // largest to smallest + iterator = store.ReverseSubspaceIterator(ValidatorsKey) // largest to smallest validators = make([]Validator, maxValidators) i := 0 for ; ; i++ { @@ -258,7 +258,7 @@ func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) { } // add any kicked out validators to the acc change - iterator = store.Iterator(subspace(ToKickOutValidatorsKey)) + iterator = store.SubspaceIterator(ToKickOutValidatorsKey) for ; iterator.Valid(); iterator.Next() { key := iterator.Key() addr := AddrFromKey(key) @@ -289,7 +289,7 @@ func (k Keeper) GetValidators(ctx sdk.Context) (validators []Validator) { func (k Keeper) isNewValidator(ctx sdk.Context, store sdk.KVStore, address sdk.Address) bool { // add the actual validator power sorted store maxVal := k.GetParams(ctx).MaxValidators - iterator := store.ReverseIterator(subspace(ValidatorsKey)) // largest to smallest + iterator := store.ReverseSubspaceIterator(ValidatorsKey) // largest to smallest for i := 0; ; i++ { if !iterator.Valid() || i > int(maxVal-1) { iterator.Close() @@ -326,7 +326,7 @@ func (k Keeper) IsRecentValidator(ctx sdk.Context, address sdk.Address) bool { func (k Keeper) getAccUpdateValidators(ctx sdk.Context) (updates []abci.Validator) { store := ctx.KVStore(k.storeKey) - iterator := store.Iterator(subspace(AccUpdateValidatorsKey)) //smallest to largest + iterator := store.SubspaceIterator(AccUpdateValidatorsKey) //smallest to largest for ; iterator.Valid(); iterator.Next() { valBytes := iterator.Value() var val abci.Validator @@ -345,7 +345,7 @@ func (k Keeper) clearAccUpdateValidators(ctx sdk.Context) { store := ctx.KVStore(k.storeKey) // delete subspace - iterator := store.Iterator(subspace(AccUpdateValidatorsKey)) + iterator := store.SubspaceIterator(AccUpdateValidatorsKey) for ; iterator.Valid(); iterator.Next() { store.Delete(iterator.Key()) } @@ -374,7 +374,7 @@ func (k Keeper) GetDelegatorBond(ctx sdk.Context, // load all bonds func (k Keeper) getBonds(ctx sdk.Context, maxRetrieve int16) (bonds []DelegatorBond) { store := ctx.KVStore(k.storeKey) - iterator := store.Iterator(subspace(DelegatorBondKeyPrefix)) + iterator := store.SubspaceIterator(DelegatorBondKeyPrefix) bonds = make([]DelegatorBond, maxRetrieve) i := 0 @@ -399,7 +399,7 @@ func (k Keeper) getBonds(ctx sdk.Context, maxRetrieve int16) (bonds []DelegatorB func (k Keeper) GetDelegatorBonds(ctx sdk.Context, delegator sdk.Address, maxRetrieve int16) (bonds []DelegatorBond) { store := ctx.KVStore(k.storeKey) delegatorPrefixKey := GetDelegatorBondsKey(delegator, k.cdc) - iterator := store.Iterator(subspace(delegatorPrefixKey)) //smallest to largest + iterator := store.SubspaceIterator(delegatorPrefixKey) //smallest to largest bonds = make([]DelegatorBond, maxRetrieve) i := 0 diff --git a/x/stake/keeper_test.go b/x/stake/keeper_test.go index 76e69de58f..b537bd92e2 100644 --- a/x/stake/keeper_test.go +++ b/x/stake/keeper_test.go @@ -49,14 +49,14 @@ func TestCandidate(t *testing.T) { keeper.setCandidate(ctx, candidates[0]) resCand, found := keeper.GetCandidate(ctx, addrVals[0]) require.True(t, found) - assert.True(t, candidatesEqual(candidates[0], resCand), "%v \n %v", resCand, candidates[0]) + assert.True(t, candidates[0].equal(resCand), "%v \n %v", resCand, candidates[0]) // modify a records, save, and retrieve candidates[0].Liabilities = sdk.NewRat(99) keeper.setCandidate(ctx, candidates[0]) resCand, found = keeper.GetCandidate(ctx, addrVals[0]) require.True(t, found) - assert.True(t, candidatesEqual(candidates[0], resCand)) + assert.True(t, candidates[0].equal(resCand)) // also test that the address has been added to address list resCands = keeper.GetCandidates(ctx, 100) @@ -68,15 +68,15 @@ func TestCandidate(t *testing.T) { keeper.setCandidate(ctx, candidates[2]) resCand, found = keeper.GetCandidate(ctx, addrVals[1]) require.True(t, found) - assert.True(t, candidatesEqual(candidates[1], resCand), "%v \n %v", resCand, candidates[1]) + assert.True(t, candidates[1].equal(resCand), "%v \n %v", resCand, candidates[1]) resCand, found = keeper.GetCandidate(ctx, addrVals[2]) require.True(t, found) - assert.True(t, candidatesEqual(candidates[2], resCand), "%v \n %v", resCand, candidates[2]) + assert.True(t, candidates[2].equal(resCand), "%v \n %v", resCand, candidates[2]) resCands = keeper.GetCandidates(ctx, 100) require.Equal(t, 3, len(resCands)) - assert.True(t, candidatesEqual(candidates[0], resCands[0]), "%v \n %v", resCands[0], candidates[0]) - assert.True(t, candidatesEqual(candidates[1], resCands[1]), "%v \n %v", resCands[1], candidates[1]) - assert.True(t, candidatesEqual(candidates[2], resCands[2]), "%v \n %v", resCands[2], candidates[2]) + assert.True(t, candidates[0].equal(resCands[0]), "%v \n %v", resCands[0], candidates[0]) + assert.True(t, candidates[1].equal(resCands[1]), "%v \n %v", resCands[1], candidates[1]) + assert.True(t, candidates[2].equal(resCands[2]), "%v \n %v", resCands[2], candidates[2]) // remove a record keeper.removeCandidate(ctx, candidates[1].Address) @@ -117,14 +117,14 @@ func TestBond(t *testing.T) { keeper.setDelegatorBond(ctx, bond1to1) resBond, found := keeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0]) assert.True(t, found) - assert.True(t, bondsEqual(bond1to1, resBond)) + assert.True(t, bond1to1.equal(resBond)) // modify a records, save, and retrieve bond1to1.Shares = sdk.NewRat(99) keeper.setDelegatorBond(ctx, bond1to1) resBond, found = keeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0]) assert.True(t, found) - assert.True(t, bondsEqual(bond1to1, resBond)) + assert.True(t, bond1to1.equal(resBond)) // add some more records keeper.setCandidate(ctx, candidates[1]) @@ -143,26 +143,26 @@ func TestBond(t *testing.T) { // test all bond retrieve capabilities resBonds := keeper.GetDelegatorBonds(ctx, addrDels[0], 5) require.Equal(t, 3, len(resBonds)) - assert.True(t, bondsEqual(bond1to1, resBonds[0])) - assert.True(t, bondsEqual(bond1to2, resBonds[1])) - assert.True(t, bondsEqual(bond1to3, resBonds[2])) + assert.True(t, bond1to1.equal(resBonds[0])) + assert.True(t, bond1to2.equal(resBonds[1])) + assert.True(t, bond1to3.equal(resBonds[2])) resBonds = keeper.GetDelegatorBonds(ctx, addrDels[0], 3) require.Equal(t, 3, len(resBonds)) resBonds = keeper.GetDelegatorBonds(ctx, addrDels[0], 2) require.Equal(t, 2, len(resBonds)) resBonds = keeper.GetDelegatorBonds(ctx, addrDels[1], 5) require.Equal(t, 3, len(resBonds)) - assert.True(t, bondsEqual(bond2to1, resBonds[0])) - assert.True(t, bondsEqual(bond2to2, resBonds[1])) - assert.True(t, bondsEqual(bond2to3, resBonds[2])) + assert.True(t, bond2to1.equal(resBonds[0])) + assert.True(t, bond2to2.equal(resBonds[1])) + assert.True(t, bond2to3.equal(resBonds[2])) allBonds := keeper.getBonds(ctx, 1000) require.Equal(t, 6, len(allBonds)) - assert.True(t, bondsEqual(bond1to1, allBonds[0])) - assert.True(t, bondsEqual(bond1to2, allBonds[1])) - assert.True(t, bondsEqual(bond1to3, allBonds[2])) - assert.True(t, bondsEqual(bond2to1, allBonds[3])) - assert.True(t, bondsEqual(bond2to2, allBonds[4])) - assert.True(t, bondsEqual(bond2to3, allBonds[5])) + assert.True(t, bond1to1.equal(allBonds[0])) + assert.True(t, bond1to2.equal(allBonds[1])) + assert.True(t, bond1to3.equal(allBonds[2])) + assert.True(t, bond2to1.equal(allBonds[3])) + assert.True(t, bond2to2.equal(allBonds[4])) + assert.True(t, bond2to3.equal(allBonds[5])) // delete a record keeper.removeDelegatorBond(ctx, bond2to3) @@ -170,8 +170,8 @@ func TestBond(t *testing.T) { assert.False(t, found) resBonds = keeper.GetDelegatorBonds(ctx, addrDels[1], 5) require.Equal(t, 2, len(resBonds)) - assert.True(t, bondsEqual(bond2to1, resBonds[0])) - assert.True(t, bondsEqual(bond2to2, resBonds[1])) + assert.True(t, bond2to1.equal(resBonds[0])) + assert.True(t, bond2to2.equal(resBonds[1])) // delete all the records from delegator 2 keeper.removeDelegatorBond(ctx, bond2to1) @@ -443,8 +443,8 @@ func TestGetAccUpdateValidators(t *testing.T) { require.Equal(t, 2, len(candidates)) assert.Equal(t, candidates[0].validator().abciValidator(keeper.cdc), acc[0]) assert.Equal(t, candidates[1].validator().abciValidator(keeper.cdc), acc[1]) - assert.True(t, validatorsEqual(candidates[0].validator(), vals[1])) - assert.True(t, validatorsEqual(candidates[1].validator(), vals[0])) + assert.True(t, candidates[0].validator().equal(vals[1])) + assert.True(t, candidates[1].validator().equal(vals[0])) // test identical, // candidate set: {c1, c3} -> {c1, c3} @@ -637,10 +637,10 @@ func TestIsRecentValidator(t *testing.T) { keeper.setCandidate(ctx, candidatesIn[1]) validators = keeper.GetValidators(ctx) require.Equal(t, 2, len(validators)) - assert.True(t, validatorsEqual(candidatesIn[0].validator(), validators[0])) + assert.True(t, candidatesIn[0].validator().equal(validators[0])) c1ValWithCounter := candidatesIn[1].validator() c1ValWithCounter.Counter = int16(1) - assert.True(t, validatorsEqual(c1ValWithCounter, validators[1])) + assert.True(t, c1ValWithCounter.equal(validators[1])) // test a basic retrieve of something that should be a recent validator assert.True(t, keeper.IsRecentValidator(ctx, candidatesIn[0].Address)) diff --git a/x/stake/test_common.go b/x/stake/test_common.go index 27acebe086..b5139b4b34 100644 --- a/x/stake/test_common.go +++ b/x/stake/test_common.go @@ -1,7 +1,6 @@ package stake import ( - "bytes" "encoding/hex" "testing" @@ -52,71 +51,6 @@ var ( emptyPubkey crypto.PubKey ) -func validatorsEqual(b1, b2 Validator) bool { - return bytes.Equal(b1.Address, b2.Address) && - b1.PubKey.Equals(b2.PubKey) && - b1.Power.Equal(b2.Power) && - b1.Height == b2.Height && - b1.Counter == b2.Counter -} - -func candidatesEqual(c1, c2 Candidate) bool { - return c1.Status == c2.Status && - c1.PubKey.Equals(c2.PubKey) && - bytes.Equal(c1.Address, c2.Address) && - c1.Assets.Equal(c2.Assets) && - c1.Liabilities.Equal(c2.Liabilities) && - c1.Description == c2.Description -} - -func bondsEqual(b1, b2 DelegatorBond) bool { - return bytes.Equal(b1.DelegatorAddr, b2.DelegatorAddr) && - bytes.Equal(b1.CandidateAddr, b2.CandidateAddr) && - b1.Height == b2.Height && - b1.Shares.Equal(b2.Shares) -} - -// default params for testing -func defaultParams() Params { - return Params{ - InflationRateChange: sdk.NewRat(13, 100), - InflationMax: sdk.NewRat(20, 100), - InflationMin: sdk.NewRat(7, 100), - GoalBonded: sdk.NewRat(67, 100), - MaxValidators: 100, - BondDenom: "steak", - } -} - -// initial pool for testing -func initialPool() Pool { - return Pool{ - TotalSupply: 0, - BondedShares: sdk.ZeroRat(), - UnbondedShares: sdk.ZeroRat(), - BondedPool: 0, - UnbondedPool: 0, - InflationLastTime: 0, - Inflation: sdk.NewRat(7, 100), - } -} - -// get raw genesis raw message for testing -func GetDefaultGenesisState() GenesisState { - return GenesisState{ - Pool: initialPool(), - Params: defaultParams(), - } -} - -// XXX reference the common declaration of this function -func subspace(prefix []byte) (start, end []byte) { - end = make([]byte, len(prefix)) - copy(end, prefix) - end[len(end)-1]++ - return prefix, end -} - func makeTestCodec() *wire.Codec { var cdc = wire.NewCodec() diff --git a/x/stake/types.go b/x/stake/types.go index ffe2ca4fb4..918075358b 100644 --- a/x/stake/types.go +++ b/x/stake/types.go @@ -1,6 +1,8 @@ package stake import ( + "bytes" + sdk "github.com/cosmos/cosmos-sdk/types" crypto "github.com/tendermint/go-crypto" @@ -15,6 +17,14 @@ type GenesisState struct { Bonds []DelegatorBond `json:"bonds"` } +// get raw genesis raw message for testing +func DefaultGenesisState() GenesisState { + return GenesisState{ + Pool: initialPool(), + Params: defaultParams(), + } +} + //_________________________________________________________________________ // Params defines the high level settings for staking @@ -26,6 +36,8 @@ type Params struct { MaxValidators uint16 `json:"max_validators"` // maximum number of validators BondDenom string `json:"bond_denom"` // bondable coin denomination + + ReservePoolFee sdk.Rat `json:"reserve_pool_fee"` // percent of fees which go to reserve pool } func (p Params) equal(p2 Params) bool { @@ -34,7 +46,20 @@ func (p Params) equal(p2 Params) bool { p.InflationMin.Equal(p2.InflationMin) && p.GoalBonded.Equal(p2.GoalBonded) && p.MaxValidators == p2.MaxValidators && - p.BondDenom == p2.BondDenom + p.BondDenom == p2.BondDenom && + p.ReservePoolFee.Equal(p2.ReservePoolFee) +} + +func defaultParams() Params { + return Params{ + InflationRateChange: sdk.NewRat(13, 100), + InflationMax: sdk.NewRat(20, 100), + InflationMin: sdk.NewRat(7, 100), + GoalBonded: sdk.NewRat(67, 100), + MaxValidators: 100, + BondDenom: "steak", + ReservePoolFee: sdk.NewRat(5, 100), + } } //_________________________________________________________________________ @@ -48,16 +73,50 @@ type Pool struct { UnbondedPool int64 `json:"unbonded_pool"` // reserve of unbonded tokens held with candidates InflationLastTime int64 `json:"inflation_last_time"` // block which the last inflation was processed // TODO make time Inflation sdk.Rat `json:"inflation"` // current annual inflation rate + + DateLastCommissionReset int64 `json:"date_last_commission_reset"` // unix timestamp for last commission accounting reset (daily) + + // XXX need to use special sdk.Rat amounts in coins here because added at small increments + ReservePool sdk.Coins `json:"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 + SumFeesReceived sdk.Coins `json:"sum_fees_received"` // XXX sum of all fees received + RecentFee sdk.Coins `json:"recent_fee"` // XXX most recent fee collected + Adjustment sdk.Rat `json:"adjustment"` // XXX Adjustment factor for calculating global fee accum } func (p Pool) equal(p2 Pool) bool { - return p.BondedShares.Equal(p2.BondedShares) && + return p.TotalSupply == p2.TotalSupply && + p.BondedShares.Equal(p2.BondedShares) && p.UnbondedShares.Equal(p2.UnbondedShares) && - p.Inflation.Equal(p2.Inflation) && - p.TotalSupply == p2.TotalSupply && p.BondedPool == p2.BondedPool && p.UnbondedPool == p2.UnbondedPool && - p.InflationLastTime == p2.InflationLastTime + p.InflationLastTime == p2.InflationLastTime && + p.Inflation.Equal(p2.Inflation) && + p.DateLastCommissionReset == p2.DateLastCommissionReset && + p.ReservePool.IsEqual(p2.ReservePool) && + p.FeePool.IsEqual(p2.FeePool) && + p.SumFeesReceived.IsEqual(p2.SumFeesReceived) && + p.RecentFee.IsEqual(p2.RecentFee) && + p.Adjustment.Equal(p2.Adjustment) +} + +// initial pool for testing +func initialPool() Pool { + return Pool{ + TotalSupply: 0, + BondedShares: sdk.ZeroRat(), + UnbondedShares: sdk.ZeroRat(), + BondedPool: 0, + UnbondedPool: 0, + InflationLastTime: 0, + Inflation: sdk.NewRat(7, 100), + DateLastCommissionReset: 0, + ReservePool: sdk.Coins{}, + FeePool: sdk.Coins{}, + SumFeesReceived: sdk.Coins{}, + RecentFee: sdk.Coins{}, + Adjustment: sdk.ZeroRat(), + } } //_________________________________________________________________________ @@ -80,14 +139,19 @@ const ( // exchange rate. Voting power can be calculated as total bonds multiplied by // exchange rate. type Candidate struct { - Status CandidateStatus `json:"status"` // Bonded status - Address sdk.Address `json:"owner"` // Sender of BondTx - UnbondTx returns here - PubKey crypto.PubKey `json:"pub_key"` // Pubkey of candidate - Assets sdk.Rat `json:"assets"` // total shares of a global hold pools - Liabilities sdk.Rat `json:"liabilities"` // total shares issued to a candidate's delegators - Description Description `json:"description"` // Description terms for the candidate - ValidatorBondHeight int64 `json:"validator_bond_height"` // Earliest height as a bonded validator - ValidatorBondCounter int16 `json:"validator_bond_counter"` // Block-local tx index of validator change + Status CandidateStatus `json:"status"` // Bonded status + Address sdk.Address `json:"owner"` // Sender of BondTx - UnbondTx returns here + PubKey crypto.PubKey `json:"pub_key"` // Pubkey of candidate + Assets sdk.Rat `json:"assets"` // total shares of a global hold pools + Liabilities sdk.Rat `json:"liabilities"` // total shares issued to a candidate's delegators + Description Description `json:"description"` // Description terms for the candidate + ValidatorBondHeight int64 `json:"validator_bond_height"` // Earliest height as a bonded validator + ValidatorBondCounter int16 `json:"validator_bond_counter"` // Block-local tx index of validator change + ProposerRewardPool sdk.Coins `json:"proposer_reward_pool"` // XXX reward pool collected from being the proposer + Commission sdk.Rat `json:"commission"` // XXX the commission rate of fees charged to any delegators + CommissionMax sdk.Rat `json:"commission_max"` // XXX maximum commission rate which this candidate can ever charge + CommissionChangeRate sdk.Rat `json:"commission_change_rate"` // XXX maximum daily increase of the candidate commission + CommissionChangeToday sdk.Rat `json:"commission_change_today"` // XXX commission rate change today, reset each day (UTC time) } // Candidates - list of Candidates @@ -96,17 +160,38 @@ type Candidates []Candidate // NewCandidate - initialize a new candidate func NewCandidate(address sdk.Address, pubKey crypto.PubKey, description Description) Candidate { return Candidate{ - Status: Unbonded, - Address: address, - PubKey: pubKey, - Assets: sdk.ZeroRat(), - Liabilities: sdk.ZeroRat(), - Description: description, - ValidatorBondHeight: int64(0), - ValidatorBondCounter: int16(0), + Status: Unbonded, + Address: address, + PubKey: pubKey, + Assets: sdk.ZeroRat(), + Liabilities: sdk.ZeroRat(), + Description: description, + ValidatorBondHeight: int64(0), + ValidatorBondCounter: int16(0), + ProposerRewardPool: sdk.Coins{}, + Commission: sdk.ZeroRat(), + CommissionMax: sdk.ZeroRat(), + CommissionChangeRate: sdk.ZeroRat(), + CommissionChangeToday: sdk.ZeroRat(), } } +func (c Candidate) equal(c2 Candidate) bool { + return c.Status == c2.Status && + c.PubKey.Equals(c2.PubKey) && + bytes.Equal(c.Address, c2.Address) && + c.Assets.Equal(c2.Assets) && + c.Liabilities.Equal(c2.Liabilities) && + c.Description == c2.Description && + c.ValidatorBondHeight == c2.ValidatorBondHeight && + c.ValidatorBondCounter == c2.ValidatorBondCounter && + c.ProposerRewardPool.IsEqual(c2.ProposerRewardPool) && + c.Commission.Equal(c2.Commission) && + c.CommissionMax.Equal(c2.CommissionMax) && + c.CommissionChangeRate.Equal(c2.CommissionChangeRate) && + c.CommissionChangeToday.Equal(c2.CommissionChangeToday) +} + // Description - description fields for a candidate type Description struct { Moniker string `json:"moniker"` @@ -158,6 +243,14 @@ type Validator struct { Counter int16 `json:"counter"` // Block-local tx index for resolving equal voting power & height } +func (v Validator) equal(v2 Validator) bool { + return bytes.Equal(v.Address, v2.Address) && + v.PubKey.Equals(v2.PubKey) && + v.Power.Equal(v2.Power) && + v.Height == v2.Height && + v.Counter == v2.Counter +} + // abci validator from stake validator type func (v Validator) abciValidator(cdc *wire.Codec) sdk.Validator { return sdk.Validator{ @@ -187,3 +280,10 @@ type DelegatorBond struct { Shares sdk.Rat `json:"shares"` Height int64 `json:"height"` // Last height bond updated } + +func (b DelegatorBond) equal(b2 DelegatorBond) bool { + return bytes.Equal(b.DelegatorAddr, b2.DelegatorAddr) && + bytes.Equal(b.CandidateAddr, b2.CandidateAddr) && + b.Height == b2.Height && + b.Shares.Equal(b2.Shares) +} diff --git a/x/stake/view_slash_keeper_test.go b/x/stake/view_slash_keeper_test.go index 7380859105..7c75f5c954 100644 --- a/x/stake/view_slash_keeper_test.go +++ b/x/stake/view_slash_keeper_test.go @@ -44,14 +44,14 @@ func TestViewSlashBond(t *testing.T) { keeper.setDelegatorBond(ctx, bond1to1) resBond, found := viewSlashKeeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0]) assert.True(t, found) - assert.True(t, bondsEqual(bond1to1, resBond)) + assert.True(t, bond1to1.equal(resBond)) // modify a records, save, and retrieve bond1to1.Shares = sdk.NewRat(99) keeper.setDelegatorBond(ctx, bond1to1) resBond, found = viewSlashKeeper.GetDelegatorBond(ctx, addrDels[0], addrVals[0]) assert.True(t, found) - assert.True(t, bondsEqual(bond1to1, resBond)) + assert.True(t, bond1to1.equal(resBond)) // add some more records keeper.setCandidate(ctx, candidates[1]) @@ -70,17 +70,17 @@ func TestViewSlashBond(t *testing.T) { // test all bond retrieve capabilities resBonds := viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 5) require.Equal(t, 3, len(resBonds)) - assert.True(t, bondsEqual(bond1to1, resBonds[0])) - assert.True(t, bondsEqual(bond1to2, resBonds[1])) - assert.True(t, bondsEqual(bond1to3, resBonds[2])) + assert.True(t, bond1to1.equal(resBonds[0])) + assert.True(t, bond1to2.equal(resBonds[1])) + assert.True(t, bond1to3.equal(resBonds[2])) resBonds = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 3) require.Equal(t, 3, len(resBonds)) resBonds = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[0], 2) require.Equal(t, 2, len(resBonds)) resBonds = viewSlashKeeper.GetDelegatorBonds(ctx, addrDels[1], 5) require.Equal(t, 3, len(resBonds)) - assert.True(t, bondsEqual(bond2to1, resBonds[0])) - assert.True(t, bondsEqual(bond2to2, resBonds[1])) - assert.True(t, bondsEqual(bond2to3, resBonds[2])) + assert.True(t, bond2to1.equal(resBonds[0])) + assert.True(t, bond2to2.equal(resBonds[1])) + assert.True(t, bond2to3.equal(resBonds[2])) }