Merge PR #3819: Simulation Refactor
This commit is contained in:
committed by
Christopher Goes
parent
48b6b3884f
commit
f0d1efa43c
@@ -11,11 +11,11 @@ import (
|
||||
func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper) {
|
||||
|
||||
// determine the total power signing the block
|
||||
var totalPower, sumPrecommitPower int64
|
||||
var previousTotalPower, sumPreviousPrecommitPower int64
|
||||
for _, voteInfo := range req.LastCommitInfo.GetVotes() {
|
||||
totalPower += voteInfo.Validator.Power
|
||||
previousTotalPower += voteInfo.Validator.Power
|
||||
if voteInfo.SignedLastBlock {
|
||||
sumPrecommitPower += voteInfo.Validator.Power
|
||||
sumPreviousPrecommitPower += voteInfo.Validator.Power
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, k keeper.Keeper)
|
||||
// ref https://github.com/cosmos/cosmos-sdk/issues/3095
|
||||
if ctx.BlockHeight() > 1 {
|
||||
previousProposer := k.GetPreviousProposerConsAddr(ctx)
|
||||
k.AllocateTokens(ctx, sumPrecommitPower, totalPower, previousProposer, req.LastCommitInfo.GetVotes())
|
||||
k.AllocateTokens(ctx, sumPreviousPrecommitPower, previousTotalPower, previousProposer, req.LastCommitInfo.GetVotes())
|
||||
}
|
||||
|
||||
// record the proposer for when we payout on the next block
|
||||
|
||||
@@ -9,42 +9,45 @@ import (
|
||||
)
|
||||
|
||||
// allocate fees handles distribution of the collected fees
|
||||
func (k Keeper) AllocateTokens(ctx sdk.Context, sumPrecommitPower, totalPower int64, proposer sdk.ConsAddress, votes []abci.VoteInfo) {
|
||||
func (k Keeper) AllocateTokens(ctx sdk.Context, sumPreviousPrecommitPower, totalPreviousPower int64,
|
||||
previousProposer sdk.ConsAddress, previousVotes []abci.VoteInfo) {
|
||||
|
||||
logger := ctx.Logger().With("module", "x/distribution")
|
||||
|
||||
// fetch collected fees & fee pool
|
||||
// fetch and clear the collected fees for distribution, since this is
|
||||
// called in BeginBlock, collected fees will be from the previous block
|
||||
// (and distributed to the previous proposer)
|
||||
feesCollectedInt := k.feeCollectionKeeper.GetCollectedFees(ctx)
|
||||
feesCollected := sdk.NewDecCoins(feesCollectedInt)
|
||||
feePool := k.GetFeePool(ctx)
|
||||
|
||||
// clear collected fees, which will now be distributed
|
||||
k.feeCollectionKeeper.ClearCollectedFees(ctx)
|
||||
|
||||
// temporary workaround to keep CanWithdrawInvariant happy
|
||||
// general discussions here: https://github.com/cosmos/cosmos-sdk/issues/2906#issuecomment-441867634
|
||||
if totalPower == 0 {
|
||||
feePool := k.GetFeePool(ctx)
|
||||
if totalPreviousPower == 0 {
|
||||
feePool.CommunityPool = feePool.CommunityPool.Add(feesCollected)
|
||||
k.SetFeePool(ctx, feePool)
|
||||
return
|
||||
}
|
||||
|
||||
// calculate fraction votes
|
||||
fractionVotes := sdk.NewDec(sumPrecommitPower).Quo(sdk.NewDec(totalPower))
|
||||
previousFractionVotes := sdk.NewDec(sumPreviousPrecommitPower).Quo(sdk.NewDec(totalPreviousPower))
|
||||
|
||||
// calculate proposer reward
|
||||
// calculate previous proposer reward
|
||||
baseProposerReward := k.GetBaseProposerReward(ctx)
|
||||
bonusProposerReward := k.GetBonusProposerReward(ctx)
|
||||
proposerMultiplier := baseProposerReward.Add(bonusProposerReward.MulTruncate(fractionVotes))
|
||||
proposerMultiplier := baseProposerReward.Add(bonusProposerReward.MulTruncate(previousFractionVotes))
|
||||
proposerReward := feesCollected.MulDecTruncate(proposerMultiplier)
|
||||
|
||||
// pay proposer
|
||||
// pay previous proposer
|
||||
remaining := feesCollected
|
||||
proposerValidator := k.stakingKeeper.ValidatorByConsAddr(ctx, proposer)
|
||||
proposerValidator := k.stakingKeeper.ValidatorByConsAddr(ctx, previousProposer)
|
||||
|
||||
if proposerValidator != nil {
|
||||
k.AllocateTokensToValidator(ctx, proposerValidator, proposerReward)
|
||||
remaining = remaining.Sub(proposerReward)
|
||||
} else {
|
||||
// proposer can be unknown if say, the unbonding period is 1 block, so
|
||||
// previous proposer can be unknown if say, the unbonding period is 1 block, so
|
||||
// e.g. a validator undelegates at block X, it's removed entirely by
|
||||
// block X+1's endblock, then X+2 we need to refer to the previous
|
||||
// proposer for X+1, but we've forgotten about them.
|
||||
@@ -53,7 +56,7 @@ func (k Keeper) AllocateTokens(ctx sdk.Context, sumPrecommitPower, totalPower in
|
||||
"This should happen only if the proposer unbonded completely within a single block, "+
|
||||
"which generally should not happen except in exceptional circumstances (or fuzz testing). "+
|
||||
"We recommend you investigate immediately.",
|
||||
proposer.String()))
|
||||
previousProposer.String()))
|
||||
}
|
||||
|
||||
// calculate fraction allocated to validators
|
||||
@@ -62,14 +65,13 @@ func (k Keeper) AllocateTokens(ctx sdk.Context, sumPrecommitPower, totalPower in
|
||||
|
||||
// allocate tokens proportionally to voting power
|
||||
// TODO consider parallelizing later, ref https://github.com/cosmos/cosmos-sdk/pull/3099#discussion_r246276376
|
||||
for _, vote := range votes {
|
||||
for _, vote := range previousVotes {
|
||||
validator := k.stakingKeeper.ValidatorByConsAddr(ctx, vote.Validator.Address)
|
||||
|
||||
// TODO consider microslashing for missing votes.
|
||||
// ref https://github.com/cosmos/cosmos-sdk/issues/2525#issuecomment-430838701
|
||||
powerFraction := sdk.NewDec(vote.Validator.Power).QuoTruncate(sdk.NewDec(totalPower))
|
||||
powerFraction := sdk.NewDec(vote.Validator.Power).QuoTruncate(sdk.NewDec(totalPreviousPower))
|
||||
reward := feesCollected.MulDecTruncate(voteMultiplier).MulDecTruncate(powerFraction)
|
||||
reward = reward.Intersect(remaining)
|
||||
k.AllocateTokensToValidator(ctx, validator, reward)
|
||||
remaining = remaining.Sub(reward)
|
||||
}
|
||||
|
||||
@@ -8,34 +8,31 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||
"github.com/cosmos/cosmos-sdk/x/distribution"
|
||||
"github.com/cosmos/cosmos-sdk/x/mock/simulation"
|
||||
"github.com/cosmos/cosmos-sdk/x/simulation"
|
||||
)
|
||||
|
||||
// SimulateMsgSetWithdrawAddress
|
||||
func SimulateMsgSetWithdrawAddress(m auth.AccountKeeper, k distribution.Keeper) simulation.Operation {
|
||||
handler := distribution.NewHandler(k)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account, event func(string)) (
|
||||
action string, fOp []simulation.FutureOperation, err error) {
|
||||
accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
accountOrigin := simulation.RandomAcc(r, accs)
|
||||
accountDestination := simulation.RandomAcc(r, accs)
|
||||
msg := distribution.NewMsgSetWithdrawAddress(accountOrigin.Address, accountDestination.Address)
|
||||
|
||||
if msg.ValidateBasic() != nil {
|
||||
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
return simulation.NoOpMsg(), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
ok := handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
|
||||
event(fmt.Sprintf("distribution/MsgSetWithdrawAddress/%v", result.IsOK()))
|
||||
|
||||
action = fmt.Sprintf("TestMsgSetWithdrawAddress: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
|
||||
return action, nil, nil
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,27 +40,24 @@ func SimulateMsgSetWithdrawAddress(m auth.AccountKeeper, k distribution.Keeper)
|
||||
func SimulateMsgWithdrawDelegatorReward(m auth.AccountKeeper, k distribution.Keeper) simulation.Operation {
|
||||
handler := distribution.NewHandler(k)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account, event func(string)) (
|
||||
action string, fOp []simulation.FutureOperation, err error) {
|
||||
accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
delegatorAccount := simulation.RandomAcc(r, accs)
|
||||
validatorAccount := simulation.RandomAcc(r, accs)
|
||||
msg := distribution.NewMsgWithdrawDelegatorReward(delegatorAccount.Address, sdk.ValAddress(validatorAccount.Address))
|
||||
|
||||
if msg.ValidateBasic() != nil {
|
||||
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
return simulation.NoOpMsg(), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
ok := handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
|
||||
event(fmt.Sprintf("distribution/MsgWithdrawDelegatorReward/%v", result.IsOK()))
|
||||
|
||||
action = fmt.Sprintf("TestMsgWithdrawDelegatorReward: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
|
||||
return action, nil, nil
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,25 +65,22 @@ func SimulateMsgWithdrawDelegatorReward(m auth.AccountKeeper, k distribution.Kee
|
||||
func SimulateMsgWithdrawValidatorCommission(m auth.AccountKeeper, k distribution.Keeper) simulation.Operation {
|
||||
handler := distribution.NewHandler(k)
|
||||
return func(r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context,
|
||||
accs []simulation.Account, event func(string)) (
|
||||
action string, fOp []simulation.FutureOperation, err error) {
|
||||
accs []simulation.Account) (opMsg simulation.OperationMsg, fOps []simulation.FutureOperation, err error) {
|
||||
|
||||
account := simulation.RandomAcc(r, accs)
|
||||
msg := distribution.NewMsgWithdrawValidatorCommission(sdk.ValAddress(account.Address))
|
||||
|
||||
if msg.ValidateBasic() != nil {
|
||||
return "", nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
return simulation.NoOpMsg(), nil, fmt.Errorf("expected msg to pass ValidateBasic: %s", msg.GetSignBytes())
|
||||
}
|
||||
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
ok := handler(ctx, msg).IsOK()
|
||||
if ok {
|
||||
write()
|
||||
}
|
||||
|
||||
event(fmt.Sprintf("distribution/MsgWithdrawValidatorCommission/%v", result.IsOK()))
|
||||
|
||||
action = fmt.Sprintf("TestMsgWithdrawValidatorCommission: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
|
||||
return action, nil, nil
|
||||
opMsg = simulation.NewOperationMsg(msg, ok, "")
|
||||
return opMsg, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user