-
Notifications
You must be signed in to change notification settings - Fork 122
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
fix: make SlashPacketData backward compatible when sending data over the wire #1093
Changes from 3 commits
f6a0285
daca511
9467103
ebc0824
5acd85a
a3026b5
619297d
10faf0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -175,28 +175,49 @@ func (am AppModule) OnRecvPacket( | |
_ sdk.AccAddress, | ||
) ibcexported.Acknowledgement { | ||
var ( | ||
ack ibcexported.Acknowledgement | ||
consumerPacket ccv.ConsumerPacketData | ||
ack ibcexported.Acknowledgement | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changes in this functions are important to check |
||
consumerPacket ccv.ConsumerPacketData | ||
shaspitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
consumerPacketV1 ccv.ConsumerPacketDataV1 | ||
isV1Packet bool | ||
) | ||
|
||
// unmarshall consumer packet | ||
if err := ccv.ModuleCdc.UnmarshalJSON(packet.GetData(), &consumerPacket); err != nil { | ||
errAck := channeltypes.NewErrorAcknowledgement(fmt.Errorf("cannot unmarshal CCV packet data")) | ||
ack = &errAck | ||
} else { | ||
// TODO: call ValidateBasic method on consumer packet data | ||
// See: https://github.com/cosmos/interchain-security/issues/634 | ||
|
||
switch consumerPacket.Type { | ||
case ccv.VscMaturedPacket: | ||
// handle VSCMaturedPacket | ||
ack = am.keeper.OnRecvVSCMaturedPacket(ctx, packet, *consumerPacket.GetVscMaturedPacketData()) | ||
case ccv.SlashPacket: | ||
// handle SlashPacket | ||
ack = am.keeper.OnRecvSlashPacket(ctx, packet, *consumerPacket.GetSlashPacketData()) | ||
default: | ||
errAck := channeltypes.NewErrorAcknowledgement(fmt.Errorf("invalid consumer packet type: %q", consumerPacket.Type)) | ||
// retry for v1 packet type | ||
errV1 := ccv.ModuleCdc.UnmarshalJSON(packet.GetData(), &consumerPacketV1) | ||
if errV1 != nil { | ||
errAck := channeltypes.NewErrorAcknowledgement(fmt.Errorf("cannot unmarshal CCV packet data")) | ||
shaspitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ack = &errAck | ||
|
||
ctx.EventManager().EmitEvent( | ||
sdk.NewEvent( | ||
ccv.EventTypePacket, | ||
sdk.NewAttribute(sdk.AttributeKeyModule, providertypes.ModuleName), | ||
sdk.NewAttribute(ccv.AttributeKeyAckSuccess, "true"), | ||
), | ||
) | ||
return ack | ||
} | ||
isV1Packet = true | ||
} | ||
|
||
// TODO: call ValidateBasic method on consumer packet data | ||
// See: https://github.com/cosmos/interchain-security/issues/634 | ||
|
||
switch consumerPacket.Type { | ||
case ccv.VscMaturedPacket: | ||
// handle VSCMaturedPacket | ||
ack = am.keeper.OnRecvVSCMaturedPacket(ctx, packet, *consumerPacket.GetVscMaturedPacketData()) | ||
case ccv.SlashPacket: | ||
shaspitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// handle SlashPacket | ||
if isV1Packet { | ||
ack = am.keeper.OnRecvSlashPacket(ctx, packet, *consumerPacketV1.GetSlashPacketData().FromV1()) | ||
} else { | ||
ack = am.keeper.OnRecvSlashPacket(ctx, packet, *consumerPacket.GetSlashPacketData()) | ||
} | ||
default: | ||
errAck := channeltypes.NewErrorAcknowledgement(fmt.Errorf("invalid consumer packet type: %q", consumerPacket.Type)) | ||
shaspitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ack = &errAck | ||
} | ||
|
||
ctx.EventManager().EmitEvent( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -400,6 +400,26 @@ func (k Keeper) ValidateSlashPacket(ctx sdk.Context, chainID string, | |
return nil | ||
} | ||
|
||
// ValidateV1SlashPacket validates a recv slash packet compatible with v1 before it is | ||
// handled or persisted in store. An error is returned if the packet is invalid, | ||
// and an error ack should be relayed to the sender. | ||
func (k Keeper) ValidateV1SlashPacket(ctx sdk.Context, chainID string, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is never called. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||
packet channeltypes.Packet, data ccv.SlashPacketDataV1, | ||
) error { | ||
_, found := k.getMappedInfractionHeight(ctx, chainID, data.ValsetUpdateId) | ||
// return error if we cannot find infraction height matching the validator update id | ||
if !found { | ||
return fmt.Errorf("cannot find infraction height matching "+ | ||
"the validator update id %d for chain %s", data.ValsetUpdateId, chainID) | ||
} | ||
|
||
if data.Infraction != ccv.DoubleSign && data.Infraction != ccv.Downtime { | ||
return fmt.Errorf("invalid infraction type: %s", data.Infraction) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// HandleSlashPacket potentially jails a misbehaving validator for a downtime infraction. | ||
// This method should NEVER be called with a double-sign infraction. | ||
func (k Keeper) HandleSlashPacket(ctx sdk.Context, chainID string, data ccv.SlashPacketData) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,6 +59,23 @@ func NewSlashPacketData(validator abci.Validator, valUpdateId uint64, infraction | |
} | ||
} | ||
|
||
// NewSlashPacketDataV1 creates a new SlashPacketDataV1 that uses ccv.InfractionTypes to maintain backward compatibility. | ||
func NewSlashPacketDataV1(validator abci.Validator, valUpdateId uint64, infractionType stakingtypes.Infraction) *SlashPacketDataV1 { | ||
v1Type := InfractionEmpty | ||
switch infractionType { | ||
case stakingtypes.Infraction_INFRACTION_DOWNTIME: | ||
v1Type = Downtime | ||
case stakingtypes.Infraction_INFRACTION_DOUBLE_SIGN: | ||
v1Type = DoubleSign | ||
} | ||
|
||
return &SlashPacketDataV1{ | ||
Validator: validator, | ||
ValsetUpdateId: valUpdateId, | ||
Infraction: v1Type, | ||
} | ||
} | ||
|
||
func (vdt SlashPacketData) ValidateBasic() error { | ||
if len(vdt.Validator.Address) == 0 || vdt.Validator.Power == 0 { | ||
return errorsmod.Wrap(ErrInvalidPacketData, "validator fields cannot be empty") | ||
|
@@ -76,6 +93,10 @@ func (vdt SlashPacketData) GetBytes() []byte { | |
return valDowntimeBytes | ||
} | ||
|
||
func (vdt SlashPacketData) ToV1() *SlashPacketDataV1 { | ||
return NewSlashPacketDataV1(vdt.Validator, vdt.ValsetUpdateId, vdt.Infraction) | ||
} | ||
|
||
func (cp ConsumerPacketData) ValidateBasic() (err error) { | ||
switch cp.Type { | ||
case VscMaturedPacket: | ||
|
@@ -99,7 +120,45 @@ func (cp ConsumerPacketData) ValidateBasic() (err error) { | |
return | ||
} | ||
|
||
// Convert to bytes while maintaining over the wire compatibility with previous versions. | ||
func (cp ConsumerPacketData) GetBytes() []byte { | ||
bytes := ModuleCdc.MustMarshalJSON(&cp) | ||
return cp.ToV1Bytes() | ||
shaspitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// ToV1Bytes converts the ConsumerPacketData to JSON byte array compatible | ||
// with the format used by ICS versions using cosmos-sdk v45 (ICS v1 and ICS v2). | ||
func (cp ConsumerPacketData) ToV1Bytes() []byte { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Important to check and inspired by Shawn's solution from #1089 |
||
if cp.Type != SlashPacket { | ||
bytes := ModuleCdc.MustMarshalJSON(&cp) | ||
return bytes | ||
} | ||
|
||
sp := cp.GetSlashPacketData() | ||
spdv1 := NewSlashPacketDataV1(sp.Validator, sp.ValsetUpdateId, sp.Infraction) | ||
cpv1 := ConsumerPacketDataV1{ | ||
Type: cp.Type, | ||
Data: &ConsumerPacketDataV1_SlashPacketData{ | ||
SlashPacketData: spdv1, | ||
}, | ||
} | ||
bytes := ModuleCdc.MustMarshalJSON(&cpv1) | ||
return bytes | ||
} | ||
|
||
// FromV1 converts SlashPacketDataV1 to SlashPacketData. | ||
// Provider must handle both V1 and later versions of the SlashPacketData. | ||
func (vdt1 SlashPacketDataV1) FromV1() *SlashPacketData { | ||
newType := stakingtypes.Infraction_INFRACTION_UNSPECIFIED | ||
switch vdt1.Infraction { | ||
case Downtime: | ||
newType = stakingtypes.Infraction_INFRACTION_DOWNTIME | ||
case DoubleSign: | ||
newType = stakingtypes.Infraction_INFRACTION_DOUBLE_SIGN | ||
} | ||
|
||
return &SlashPacketData{ | ||
Validator: vdt1.Validator, | ||
ValsetUpdateId: vdt1.ValsetUpdateId, | ||
Infraction: newType, | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All tests were changed to support V1 data packets. The consumer will
always
send packets in V1 format over the wire