-
Notifications
You must be signed in to change notification settings - Fork 673
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
feat: add acknowledgePacket handler to channel/v2 #7412
Changes from all commits
6a12e77
288f2d3
f56cd9f
11980bc
f2ca8a5
45d1e75
48231e4
dc1fc34
a7a6734
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 |
---|---|---|
|
@@ -75,16 +75,27 @@ func (k *Keeper) GetCounterparty(ctx context.Context, clientID string) (types.Co | |
} | ||
|
||
// GetPacketReceipt returns the packet receipt from the packet receipt path based on the sourceID and sequence. | ||
func (k *Keeper) GetPacketReceipt(ctx context.Context, sourceID string, sequence uint64) (string, bool) { | ||
func (k *Keeper) GetPacketReceipt(ctx context.Context, sourceID string, sequence uint64) ([]byte, bool) { | ||
store := k.storeService.OpenKVStore(ctx) | ||
bz, err := store.Get(hostv2.PacketReceiptKey(sourceID, sequence)) | ||
if err != nil { | ||
panic(err) | ||
} | ||
if len(bz) == 0 { | ||
return "", false | ||
return nil, false | ||
} | ||
return string(bz), true | ||
return bz, true | ||
} | ||
|
||
// HasPacketRceipt returns true if the packet receipt exists, otherwise false. | ||
func (k *Keeper) HasPacketReceipt(ctx context.Context, sourceID string, sequence uint64) bool { | ||
store := k.storeService.OpenKVStore(ctx) | ||
has, err := store.Has(hostv2.PacketReceiptKey(sourceID, sequence)) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
return has | ||
} | ||
|
||
// SetPacketReceipt writes the packet receipt under the receipt path | ||
|
@@ -117,16 +128,16 @@ func (k *Keeper) HasPacketAcknowledgement(ctx context.Context, sourceID string, | |
} | ||
|
||
// GetPacketCommitment returns the packet commitment hash under the commitment path. | ||
func (k *Keeper) GetPacketCommitment(ctx context.Context, sourceID string, sequence uint64) (string, bool) { | ||
func (k *Keeper) GetPacketCommitment(ctx context.Context, sourceID string, sequence uint64) []byte { | ||
store := k.storeService.OpenKVStore(ctx) | ||
bz, err := store.Get(hostv2.PacketCommitmentKey(sourceID, sequence)) | ||
if err != nil { | ||
panic(err) | ||
} | ||
if len(bz) == 0 { | ||
return "", false | ||
return nil | ||
} | ||
return string(bz), true | ||
return bz | ||
} | ||
|
||
// SetPacketCommitment writes the commitment hash under the commitment path. | ||
|
@@ -192,3 +203,15 @@ func (k *Keeper) AliasV1Channel(ctx context.Context, portID, channelID string) ( | |
} | ||
return counterparty, true | ||
} | ||
|
||
// getV1Counterparty attempts to retrieve a v1 channel from the channel keeper if it exists, then converts it | ||
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. moved from relay.go to keeper.go |
||
// to a v2 counterparty and stores it in the v2 channel keeper for future use | ||
func (k *Keeper) getV1Counterparty(ctx context.Context, port, id string) (types.Counterparty, bool) { | ||
if counterparty, ok := k.AliasV1Channel(ctx, port, id); ok { | ||
// we can key on just the channel here since channel ids are globally unique | ||
k.SetCounterparty(ctx, id, counterparty) | ||
return counterparty, true | ||
} | ||
|
||
return types.Counterparty{}, false | ||
} |
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. renamed relay.go to packet.go |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,18 +17,6 @@ import ( | |
"github.com/cosmos/ibc-go/v9/modules/core/packet-server/types" | ||
) | ||
|
||
// getV1Counterparty attempts to retrieve a v1 channel from the channel keeper if it exists, then converts it | ||
// to a v2 counterparty and stores it in the v2 channel keeper for future use | ||
func (k *Keeper) getV1Counterparty(ctx context.Context, port, id string) (channeltypesv2.Counterparty, bool) { | ||
if counterparty, ok := k.AliasV1Channel(ctx, port, id); ok { | ||
// we can key on just the channel here since channel ids are globally unique | ||
k.SetCounterparty(ctx, id, counterparty) | ||
return counterparty, true | ||
} | ||
|
||
return channeltypesv2.Counterparty{}, false | ||
} | ||
|
||
// sendPacket constructs a packet from the input arguments, writes a packet commitment to state | ||
// in order for the packet to be sent to the counterparty. | ||
func (k *Keeper) sendPacket( | ||
|
@@ -106,7 +94,7 @@ func (k *Keeper) sendPacket( | |
// The packet handler will verify that the packet has not timed out and that the | ||
// counterparty stored a packet commitment. If successful, a packet receipt is stored | ||
// to indicate to the counterparty successful delivery. | ||
func (k Keeper) recvPacket( | ||
func (k *Keeper) recvPacket( | ||
ctx context.Context, | ||
packet channeltypesv2.Packet, | ||
proof []byte, | ||
|
@@ -137,8 +125,7 @@ func (k Keeper) recvPacket( | |
// REPLAY PROTECTION: Packet receipts will indicate that a packet has already been received | ||
// on unordered channels. Packet receipts must not be pruned, unless it has been marked stale | ||
// by the increase of the recvStartSequence. | ||
_, found := k.GetPacketReceipt(ctx, packet.DestinationChannel, packet.Sequence) | ||
if found { | ||
if k.HasPacketReceipt(ctx, packet.DestinationChannel, packet.Sequence) { | ||
EmitRecvPacketEvents(ctx, packet) | ||
// This error indicates that the packet has already been relayed. Core IBC will | ||
// treat this error as a no-op in order to prevent an entire relay transaction | ||
|
@@ -173,14 +160,70 @@ func (k Keeper) recvPacket( | |
return nil | ||
} | ||
|
||
func (k *Keeper) acknowledgePacket(ctx context.Context, packet channeltypesv2.Packet, acknowledgement channeltypesv2.Acknowledgement, proof []byte, proofHeight exported.Height) error { | ||
// Lookup counterparty associated with our channel and ensure | ||
// that the packet was indeed sent by our counterparty. | ||
counterparty, ok := k.GetCounterparty(ctx, packet.SourceChannel) | ||
if !ok { | ||
return errorsmod.Wrap(types.ErrChannelNotFound, packet.SourceChannel) | ||
} | ||
|
||
if counterparty.ClientId != packet.DestinationChannel { | ||
return channeltypes.ErrInvalidChannelIdentifier | ||
} | ||
clientID := counterparty.ClientId | ||
|
||
commitment := k.GetPacketCommitment(ctx, packet.SourceChannel, packet.Sequence) | ||
if len(commitment) == 0 { | ||
// TODO: signal noop in events? | ||
EmitAcknowledgePacketEvents(ctx, packet) | ||
|
||
// This error indicates that the acknowledgement has already been relayed | ||
// or there is a misconfigured relayer attempting to prove an acknowledgement | ||
// for a packet never sent. Core IBC will treat this error as a no-op in order to | ||
// prevent an entire relay transaction from failing and consuming unnecessary fees. | ||
return channeltypes.ErrNoOpMsg | ||
} | ||
|
||
packetCommitment := channeltypesv2.CommitPacket(packet) | ||
|
||
// verify we sent the packet and haven't cleared it out yet | ||
if !bytes.Equal(commitment, packetCommitment) { | ||
return errorsmod.Wrapf(channeltypes.ErrInvalidPacket, "commitment bytes are not equal: got (%v), expected (%v)", packetCommitment, commitment) | ||
} | ||
|
||
path := hostv2.PacketAcknowledgementKey(packet.DestinationChannel, packet.Sequence) | ||
merklePath := types.BuildMerklePath(counterparty.MerklePathPrefix, path) | ||
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.
We should move this to the channel/v2 types pkg afaik. We should not depend on anything in packet-server (which will ultimately be removed). I'd also like some insight into the implementation of 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. re dependencies: I think we can do a pass at the end and make sure there is no dependency on v1 anything within the channel/v2 package. 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. cc. @AdityaSripal @DimitrisJim on this func |
||
|
||
if err := k.ClientKeeper.VerifyMembership( | ||
ctx, | ||
clientID, | ||
proofHeight, | ||
0, 0, | ||
proof, | ||
merklePath, | ||
channeltypesv2.CommitAcknowledgement(acknowledgement), | ||
); err != nil { | ||
return errorsmod.Wrapf(err, "failed packet acknowledgement verification for client (%s)", clientID) | ||
} | ||
|
||
k.DeletePacketCommitment(ctx, packet.SourceChannel, packet.Sequence) | ||
|
||
k.Logger(ctx).Info("packet acknowledged", "sequence", strconv.FormatUint(packet.GetSequence(), 10), "source_channel_id", packet.GetSourceChannel(), "destination_channel_id", packet.GetDestinationChannel()) | ||
|
||
EmitAcknowledgePacketEvents(ctx, packet) | ||
|
||
return nil | ||
} | ||
|
||
// timeoutPacket implements the timeout logic required by a packet handler. | ||
// The packet is checked for correctness including asserting that the packet was | ||
// sent and received on clients which are counterparties for one another. | ||
// If no packet commitment exists, a no-op error is returned, otherwise | ||
// an absence proof of the packet receipt is performed to ensure that the packet | ||
// was never delivered to the counterparty. If successful, the packet commitment | ||
// is deleted and the packet has completed its lifecycle. | ||
func (k Keeper) timeoutPacket( | ||
func (k *Keeper) timeoutPacket( | ||
ctx context.Context, | ||
packet channeltypesv2.Packet, | ||
proof []byte, | ||
|
@@ -209,9 +252,8 @@ func (k Keeper) timeoutPacket( | |
} | ||
|
||
// check that the commitment has not been cleared and that it matches the packet sent by relayer | ||
commitment, ok := k.GetPacketCommitment(ctx, packet.SourceChannel, packet.Sequence) | ||
|
||
if !ok { | ||
commitment := k.GetPacketCommitment(ctx, packet.SourceChannel, packet.Sequence) | ||
if len(commitment) == 0 { | ||
EmitTimeoutPacketEvents(ctx, packet) | ||
// This error indicates that the timeout has already been relayed | ||
// or there is a misconfigured relayer attempting to prove a timeout | ||
|
@@ -222,7 +264,7 @@ func (k Keeper) timeoutPacket( | |
|
||
packetCommitment := channeltypesv2.CommitPacket(packet) | ||
// verify we sent the packet and haven't cleared it out yet | ||
if !bytes.Equal([]byte(commitment), packetCommitment) { | ||
if !bytes.Equal(commitment, packetCommitment) { | ||
return errorsmod.Wrapf(channeltypes.ErrInvalidPacket, "packet commitment bytes are not equal: got (%v), expected (%v)", commitment, packetCommitment) | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package types | ||
|
||
import ( | ||
"crypto/sha256" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
// CommitPacket returns the V2 packet commitment bytes. The commitment consists of: | ||
// sha256_hash(timeout) + sha256_hash(destinationChannel) + sha256_hash(packetData) from a given packet. | ||
// This results in a fixed length preimage. | ||
// NOTE: A fixed length preimage is ESSENTIAL to prevent relayers from being able | ||
// to malleate the packet fields and create a commitment hash that matches the original packet. | ||
func CommitPacket(packet Packet) []byte { | ||
buf := sdk.Uint64ToBigEndian(packet.GetTimeoutTimestamp()) | ||
|
||
destIDHash := sha256.Sum256([]byte(packet.DestinationChannel)) | ||
buf = append(buf, destIDHash[:]...) | ||
|
||
for _, data := range packet.Data { | ||
buf = append(buf, hashPacketData(data)...) | ||
} | ||
|
||
hash := sha256.Sum256(buf) | ||
return hash[:] | ||
} | ||
|
||
// hashPacketData returns the hash of the packet data. | ||
func hashPacketData(data PacketData) []byte { | ||
var buf []byte | ||
sourceHash := sha256.Sum256([]byte(data.SourcePort)) | ||
buf = append(buf, sourceHash[:]...) | ||
destHash := sha256.Sum256([]byte(data.DestinationPort)) | ||
buf = append(buf, destHash[:]...) | ||
payloadValueHash := sha256.Sum256(data.Payload.Value) | ||
buf = append(buf, payloadValueHash[:]...) | ||
payloadEncodingHash := sha256.Sum256([]byte(data.Payload.Encoding)) | ||
buf = append(buf, payloadEncodingHash[:]...) | ||
payloadVersionHash := sha256.Sum256([]byte(data.Payload.Version)) | ||
buf = append(buf, payloadVersionHash[:]...) | ||
hash := sha256.Sum256(buf) | ||
return hash[:] | ||
} | ||
|
||
// CommitAcknowledgement returns the hash of the acknowledgement data. | ||
func CommitAcknowledgement(acknowledgement Acknowledgement) []byte { | ||
var buf []byte | ||
for _, ack := range acknowledgement.GetAcknowledgementResults() { | ||
hash := sha256.Sum256(ack.RecvPacketResult.GetAcknowledgement()) | ||
buf = append(buf, hash[:]...) | ||
} | ||
|
||
hash := sha256.Sum256(buf) | ||
return hash[:] | ||
} |
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.
Broader question but I take this line to bring it up: given #7428, do we want to also use
sourceChannel
anddestinationChannel
in all function arguments?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.
Indeed we do, will update when others are merged