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

Add double-sign slashing #97

Merged
merged 22 commits into from
May 23, 2022
Merged
Changes from 10 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
28 changes: 14 additions & 14 deletions app/consumer/app.go
Original file line number Diff line number Diff line change
@@ -182,16 +182,16 @@ var (

// module account permissions
maccPerms = map[string][]string{
authtypes.FeeCollectorName: nil,
authtypes.FeeCollectorName: nil,
ibcconsumertypes.ConsumerRedistributeName: nil,
ibcconsumertypes.ConsumerToSendToProviderName: nil,
distrtypes.ModuleName: nil,
minttypes.ModuleName: {authtypes.Minter},
stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking},
stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
govtypes.ModuleName: {authtypes.Burner},
liquiditytypes.ModuleName: {authtypes.Minter, authtypes.Burner},
ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner},
distrtypes.ModuleName: nil,
minttypes.ModuleName: {authtypes.Minter},
stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking},
stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking},
govtypes.ModuleName: {authtypes.Burner},
liquiditytypes.ModuleName: {authtypes.Minter, authtypes.Burner},
ibctransfertypes.ModuleName: {authtypes.Minter, authtypes.Burner},
}
)

@@ -241,13 +241,13 @@ type App struct { // nolint: golint
FeeGrantKeeper feegrantkeeper.Keeper
AuthzKeeper authzkeeper.Keeper
LiquidityKeeper liquiditykeeper.Keeper
ConsumerKeeper ibcconsumerkeeper.Keeper
ProviderKeeper ibcproviderkeeper.Keeper
ConsumerKeeper ibcconsumerkeeper.Keeper
ProviderKeeper ibcproviderkeeper.Keeper

// make scoped keepers public for test purposes
ScopedIBCKeeper capabilitykeeper.ScopedKeeper
ScopedTransferKeeper capabilitykeeper.ScopedKeeper
ScopedIBCConsumerKeeper capabilitykeeper.ScopedKeeper
ScopedIBCKeeper capabilitykeeper.ScopedKeeper
ScopedTransferKeeper capabilitykeeper.ScopedKeeper
ScopedIBCConsumerKeeper capabilitykeeper.ScopedKeeper
ScopedIBCProviderKeeper capabilitykeeper.ScopedKeeper

// the module manager
@@ -537,7 +537,7 @@ func New(
evidenceKeeper := evidencekeeper.NewKeeper(
appCodec,
keys[evidencetypes.StoreKey],
&app.StakingKeeper,
app.ConsumerKeeper,
app.SlashingKeeper,
)

12 changes: 0 additions & 12 deletions app/provider/app.go
Original file line number Diff line number Diff line change
@@ -477,18 +477,6 @@ func New(
authtypes.FeeCollectorName,
)
providerModule := ibcprovider.NewAppModule(&app.ProviderKeeper)

danwt marked this conversation as resolved.
Show resolved Hide resolved
// consumer keeper satisfies the staking keeper interface
// of the slashing module
app.SlashingKeeper = slashingkeeper.NewKeeper(
appCodec,
keys[slashingtypes.StoreKey],
app.ConsumerKeeper,
app.GetSubspace(slashingtypes.ModuleName),
)

// register slashing module StakingHooks to the consumer keeper
app.ConsumerKeeper = *app.ConsumerKeeper.SetHooks(app.SlashingKeeper.Hooks())
consumerModule := ibcconsumer.NewAppModule(app.ConsumerKeeper)

// register the proposal types
6 changes: 6 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -23,6 +23,9 @@ require (
github.com/tendermint/tendermint v0.34.14
github.com/tendermint/tm-db v0.6.4
github.com/tidwall/gjson v1.6.7
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e
google.golang.org/grpc v1.45.0
google.golang.org/protobuf v1.27.1
@@ -31,6 +34,9 @@ require (

replace (
github.com/99designs/keyring => github.com/cosmos/keyring v1.1.7-0.20210622111912-ef00f8ac3d76

// TODO: update this cosmos-sdk import once #11921 gets merged
github.com/cosmos/cosmos-sdk => github.com/sainoe/cosmos-sdk v0.44.4-0.20220510144203-a0f64cfd6249
github.com/cosmos/ibc-go/v3 => github.com/informalsystems/ibc-go/v3 v3.0.0-alpha1.0.20220505161112-1f45da82fc75
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
google.golang.org/grpc => google.golang.org/grpc v1.33.2
284 changes: 8 additions & 276 deletions go.sum

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions proto/interchain_security/ccv/v1/ccv.proto
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@ package interchain_security.ccv.v1;

option go_package = "github.com/cosmos/interchain-security/x/ccv/types";

import "cosmos/staking/v1beta1/staking.proto";

import "gogoproto/gogo.proto";
import "tendermint/abci/types.proto";

@@ -48,7 +50,8 @@ message UnbondingOp {
message SlashPacketData {
tendermint.abci.Validator validator = 1
[(gogoproto.nullable) = false, (gogoproto.moretags) = "yaml:\"validator\""];
int64 jail_time = 2;
int64 slash_fraction = 3;
uint64 valset_update_id = 4;
// map to the infraction block height on the provider
uint64 valset_update_id = 2;
// tell if the slashing is for a downtime or a double-signing infraction
cosmos.staking.v1beta1.InfractionType infraction = 3;
}
109 changes: 86 additions & 23 deletions x/ccv/consumer/keeper/keeper_test.go
Original file line number Diff line number Diff line change
@@ -3,10 +3,14 @@ package keeper_test
import (
"fmt"
"testing"
"time"

cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
sdk "github.com/cosmos/cosmos-sdk/types"
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
commitmenttypes "github.com/cosmos/ibc-go/v3/modules/core/23-commitment/types"
@@ -317,7 +321,7 @@ func (suite *KeeperTestSuite) TestVerifyProviderChain() {
}
}

// TestValidatorDowntime tests if a slashing packet is sent
// TestValidatorDowntime tests if a slash packet is sent
// and if the outstanding slashing flag is switched
// when a validator has downtime on the slashing module
func (suite *KeeperTestSuite) TestValidatorDowntime() {
@@ -336,7 +340,7 @@ func (suite *KeeperTestSuite) TestValidatorDowntime() {
// signInfo, found := app.SlashingKeeper.GetValidatorSigningInfo(ctx, consAddr)
// suite.Require().True(found)

// save next sequence before sending a slashing packet
// save next sequence before sending a slash packet
seq, ok := app.GetIBCKeeper().ChannelKeeper.GetNextSequenceSend(ctx, types.PortID, channelID)
suite.Require().True(ok)

@@ -347,7 +351,7 @@ func (suite *KeeperTestSuite) TestValidatorDowntime() {
ctx = ctx.WithBlockHeight(height)
app.SlashingKeeper.HandleValidatorSignature(ctx, vals[0].Address, valPower, true)
}
// Miss 500 blocks and expect a slashing packet to be sent
// Miss 500 blocks and expect a slash packet to be sent
for ; height < app.SlashingKeeper.SignedBlocksWindow(ctx)+(app.SlashingKeeper.SignedBlocksWindow(ctx)-app.SlashingKeeper.MinSignedPerWindow(ctx))+1; height++ {
ctx = ctx.WithBlockHeight(height)
app.SlashingKeeper.HandleValidatorSignature(ctx, vals[0].Address, valPower, false)
@@ -365,11 +369,11 @@ func (suite *KeeperTestSuite) TestValidatorDowntime() {
return false
})

// verify that the slashing packet was sent
// verify that the slash packet was sent
commit := app.IBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, types.PortID, channelID, seq)
suite.Require().NotNil(commit, "did not found slashing packet commitment")
suite.Require().NotNil(commit, "did not found slash packet commitment")

// verify that the slashing packet was sent
// verify that the slash packet was sent
suite.Require().True(app.ConsumerKeeper.OutstandingDowntime(ctx, consAddr))

// check that the outstanding slashing flag prevents
@@ -390,7 +394,54 @@ func (suite *KeeperTestSuite) TestValidatorDowntime() {
})
}

func (suite *KeeperTestSuite) TestPendingSlashRequestsLogic() {
// TestValidatorDoubleSigning tests if a slash packet is sent
// when a double-signing evidence is handled by the evidence module
func (suite *KeeperTestSuite) TestValidatorDoubleSigning() {
// initial setup
suite.SetupCCVChannel()
suite.SendEmptyVSCPacket()

app, ctx := suite.consumerChain.App.(*appConsumer.App), suite.consumerChain.GetContext()
channelID := suite.path.EndpointA.ChannelID

// create a validator pubkey and address
// note that the validator shouldn't necessarily be in the cross-chain validator states

pubkey := ed25519.GenPrivKey().PubKey()
consAddr := sdk.ConsAddress(pubkey.Address())

// create evidence
e := &evidencetypes.Equivocation{
Height: 1,
Power: 100,
Time: time.Now().UTC(),
ConsensusAddress: consAddr.String(),
}

// add validator signing-info to the store
signInfo := slashingtypes.ValidatorSigningInfo{
Address: consAddr.String(),
Tombstoned: false,
}
app.SlashingKeeper.SetValidatorSigningInfo(ctx, consAddr, signInfo)

// save next sequence before sending a slash packet
seq, ok := app.GetIBCKeeper().ChannelKeeper.GetNextSequenceSend(ctx, types.PortID, channelID)
suite.Require().True(ok)

// handles double-signing evidence
app.EvidenceKeeper.HandleEquivocationEvidence(ctx, e)

// check that slash packet is sent
commit := app.IBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, types.PortID, channelID, seq)
suite.NotNil(commit)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this check that packet commitment has expected value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. Now downtime and double-sign tests check the commit value using a reconstructed packet's hash.


// check evidence is stored
_, found := app.EvidenceKeeper.GetEvidence(ctx, e.Hash())
suite.True(found)
}

func (suite *KeeperTestSuite) TestSendSlashPacket() {
suite.SetupCCVChannel()

app := suite.consumerChain.App.(*appConsumer.App)
@@ -401,25 +452,35 @@ func (suite *KeeperTestSuite) TestPendingSlashRequestsLogic() {
_, ok := app.ConsumerKeeper.GetProviderChannel(ctx)
suite.Require().False(ok)

// expect to store 4 slash requests
validators := []abci.Validator{}
for i := 0; i < 4; i++ {
addr := ed25519.GenPrivKey().PubKey().Address()
val := abci.Validator{
Address: addr}
app.ConsumerKeeper.SendSlashPacket(ctx, val, 0, 0, 0)
validators = append(validators, val)
// expect to store 4 slash requests for downtime
// and 4 slash request for double-signing
type slashedVal struct {
validator abci.Validator
infraction stakingtypes.InfractionType
}
slashedVals := []slashedVal{}

infraction := stakingtypes.Downtime
for j := 0; j < 2; j++ {
for i := 0; i < 4; i++ {
addr := ed25519.GenPrivKey().PubKey().Address()
val := abci.Validator{
Address: addr}
app.ConsumerKeeper.SendSlashPacket(ctx, val, 0, infraction)
slashedVals = append(slashedVals, slashedVal{validator: val, infraction: infraction})
}
infraction = stakingtypes.DoubleSign
}

// expect to store a duplicate for each slash request
// in order to test the outstanding downtime logic
for _, v := range validators {
app.ConsumerKeeper.SendSlashPacket(ctx, v, 0, 0, 0)
for _, sv := range slashedVals {
app.ConsumerKeeper.SendSlashPacket(ctx, sv.validator, 0, sv.infraction)
}

// verify that all requests are stored
requests := app.ConsumerKeeper.GetPendingSlashRequests(ctx)
suite.Require().Len(requests, 8)
suite.Require().Len(requests, 16)

// save consumer next sequence
seq, _ := app.GetIBCKeeper().ChannelKeeper.GetNextSequenceSend(ctx, types.PortID, channelID)
@@ -428,17 +489,18 @@ func (suite *KeeperTestSuite) TestPendingSlashRequestsLogic() {
suite.SendEmptyVSCPacket()

// check that each pending slash requests is sent once
// and that the duplicates are skipped (due to the outstanding downtime flag)
for i := 0; i < 7; i++ {
// and that the downtime slash request duplicates are skipped (due to the outstanding downtime flag)
for i := 0; i < 16; i++ {
commit := app.IBCKeeper.ChannelKeeper.GetPacketCommitment(ctx, types.PortID, channelID, seq+uint64(i))
if i > 3 {
if i > 11 {
suite.Require().Nil(commit)
continue
}
suite.Require().NotNil(commit)
}

// check that outstanding downtime flags are all set to true
// check that outstanding downtime flags
// are all set to true for validators slashed for downtime requests
for _, r := range requests {
if r.Downtime {
consAddr := sdk.ConsAddress(r.Packet.Validator.Address)
@@ -451,7 +513,8 @@ func (suite *KeeperTestSuite) TestPendingSlashRequestsLogic() {
suite.Require().Len(requests, 0)

// check that slash requests aren't stored when channel is established
app.ConsumerKeeper.SendSlashPacket(ctx, abci.Validator{}, 0, 0, 0)
app.ConsumerKeeper.SendSlashPacket(ctx, abci.Validator{}, 0, stakingtypes.Downtime)
app.ConsumerKeeper.SendSlashPacket(ctx, abci.Validator{}, 0, stakingtypes.DoubleSign)

requests = app.ConsumerKeeper.GetPendingSlashRequests(ctx)
suite.Require().Len(requests, 0)
20 changes: 12 additions & 8 deletions x/ccv/consumer/keeper/relay.go
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
host "github.com/cosmos/ibc-go/v3/modules/core/24-host"
@@ -103,24 +104,25 @@ func (k Keeper) UnbondMaturePackets(ctx sdk.Context) error {
}

// SendSlashPacket sends a slash packet containing the given validator data and slashing info
func (k Keeper) SendSlashPacket(ctx sdk.Context, validator abci.Validator, valsetUpdateID uint64, slashFraction, jailedUntil int64) {
func (k Keeper) SendSlashPacket(ctx sdk.Context, validator abci.Validator, valsetUpdateID uint64, infraction stakingtypes.InfractionType) {
consAddr := sdk.ConsAddress(validator.Address)
downtime := infraction == stakingtypes.Downtime

// return if an outstanding downtime request is set for the validator
consAddr := sdk.ConsAddress(validator.Address)
if k.OutstandingDowntime(ctx, consAddr) { // TODO: add to condition if the slash is for downtime
if downtime && k.OutstandingDowntime(ctx, consAddr) {
return
}

// construct slash packet data
packetData := ccv.NewSlashPacketData(validator, valsetUpdateID, slashFraction, jailedUntil)
packetData := ccv.NewSlashPacketData(validator, valsetUpdateID, infraction)

// check that provider channel is established
// if not, append slashing packet to pending slash requests
channelID, ok := k.GetProviderChannel(ctx)
if !ok {
k.AppendPendingSlashRequests(ctx, types.SlashRequest{
Packet: &packetData,
Downtime: true}, // TODO Simon: add double-signing check
Downtime: downtime},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's odd here to use a boolean to determine what type of slash it is, where everywhere else you use an enum.

I suggest that this gets changed to be the enum value as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. PendingSlashRequest msg definition updated accordingly.

)
return
}
@@ -155,8 +157,10 @@ func (k Keeper) SendSlashPacket(ctx sdk.Context, validator abci.Validator, valse
panic(err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be replaced with sendIBCPacket util introduced in #86?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea sure. Thanks for pointing this out.

}

// set outstanding downtime flag for the validator
k.SetOutstandingDowntime(ctx, consAddr) // TODO: do this only if the slash is for downtime
// set outstanding downtime if slash request sent is for downtime
if downtime {
k.SetOutstandingDowntime(ctx, consAddr)
}
}

// SendPendingSlashRequests iterates over the stored pending slash requests in reverse order
@@ -205,7 +209,7 @@ func (k Keeper) SendPendingSlashRequests(ctx sdk.Context) {
}

// set validator outstanding downtime flag to true
if slashReq.Downtime { // TODO: add to condition if the slash is for downtime
if slashReq.Downtime {
k.SetOutstandingDowntime(ctx, sdk.ConsAddress(slashReq.Packet.Validator.Address))
}
}
3 changes: 2 additions & 1 deletion x/ccv/consumer/keeper/relay_test.go
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import (

cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
clienttypes "github.com/cosmos/ibc-go/v3/modules/core/02-client/types"
channeltypes "github.com/cosmos/ibc-go/v3/modules/core/04-channel/types"
appConsumer "github.com/cosmos/interchain-security/app/consumer"
@@ -246,7 +247,7 @@ func (suite *KeeperTestSuite) TestUnbondMaturePackets() {

func (suite *KeeperTestSuite) TestOnAcknowledgement() {
packetData := types.NewSlashPacketData(
abci.Validator{Address: bytes.HexBytes{}, Power: int64(1)}, uint64(1), int64(4), int64(1),
abci.Validator{Address: bytes.HexBytes{}, Power: int64(1)}, uint64(1), stakingtypes.Downtime,
)

packet := channeltypes.NewPacket(packetData.GetBytes(), 1, providertypes.PortID, suite.path.EndpointB.ChannelID,
Loading