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

ibc: async acknowledgements #7361

Merged
merged 24 commits into from
Oct 1, 2020
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
12 changes: 6 additions & 6 deletions proto/ibc/lightclients/solomachine/v1/solomachine.proto
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ message ClientState {
message ConsensusState {
option (gogoproto.goproto_getters) = false;
// public key of the solo machine
google.protobuf.Any public_key = 1 [(gogoproto.moretags) = "yaml:\"public_key\""];
google.protobuf.Any public_key = 1 [(gogoproto.moretags) = "yaml:\"public_key\""];
// diversifier allows the same public key to be re-used across different solo machine clients
// (potentially on different chains) without being considered misbehaviour.
string diversifier = 2;
Expand All @@ -39,11 +39,11 @@ message ConsensusState {
message Header {
option (gogoproto.goproto_getters) = false;
// sequence to update solo machine public key at
uint64 sequence = 1;
uint64 timestamp = 2;
bytes signature = 3;
uint64 sequence = 1;
uint64 timestamp = 2;
bytes signature = 3;
google.protobuf.Any new_public_key = 4 [(gogoproto.moretags) = "yaml:\"new_public_key\""];
string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""];
string new_diversifier = 5 [(gogoproto.moretags) = "yaml:\"new_diversifier\""];
}

// Misbehaviour defines misbehaviour for a solo machine which consists
Expand Down Expand Up @@ -143,7 +143,7 @@ message PacketAcknowledgementData {
bytes acknowledgement = 2;
}

// PacketAcknowledgementAbsenceSignBytes returns the SignBytes data for
// PacketReceiptAbsenceSignBytes returns the SignBytes data for
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
// acknowledgement absence verification.
message PacketAcknowledgementAbsenseData {
bytes path = 1;
Expand Down
2 changes: 1 addition & 1 deletion x/ibc-transfer/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (k Keeper) ReceiveExecuted(ctx sdk.Context, packet ibcexported.PacketI, ack
if !ok {
return sdkerrors.Wrap(channeltypes.ErrChannelCapabilityNotFound, "channel capability could not be retrieved for packet")
}
return k.channelKeeper.ReceiveExecuted(ctx, chanCap, packet, acknowledgement)
return k.channelKeeper.ReceiveExecuted(ctx, chanCap, packet)
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
}

// ChanCloseInit defines a wrapper function for the channel Keeper's function
Expand Down
3 changes: 2 additions & 1 deletion x/ibc-transfer/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ type ChannelKeeper interface {
GetChannel(ctx sdk.Context, srcPort, srcChan string) (channel channeltypes.Channel, found bool)
GetNextSequenceSend(ctx sdk.Context, portID, channelID string) (uint64, bool)
SendPacket(ctx sdk.Context, channelCap *capabilitytypes.Capability, packet ibcexported.PacketI) error
ReceiveExecuted(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, acknowledgement []byte) error
ReceiveExecuted(ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI) error
WriteAcknowledgement(ctx sdk.Context, packet ibcexported.PacketI, acknowledgement []byte) error
ChanCloseInit(ctx sdk.Context, portID, channelID string, chanCap *capabilitytypes.Capability) error
}

Expand Down
2 changes: 1 addition & 1 deletion x/ibc/02-client/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var (
ErrFailedChannelStateVerification = sdkerrors.Register(SubModuleName, 16, "channel state verification failed")
ErrFailedPacketCommitmentVerification = sdkerrors.Register(SubModuleName, 17, "packet commitment verification failed")
ErrFailedPacketAckVerification = sdkerrors.Register(SubModuleName, 18, "packet acknowledgement verification failed")
ErrFailedPacketAckAbsenceVerification = sdkerrors.Register(SubModuleName, 19, "packet acknowledgement absence verification failed")
ErrFailedPacketReceiptVerification = sdkerrors.Register(SubModuleName, 19, "packet receipt verification failed")
ErrFailedNextSeqRecvVerification = sdkerrors.Register(SubModuleName, 20, "next sequence receive verification failed")
ErrSelfConsensusStateNotFound = sdkerrors.Register(SubModuleName, 21, "self consensus state not found")
ErrUpdateClientFailed = sdkerrors.Register(SubModuleName, 22, "unable to update light client")
Expand Down
6 changes: 3 additions & 3 deletions x/ibc/03-connection/keeper/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,10 @@ func (k Keeper) VerifyPacketAcknowledgement(
return nil
}

// VerifyPacketAcknowledgementAbsence verifies a proof of the absence of an
// VerifyPacketReceiptAbsence verifies a proof of the absence of an
// incoming packet acknowledgement at the specified port, specified channel, and
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
// specified sequence.
func (k Keeper) VerifyPacketAcknowledgementAbsence(
func (k Keeper) VerifyPacketReceiptAbsence(
ctx sdk.Context,
connection exported.ConnectionI,
height exported.Height,
Expand All @@ -192,7 +192,7 @@ func (k Keeper) VerifyPacketAcknowledgementAbsence(
return sdkerrors.Wrap(clienttypes.ErrClientNotFound, connection.GetClientID())
}

if err := clientState.VerifyPacketAcknowledgementAbsence(
if err := clientState.VerifyPacketReceiptAbsence(
k.clientKeeper.ClientStore(ctx, connection.GetClientID()), k.cdc, height,
connection.GetCounterparty().GetPrefix(), proof, portID, channelID,
sequence,
Expand Down
6 changes: 3 additions & 3 deletions x/ibc/03-connection/keeper/verify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,10 +361,10 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgement() {
}
}

// TestVerifyPacketAcknowledgementAbsence has chainA verify the acknowledgement
// TestVerifyPacketReceiptAbsence has chainA verify the acknowledgement
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
// absence on channelB. The channels on chainA and chainB are fully opened and
// a packet is sent from chainA to chainB and not received.
func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgementAbsence() {
func (suite *KeeperTestSuite) TestVerifyPacketReceiptAbsence() {
cases := []struct {
msg string
changeClientID bool
Expand Down Expand Up @@ -407,7 +407,7 @@ func (suite *KeeperTestSuite) TestVerifyPacketAcknowledgementAbsence() {
packetAckKey := host.KeyPacketAcknowledgement(packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
proof, proofHeight := suite.chainB.QueryProof(packetAckKey)

err = suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyPacketAcknowledgementAbsence(
err = suite.chainA.App.IBCKeeper.ConnectionKeeper.VerifyPacketReceiptAbsence(
suite.chainA.GetContext(), connection, malleateHeight(proofHeight, tc.heightDiff), proof,
packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
)
Expand Down
17 changes: 17 additions & 0 deletions x/ibc/04-channel/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,23 @@ func (k Keeper) SetNextSequenceAck(ctx sdk.Context, portID, channelID string, se
store.Set(host.KeyNextSequenceAck(portID, channelID), bz)
}

// GetPacketReceipt gets a packet receipt from the store
func (k Keeper) GetPacketReceipt(ctx sdk.Context, portID, channelID string, sequence uint64) (string, bool) {
store := ctx.KVStore(k.storeKey)
bz := store.Get(host.KeyPacketReceipt(portID, channelID, sequence))
if bz == nil {
return "", false
}

return string(bz), true
}

// SetPacketReceipt sets an empty packet receipt to the store
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
func (k Keeper) SetPacketReceipt(ctx sdk.Context, portID, channelID string, sequence uint64) {
store := ctx.KVStore(k.storeKey)
store.Set(host.KeyPacketReceipt(portID, channelID, sequence), []byte(""))
}

// GetPacketCommitment gets the packet commitment hash from the store
func (k Keeper) GetPacketCommitment(ctx sdk.Context, portID, channelID string, sequence uint64) []byte {
store := ctx.KVStore(k.storeKey)
Expand Down
70 changes: 55 additions & 15 deletions x/ibc/04-channel/keeper/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"github.com/cosmos/cosmos-sdk/x/ibc/exported"
)

const emptyReceipt = ""

// SendPacket is called by a module in order to send an IBC packet on a channel
// end owned by the calling module to the corresponding module on the counterparty
// chain.
Expand Down Expand Up @@ -242,15 +244,14 @@ func (k Keeper) RecvPacket(
return nil
}

// ReceiveExecuted writes the packet execution acknowledgement to the state,
// which will be verified by the counterparty chain using AcknowledgePacket.
// ReceiveExecuted updates the receive sequence in the case of an ordered channel or sets an empty receipt
// if the channel is unordered.
//
// CONTRACT: this function must be called in the IBC handler
func (k Keeper) ReceiveExecuted(
ctx sdk.Context,
chanCap *capabilitytypes.Capability,
packet exported.PacketI,
acknowledgement []byte,
) error {
channel, found := k.GetChannel(ctx, packet.GetDestPort(), packet.GetDestChannel())
if !found {
Expand All @@ -273,16 +274,6 @@ func (k Keeper) ReceiveExecuted(
)
}

if len(acknowledgement) == 0 {
return sdkerrors.Wrap(types.ErrInvalidAcknowledgement, "acknowledgement cannot be empty")
}

// always set the acknowledgement so that it can be verified on the other side
k.SetPacketAcknowledgement(
ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
types.CommitAcknowledgement(acknowledgement),
)

if channel.Ordering == types.ORDERED {
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
nextSequenceRecv, found := k.GetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel())
if !found {
Expand All @@ -297,17 +288,29 @@ func (k Keeper) ReceiveExecuted(
// incrementng nextSequenceRecv and storing under this chain's channelEnd identifiers
// Since this is the receiving chain, our channelEnd is packet's destination port and channel
k.SetNextSequenceRecv(ctx, packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv)
} else {
// For unordered channels we must set the receipt so it can be verified on the other side.
// This receipt does not contain any data, since the packet has not yet been processed,
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
// it's just a single store key set to an empty string to indicate that the packet has been received
_, found := k.GetPacketReceipt(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
if found {
return sdkerrors.Wrapf(
types.ErrPacketReceived,
"destination port: %s, destination channel: %s, sequence: %d", packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
)
}

k.SetPacketReceipt(ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence())
}

// log that a packet has been received & executed
k.Logger(ctx).Info(fmt.Sprintf("packet received & executed: %v", packet))
k.Logger(ctx).Info("packet received and executed", "packet", fmt.Sprintf("%v", packet))
fedekunze marked this conversation as resolved.
Show resolved Hide resolved

// emit an event that the relayer can query for
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeRecvPacket,
sdk.NewAttribute(types.AttributeKeyData, string(packet.GetData())),
sdk.NewAttribute(types.AttributeKeyAck, string(acknowledgement)),
sdk.NewAttribute(types.AttributeKeyTimeoutHeight, packet.GetTimeoutHeight().String()),
sdk.NewAttribute(types.AttributeKeyTimeoutTimestamp, fmt.Sprintf("%d", packet.GetTimeoutTimestamp())),
sdk.NewAttribute(types.AttributeKeySequence, fmt.Sprintf("%d", packet.GetSequence())),
Expand All @@ -326,6 +329,43 @@ func (k Keeper) ReceiveExecuted(
return nil
}

// WriteAcknowledgement writes the packet execution acknowledgement to the state,
// which will be verified by the counterparty chain using AcknowledgePacket.
//
// CONTRACT: this function must be called in the IBC handler
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
func (k Keeper) WriteAcknowledgement(
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
ctx sdk.Context,
packet exported.PacketI,
acknowledgement []byte,
) error {
if len(acknowledgement) == 0 {
return sdkerrors.Wrap(types.ErrInvalidAcknowledgement, "acknowledgement cannot be empty")
}

// always set the acknowledgement so that it can be verified on the other side
k.SetPacketAcknowledgement(
ctx, packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
types.CommitAcknowledgement(acknowledgement),
)

// log that a packet has been acknowledged
k.Logger(ctx).Info("packet acknowledged", "packet", fmt.Sprintf("%v", packet))

// emit an event that the relayer can query for
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeRecvPacket,
sdk.NewAttribute(types.AttributeKeyAck, string(acknowledgement)),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
),
})

return nil
}

// AcknowledgePacket is called by a module to process the acknowledgement of a
// packet previously sent by the calling module on a channel to a counterparty
// module on the counterparty chain. Its intended usage is within the ante
Expand Down
2 changes: 1 addition & 1 deletion x/ibc/04-channel/keeper/packet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ func (suite *KeeperTestSuite) TestReceiveExecuted() {

tc.malleate()

err := suite.chainB.App.IBCKeeper.ChannelKeeper.ReceiveExecuted(suite.chainB.GetContext(), channelCap, packet, ack)
err := suite.chainB.App.IBCKeeper.ChannelKeeper.ReceiveExecuted(suite.chainB.GetContext(), channelCap, packet)

if tc.expPass {
suite.Require().NoError(err)
Expand Down
4 changes: 2 additions & 2 deletions x/ibc/04-channel/keeper/timeout.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func (k Keeper) TimeoutPacket(
packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv,
)
case types.UNORDERED:
err = k.connectionKeeper.VerifyPacketAcknowledgementAbsence(
err = k.connectionKeeper.VerifyPacketReceiptAbsence(
ctx, connectionEnd, proofHeight, proof,
packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
)
Expand Down Expand Up @@ -255,7 +255,7 @@ func (k Keeper) TimeoutOnClose(
packet.GetDestPort(), packet.GetDestChannel(), nextSequenceRecv,
)
case types.UNORDERED:
err = k.connectionKeeper.VerifyPacketAcknowledgementAbsence(
err = k.connectionKeeper.VerifyPacketReceiptAbsence(
ctx, connectionEnd, proofHeight, proof,
packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
)
Expand Down
1 change: 1 addition & 0 deletions x/ibc/04-channel/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ var (
ErrAcknowledgementTooLong = sdkerrors.Register(SubModuleName, 16, "acknowledgement too long")
ErrInvalidAcknowledgement = sdkerrors.Register(SubModuleName, 17, "invalid acknowledgement")
ErrPacketCommitmentNotFound = sdkerrors.Register(SubModuleName, 18, "packet commitment not found")
ErrPacketReceived = sdkerrors.Register(SubModuleName, 19, "packet receipt already received")
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
)
2 changes: 1 addition & 1 deletion x/ibc/04-channel/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type ConnectionKeeper interface {
sequence uint64,
acknowledgement []byte,
) error
VerifyPacketAcknowledgementAbsence(
VerifyPacketReceiptAbsence(
ctx sdk.Context,
connection exported.ConnectionI,
height exported.Height,
Expand Down
6 changes: 3 additions & 3 deletions x/ibc/07-tendermint/types/client_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,10 @@ func (cs ClientState) VerifyPacketAcknowledgement(
return nil
}

// VerifyPacketAcknowledgementAbsence verifies a proof of the absence of an
// VerifyPacketReceiptAbsence verifies a proof of the absence of an
// incoming packet acknowledgement at the specified port, specified channel, and
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
// specified sequence.
func (cs ClientState) VerifyPacketAcknowledgementAbsence(
func (cs ClientState) VerifyPacketReceiptAbsence(
store sdk.KVStore,
cdc codec.BinaryMarshaler,
height exported.Height,
Expand All @@ -361,7 +361,7 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence(
return err
}

path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketAcknowledgementPath(portID, channelID, sequence))
path, err := commitmenttypes.ApplyPrefix(prefix, host.PacketReceiptPath(portID, channelID, sequence))
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions x/ibc/07-tendermint/types/client_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgement() {
// test verification of the absent acknowledgement on chainB being represented
// in the light client on chainA. A send from chainB to chainA is simulated, but
// no receive.
func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() {
func (suite *TendermintTestSuite) TestVerifyPacketReceiptAbsence() {
var (
clientState *types.ClientState
proof []byte
Expand Down Expand Up @@ -565,7 +565,7 @@ func (suite *TendermintTestSuite) TestVerifyPacketAcknowledgementAbsence() {

store := suite.chainA.App.IBCKeeper.ClientKeeper.ClientStore(suite.chainA.GetContext(), clientA)

err = clientState.VerifyPacketAcknowledgementAbsence(
err = clientState.VerifyPacketReceiptAbsence(
store, suite.chainA.Codec, proofHeight, &prefix, proof,
packet.GetDestPort(), packet.GetDestChannel(), packet.GetSequence(),
)
Expand Down
10 changes: 5 additions & 5 deletions x/ibc/09-localhost/types/client_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,10 @@ func (cs ClientState) VerifyPacketAcknowledgement(
return nil
}

// VerifyPacketAcknowledgementAbsence verifies a proof of the absence of an
// incoming packet acknowledgement at the specified port, specified channel, and
// VerifyPacketReceiptAbsence verifies a proof of the absence of an
// incoming packet receipt at the specified port, specified channel, and
// specified sequence.
func (cs ClientState) VerifyPacketAcknowledgementAbsence(
func (cs ClientState) VerifyPacketReceiptAbsence(
store sdk.KVStore,
_ codec.BinaryMarshaler,
_ exported.Height,
Expand All @@ -270,11 +270,11 @@ func (cs ClientState) VerifyPacketAcknowledgementAbsence(
channelID string,
sequence uint64,
) error {
path := host.KeyPacketAcknowledgement(portID, channelID, sequence)
path := host.KeyPacketReceipt(portID, channelID, sequence)

data := store.Get(path)
if data != nil {
return sdkerrors.Wrap(clienttypes.ErrFailedPacketAckAbsenceVerification, "expected no ack absence")
return sdkerrors.Wrap(clienttypes.ErrFailedPacketReceiptVerification, "expected no packet receipt")
}

return nil
Expand Down
6 changes: 3 additions & 3 deletions x/ibc/09-localhost/types/client_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,18 +407,18 @@ func (suite *LocalhostTestSuite) TestVerifyPacketAcknowledgement() {
}
}

func (suite *LocalhostTestSuite) TestVerifyPacketAcknowledgementAbsence() {
func (suite *LocalhostTestSuite) TestVerifyPacketReceiptAbsence() {
clientState := types.NewClientState("chainID", clientHeight)

err := clientState.VerifyPacketAcknowledgementAbsence(
err := clientState.VerifyPacketReceiptAbsence(
suite.store, suite.cdc, clientHeight, nil, nil, testPortID, testChannelID, testSequence,
)

suite.Require().NoError(err, "ack absence failed")
fedekunze marked this conversation as resolved.
Show resolved Hide resolved

suite.store.Set(host.KeyPacketAcknowledgement(testPortID, testChannelID, testSequence), []byte("ack"))

err = clientState.VerifyPacketAcknowledgementAbsence(
err = clientState.VerifyPacketReceiptAbsence(
suite.store, suite.cdc, clientHeight, nil, nil, testPortID, testChannelID, testSequence,
)
suite.Require().Error(err, "ack exists in store")
fedekunze marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
Loading