Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

R4R: Censorship Slash #1080

Merged
merged 5 commits into from
Jan 17, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions app/baseapp.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/irisnet/irishub/app/protocol"
"github.com/irisnet/irishub/codec"
"github.com/irisnet/irishub/modules/auth"
"github.com/irisnet/irishub/store"
sdk "github.com/irisnet/irishub/types"
"github.com/irisnet/irishub/version"
tmstate "github.com/tendermint/tendermint/state"
"strconv"
"github.com/irisnet/irishub/modules/auth"
)

// Key to store the consensus params in the main store.
Expand Down Expand Up @@ -85,10 +85,10 @@ var _ abci.Application = (*BaseApp)(nil)
// Accepts variable number of option functions, which act on the BaseApp to set configuration choices
func NewBaseApp(name string, logger log.Logger, db dbm.DB, options ...func(*BaseApp)) *BaseApp {
app := &BaseApp{
Logger: logger,
name: name,
db: db,
cms: store.NewCommitMultiStore(db),
Logger: logger,
name: name,
db: db,
cms: store.NewCommitMultiStore(db),
}

for _, option := range options {
Expand Down Expand Up @@ -491,7 +491,7 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
// by InitChain. Context is now updated with Header information.
app.deliverState.ctx = app.deliverState.ctx.
WithBlockHeader(req.Header).
WithBlockHeight(req.Header.Height)
WithBlockHeight(req.Header.Height).WithCheckValidNum(0)
}

// add block gas meter
Expand All @@ -511,6 +511,7 @@ func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeg
// set the signed validators for addition to context in deliverTx
// TODO: communicate this result to the address to pubkey map in slashing
app.voteInfos = req.LastCommitInfo.GetVotes()

return
}

Expand Down Expand Up @@ -548,7 +549,9 @@ func (app *BaseApp) DeliverTx(txBytes []byte) (res abci.ResponseDeliverTx) {
if err != nil {
result = err.Result()
} else {
// success pass txDecoder
result = app.runTx(RunTxModeDeliver, txBytes, tx)

}

// Even though the Result.Code is not OK, there are still effects,
Expand Down Expand Up @@ -713,6 +716,10 @@ func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk
return err.Result()
}

if mode == RunTxModeDeliver {
app.deliverState.ctx = app.deliverState.ctx.WithCheckValidNum(app.deliverState.ctx.CheckValidNum() + 1)
}

defer func() {
if r := recover(); r != nil {
switch rType := r.(type) {
Expand Down Expand Up @@ -762,7 +769,6 @@ func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk
}
}()


feePreprocessHandler := app.Engine.GetCurrentProtocol().GetFeePreprocessHandler()
// run the fee handler
if feePreprocessHandler != nil && ctx.BlockHeight() != 0 {
Expand Down Expand Up @@ -801,7 +807,7 @@ func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk
return result
}

newCtx.GasMeter().ConsumeGas(auth.BlockStoreCostPerByte * sdk.Gas(len(txBytes)), "blockstore")
newCtx.GasMeter().ConsumeGas(auth.BlockStoreCostPerByte*sdk.Gas(len(txBytes)), "blockstore")

msCache.Write()
gasWanted = result.GasWanted
Expand Down Expand Up @@ -831,6 +837,7 @@ func (app *BaseApp) runTx(mode RunTxMode, txBytes []byte, tx sdk.Tx) (result sdk

// EndBlock implements the ABCI application interface.
func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) {

if app.deliverState.ms.TracingEnabled() {
app.deliverState.ms = app.deliverState.ms.ResetTraceContext().(sdk.CacheMultiStore)
}
Expand Down
4 changes: 2 additions & 2 deletions app/v0/protocol_v0.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,10 +304,10 @@ func (p *ProtocolV0) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) a
// application updates every end block
func (p *ProtocolV0) EndBlocker(ctx sdk.Context, req abci.RequestEndBlock) abci.ResponseEndBlock {
tags := gov.EndBlocker(ctx, p.govKeeper)
validatorUpdates := stake.EndBlocker(ctx, p.StakeKeeper)
tags = tags.AppendTags(slashing.EndBlocker(ctx, req, p.slashingKeeper))
tags = tags.AppendTags(service.EndBlocker(ctx, p.serviceKeeper))
tags = tags.AppendTags(upgrade.EndBlocker(ctx, p.upgradeKeeper))

validatorUpdates := stake.EndBlocker(ctx, p.StakeKeeper)
p.assertRuntimeInvariants(ctx)

return abci.ResponseEndBlock{
Expand Down
44 changes: 44 additions & 0 deletions modules/slashing/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,50 @@ func (k Keeper) handleValidatorSignature(ctx sdk.Context, addr crypto.Address, p
return
}

// Punish proposer censorship by slashing malefactor's stake
func (k Keeper) handleProposerCensorship(ctx sdk.Context, addr crypto.Address, infractionHeight int64) (tags sdk.Tags) {
logger := ctx.Logger().With("module", "x/slashing")
time := ctx.BlockHeader().Time
consAddr := sdk.ConsAddress(addr)
pubkey, err := k.getPubkey(ctx, addr)
if err != nil {
panic(fmt.Sprintf("Validator consensus-address %v not found", consAddr))
}

// Get validator.
validator := k.validatorSet.ValidatorByConsAddr(ctx, consAddr)
if validator == nil || validator.GetStatus() == sdk.Unbonded {
// Defensive.
// Simulation doesn't take unbonding periods into account, and
// Tendermint might break this assumption at some point.
return
}
logger.Info(fmt.Sprintf("proposer censorship from %s at height %d", pubkey.Address(), infractionHeight))

distributionHeight := infractionHeight - stake.ValidatorUpdateDelay
// Slash validator
// `power` is the int64 power of the validator as provided to/by
// Tendermint. This value is validator.Tokens as sent to Tendermint via
// ABCI, and now received as evidence.
// The revisedFraction (which is the new fraction to be slashed) is passed
// in separately to separately slash unbonding and rebonding delegations.
tags = k.validatorSet.Slash(ctx, consAddr, distributionHeight, validator.GetPower().RoundInt64(),k.SlashFractionCensorship(ctx))

// Jail validator if not already jailed
if !validator.GetJailed() {
k.validatorSet.Jail(ctx, consAddr)
}

// Set or updated validator jail duration
signInfo, found := k.getValidatorSigningInfo(ctx, consAddr)
if !found {
panic(fmt.Sprintf("Expected signing info for validator %s but not found", consAddr))
}
signInfo.JailedUntil = time.Add(k.CensorshipUnbondDuration(ctx))
k.SetValidatorSigningInfo(ctx, consAddr, signInfo)
return
}

func (k Keeper) addPubkey(ctx sdk.Context, pubkey crypto.PubKey) {
addr := pubkey.Address()
k.setAddrPubkeyRelation(ctx, addr, pubkey)
Expand Down
66 changes: 65 additions & 1 deletion modules/slashing/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ var (
KeyDowntimeJailDuration = []byte("DowntimeJailDuration")
KeySlashFractionDoubleSign = []byte("SlashFractionDoubleSign")
KeySlashFractionDowntime = []byte("SlashFractionDowntime")
keySlashFractionCensorship = []byte("SlashFractionCensorship")
keyCensorshipJailDuration = []byte("CensorshipJailDuration")
)

// ParamTypeTable for slashing module
Expand All @@ -42,6 +44,8 @@ type Params struct {
DowntimeJailDuration time.Duration `json:"downtime-unbond-duration"`
SlashFractionDoubleSign sdk.Dec `json:"slash-fraction-double-sign"`
SlashFractionDowntime sdk.Dec `json:"slash-fraction-downtime"`
SlashFractionCensorship sdk.Dec `json:"slash-fraction-censorship"`
CensorshipJailDuration time.Duration `json:"censorship-jail-duration"`
}

// Implements params.ParamStruct
Expand All @@ -54,6 +58,8 @@ func (p *Params) KeyValuePairs() params.KeyValuePairs {
{KeyDowntimeJailDuration, &p.DowntimeJailDuration},
{KeySlashFractionDoubleSign, &p.SlashFractionDoubleSign},
{KeySlashFractionDowntime, &p.SlashFractionDowntime},
{keySlashFractionCensorship, &p.SlashFractionCensorship},
{keyCensorshipJailDuration, &p.CensorshipJailDuration},
}
}

Expand Down Expand Up @@ -122,6 +128,24 @@ func (p *Params) Validate(key string, value string) (interface{}, sdk.Error) {
return nil, err
}
return slashFractionDowntime, nil
case string(keySlashFractionCensorship):
slashFractionCensorship, err := sdk.NewDecFromStr(value)
if err != nil {
return nil, params.ErrInvalidString(value)
}
if err := validateSlashFractionCensorship(slashFractionCensorship); err != nil {
return nil, err
}
return slashFractionCensorship, nil
case string(keyCensorshipJailDuration):
censorshipJailDuration, err := time.ParseDuration(value)
if err != nil {
return nil, params.ErrInvalidString(value)
}
if err := validateCensorshipJailDuration(censorshipJailDuration); err != nil {
return nil, err
}
return censorshipJailDuration, nil
default:
return nil, sdk.NewError(params.DefaultCodespace, params.CodeInvalidKey, fmt.Sprintf("%s is not found", key))
}
Expand Down Expand Up @@ -154,6 +178,12 @@ func (p *Params) StringFromBytes(cdc *codec.Codec, key string, bytes []byte) (st
case string(KeySlashFractionDowntime):
err := cdc.UnmarshalJSON(bytes, &p.SlashFractionDowntime)
return p.SlashFractionDowntime.String(), err
case string(keySlashFractionCensorship):
err := cdc.UnmarshalJSON(bytes, &p.SlashFractionCensorship)
return p.SlashFractionCensorship.String(), err
case string(keyCensorshipJailDuration):
err := cdc.UnmarshalJSON(bytes, &p.CensorshipJailDuration)
return p.CensorshipJailDuration.String(), err
default:
return "", fmt.Errorf("%s is not existed", key)
}
Expand All @@ -169,6 +199,8 @@ func DefaultParams() Params {
MinSignedPerWindow: sdk.NewDecWithPrec(5, 1),
SlashFractionDoubleSign: sdk.NewDecWithPrec(1, 2),
SlashFractionDowntime: sdk.NewDecWithPrec(5, 3),
SlashFractionCensorship: sdk.NewDecWithPrec(2, 2),
CensorshipJailDuration: 7 * sdk.Day,
}
}

Expand All @@ -181,6 +213,8 @@ func DefaultParamsForTestnet() Params {
MinSignedPerWindow: sdk.NewDecWithPrec(5, 1),
SlashFractionDoubleSign: sdk.NewDec(1).Quo(sdk.NewDec(20)),
SlashFractionDowntime: sdk.NewDec(1).Quo(sdk.NewDec(100)),
SlashFractionCensorship: sdk.NewDecWithPrec(2, 2),
CensorshipJailDuration: 60 * 7 * time.Second,
}
}

Expand Down Expand Up @@ -210,7 +244,12 @@ func validateParams(p Params) sdk.Error {
if err := validateSlashFractionDowntime(p.SlashFractionDowntime); err != nil {
return err
}

if err := validateSlashFractionCensorship(p.SlashFractionCensorship); err != nil {
return err
}
if err := validateCensorshipJailDuration(p.CensorshipJailDuration); err != nil {
return err
}
return nil
}

Expand All @@ -232,6 +271,13 @@ func validateJailDuration(p time.Duration) sdk.Error {
return nil
}

func validateCensorshipJailDuration(p time.Duration) sdk.Error {
if p <= 0 || p >= 4 * sdk.Week {
return sdk.NewError(params.DefaultCodespace, params.CodeInvalidSlashParams, fmt.Sprintf("Slash CensorshipJailDuration should be between (0, 4week) ", p.String()))
}
return nil
}

func validateSignedBlocksWindow(p int64) sdk.Error {
if p < 100 || p > 140000 {
return sdk.NewError(params.DefaultCodespace, params.CodeInvalidSlashParams, fmt.Sprintf("Slash SignedBlocksWindow [%d] should be between [100, 140000] ", p))
Expand Down Expand Up @@ -260,6 +306,12 @@ func validateSlashFractionDowntime(p sdk.Dec) sdk.Error {
return nil
}

func validateSlashFractionCensorship(p sdk.Dec) sdk.Error {
if p.LT(sdk.NewDecWithPrec(5, 3)) || p.GT(sdk.NewDecWithPrec(1, 1)) {
return sdk.NewError(params.DefaultCodespace, params.CodeInvalidSlashParams, fmt.Sprintf("Slash SlashFractionCensorship [%s] should be between [0.005, 0.1] ", p.String()))
}
return nil
}
//______________________________________________________________________

// get inflation params from the global param store
Expand Down Expand Up @@ -317,3 +369,15 @@ func (k Keeper) SlashFractionDowntime(ctx sdk.Context) (res sdk.Dec) {
k.paramspace.Get(ctx, KeySlashFractionDowntime, &res)
return
}

// SlashFractionDowntime - currently default 1%
func (k Keeper) SlashFractionCensorship(ctx sdk.Context) (res sdk.Dec) {
k.paramspace.Get(ctx, keySlashFractionCensorship, &res)
return
}

// Downtime unbond duration
func (k Keeper) CensorshipUnbondDuration(ctx sdk.Context) (res time.Duration) {
k.paramspace.Get(ctx, keyCensorshipJailDuration, &res)
return
}
17 changes: 17 additions & 0 deletions modules/slashing/tick.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,20 @@ func BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock, sk Keeper) (tags

return
}

// slashing end block functionality
func EndBlocker(ctx sdk.Context,req abci.RequestEndBlock, sk Keeper) (tags sdk.Tags) {

// Tag the height
heightBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(heightBytes, uint64(req.Height))
tags = sdk.NewTags("height", heightBytes)

if int64(ctx.CheckValidNum()) < ctx.BlockHeader().NumTxs {
proposalCensorshipTag := sk.handleProposerCensorship(ctx,
ctx.BlockHeader().ProposerAddress,
ctx.BlockHeight())
tags = tags.AppendTags(proposalCensorshipTag)
}
return
}
10 changes: 9 additions & 1 deletion types/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func NewContext(ms MultiStore, header abci.Header, isCheckTx bool, logger log.Lo
c = c.WithGasMeter(NewInfiniteGasMeter())
c = c.WithMinimumFees(Coins{})
c = c.WithConsensusParams(nil)
c = c.WithCheckValidNum(0)
return c
}

Expand Down Expand Up @@ -143,6 +144,7 @@ const (
contextKeyGasMeter
contextKeyBlockGasMeter
contextKeyMinimumFees
contextKeyCheckValidNum
)

// NOTE: Do not expose MultiStore.
Expand All @@ -159,7 +161,6 @@ func (c Context) BlockHeight() int64 { return c.Value(contextKeyBlockHeight).(in
func (c Context) ConsensusParams() *abci.ConsensusParams {
return c.Value(contextKeyConsensusParams).(*abci.ConsensusParams)
}

func (c Context) ChainID() string { return c.Value(contextKeyChainID).(string) }

func (c Context) TxBytes() []byte { return c.Value(contextKeyTxBytes).([]byte) }
Expand All @@ -178,6 +179,9 @@ func (c Context) IsCheckTx() bool { return c.Value(contextKeyIsCheckTx).(bool) }

func (c Context) MinimumFees() Coins { return c.Value(contextKeyMinimumFees).(Coins) }


func (c Context) CheckValidNum() uint64 { return c.Value(contextKeyCheckValidNum).(uint64) }

func (c Context) WithMultiStore(ms MultiStore) Context { return c.withValue(contextKeyMultiStore, ms) }

func (c Context) WithBlockHeader(header abci.Header) Context {
Expand Down Expand Up @@ -207,6 +211,10 @@ func (c Context) WithConsensusParams(params *abci.ConsensusParams) Context {
return c.withValue(contextKeyConsensusParams, params)
}

func (c Context) WithCheckValidNum(checkValidNum uint64) Context {
return c.withValue(contextKeyCheckValidNum, checkValidNum)
}

func (c Context) WithChainID(chainID string) Context { return c.withValue(contextKeyChainID, chainID) }

func (c Context) WithTxBytes(txBytes []byte) Context { return c.withValue(contextKeyTxBytes, txBytes) }
Expand Down