Skip to content

Commit

Permalink
Add VerifyMembership to 07-tendermint (cosmos#1297)
Browse files Browse the repository at this point in the history
* add VerifyMembership function and initial tests

* revert unnecessary code changes

* add rest of successful verify test cases

* increase code coverage with additional tests

* chore: remove field naming from test cases

* add test case using VerifyMembership on non IBC store value
  • Loading branch information
colin-axner authored and oshorefueled committed Aug 9, 2022
1 parent d379844 commit ed77f9b
Show file tree
Hide file tree
Showing 3 changed files with 376 additions and 26 deletions.
110 changes: 88 additions & 22 deletions modules/light-clients/07-tendermint/types/client_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ func (cs ClientState) Initialize(ctx sdk.Context, _ codec.BinaryCodec, clientSto

// VerifyClientState verifies a proof of the client state of the running chain
// stored on the target machine
// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294
func (cs ClientState) VerifyClientState(
store sdk.KVStore,
cdc codec.BinaryCodec,
Expand Down Expand Up @@ -231,6 +232,7 @@ func (cs ClientState) VerifyClientState(

// VerifyClientConsensusState verifies a proof of the consensus state of the
// Tendermint client stored on the target machine.
// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294
func (cs ClientState) VerifyClientConsensusState(
store sdk.KVStore,
cdc codec.BinaryCodec,
Expand Down Expand Up @@ -275,6 +277,7 @@ func (cs ClientState) VerifyClientConsensusState(

// VerifyConnectionState verifies a proof of the connection state of the
// specified connection end stored on the target machine.
// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294
func (cs ClientState) VerifyConnectionState(
store sdk.KVStore,
cdc codec.BinaryCodec,
Expand Down Expand Up @@ -314,6 +317,7 @@ func (cs ClientState) VerifyConnectionState(

// VerifyChannelState verifies a proof of the channel state of the specified
// channel end, under the specified port, stored on the target machine.
// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294
func (cs ClientState) VerifyChannelState(
store sdk.KVStore,
cdc codec.BinaryCodec,
Expand Down Expand Up @@ -354,6 +358,7 @@ func (cs ClientState) VerifyChannelState(

// VerifyPacketCommitment verifies a proof of an outgoing packet commitment at
// the specified port, specified channel, and specified sequence.
// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294
func (cs ClientState) VerifyPacketCommitment(
ctx sdk.Context,
store sdk.KVStore,
Expand Down Expand Up @@ -393,6 +398,7 @@ func (cs ClientState) VerifyPacketCommitment(

// VerifyPacketAcknowledgement verifies a proof of an incoming packet
// acknowledgement at the specified port, specified channel, and specified sequence.
// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294
func (cs ClientState) VerifyPacketAcknowledgement(
ctx sdk.Context,
store sdk.KVStore,
Expand Down Expand Up @@ -433,6 +439,7 @@ func (cs ClientState) VerifyPacketAcknowledgement(
// VerifyPacketReceiptAbsence verifies a proof of the absence of an
// incoming packet receipt at the specified port, specified channel, and
// specified sequence.
// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294
func (cs ClientState) VerifyPacketReceiptAbsence(
ctx sdk.Context,
store sdk.KVStore,
Expand Down Expand Up @@ -471,6 +478,7 @@ func (cs ClientState) VerifyPacketReceiptAbsence(

// VerifyNextSequenceRecv verifies a proof of the next sequence number to be
// received of the specified channel at the specified port.
// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294
func (cs ClientState) VerifyNextSequenceRecv(
ctx sdk.Context,
store sdk.KVStore,
Expand Down Expand Up @@ -509,39 +517,97 @@ func (cs ClientState) VerifyNextSequenceRecv(
return nil
}

// VerifyMembership is a generic proof verification method which verifies a proof of the existence of a value at a given CommitmentPath at the specified height.
// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24).
func (cs ClientState) VerifyMembership(
ctx sdk.Context,
clientStore sdk.KVStore,
cdc codec.BinaryCodec,
height exported.Height,
delayTimePeriod uint64,
delayBlockPeriod uint64,
proof []byte,
path []byte,
value []byte,
) error {
if cs.GetLatestHeight().LT(height) {
return sdkerrors.Wrapf(
sdkerrors.ErrInvalidHeight,
"client state height < proof height (%d < %d), please ensure the client has been updated", cs.GetLatestHeight(), height,
)
}

if err := verifyDelayPeriodPassed(ctx, clientStore, height, delayTimePeriod, delayBlockPeriod); err != nil {
return err
}

var merkleProof commitmenttypes.MerkleProof
if err := cdc.Unmarshal(proof, &merkleProof); err != nil {
return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal proof into ICS 23 commitment merkle proof")
}

var merklePath commitmenttypes.MerklePath
if err := cdc.Unmarshal(path, &merklePath); err != nil {
return sdkerrors.Wrap(commitmenttypes.ErrInvalidProof, "failed to unmarshal path into ICS 23 commitment merkle path")
}

consensusState, found := GetConsensusState(clientStore, cdc, height)
if !found {
return sdkerrors.Wrap(clienttypes.ErrConsensusStateNotFound, "please ensure the proof was constructed against a height that exists on the client")
}

if err := merkleProof.VerifyMembership(cs.ProofSpecs, consensusState.GetRoot(), merklePath, value); err != nil {
return err
}

return nil
}

// verifyDelayPeriodPassed will ensure that at least delayTimePeriod amount of time and delayBlockPeriod number of blocks have passed
// since consensus state was submitted before allowing verification to continue.
func verifyDelayPeriodPassed(ctx sdk.Context, store sdk.KVStore, proofHeight exported.Height, delayTimePeriod, delayBlockPeriod uint64) error {
// check that executing chain's timestamp has passed consensusState's processed time + delay time period
processedTime, ok := GetProcessedTime(store, proofHeight)
if !ok {
return sdkerrors.Wrapf(ErrProcessedTimeNotFound, "processed time not found for height: %s", proofHeight)
}
currentTimestamp := uint64(ctx.BlockTime().UnixNano())
validTime := processedTime + delayTimePeriod
// NOTE: delay time period is inclusive, so if currentTimestamp is validTime, then we return no error
if currentTimestamp < validTime {
return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until time: %d, current time: %d",
validTime, currentTimestamp)
}
// check that executing chain's height has passed consensusState's processed height + delay block period
processedHeight, ok := GetProcessedHeight(store, proofHeight)
if !ok {
return sdkerrors.Wrapf(ErrProcessedHeightNotFound, "processed height not found for height: %s", proofHeight)
if delayTimePeriod != 0 {
// check that executing chain's timestamp has passed consensusState's processed time + delay time period
processedTime, ok := GetProcessedTime(store, proofHeight)
if !ok {
return sdkerrors.Wrapf(ErrProcessedTimeNotFound, "processed time not found for height: %s", proofHeight)
}

currentTimestamp := uint64(ctx.BlockTime().UnixNano())
validTime := processedTime + delayTimePeriod

// NOTE: delay time period is inclusive, so if currentTimestamp is validTime, then we return no error
if currentTimestamp < validTime {
return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until time: %d, current time: %d",
validTime, currentTimestamp)
}

}
currentHeight := clienttypes.GetSelfHeight(ctx)
validHeight := clienttypes.NewHeight(processedHeight.GetRevisionNumber(), processedHeight.GetRevisionHeight()+delayBlockPeriod)
// NOTE: delay block period is inclusive, so if currentHeight is validHeight, then we return no error
if currentHeight.LT(validHeight) {
return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until height: %s, current height: %s",
validHeight, currentHeight)

if delayBlockPeriod != 0 {
// check that executing chain's height has passed consensusState's processed height + delay block period
processedHeight, ok := GetProcessedHeight(store, proofHeight)
if !ok {
return sdkerrors.Wrapf(ErrProcessedHeightNotFound, "processed height not found for height: %s", proofHeight)
}

currentHeight := clienttypes.GetSelfHeight(ctx)
validHeight := clienttypes.NewHeight(processedHeight.GetRevisionNumber(), processedHeight.GetRevisionHeight()+delayBlockPeriod)

// NOTE: delay block period is inclusive, so if currentHeight is validHeight, then we return no error
if currentHeight.LT(validHeight) {
return sdkerrors.Wrapf(ErrDelayPeriodNotPassed, "cannot verify packet until height: %s, current height: %s",
validHeight, currentHeight)
}
}

return nil
}

// produceVerificationArgs performs the basic checks on the arguments that are
// shared between the verification functions and returns the unmarshalled
// merkle proof, the consensus state and an error if one occurred.
// TODO: remove, https://github.com/cosmos/ibc-go/issues/1294
func produceVerificationArgs(
store sdk.KVStore,
cdc codec.BinaryCodec,
Expand Down
Loading

0 comments on commit ed77f9b

Please sign in to comment.