Skip to content

Commit

Permalink
revise and update multihop spec details
Browse files Browse the repository at this point in the history
  • Loading branch information
dshiell committed Mar 8, 2023
1 parent b504d41 commit 015b26b
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 68 deletions.
38 changes: 31 additions & 7 deletions spec/core/ics-003-connection-semantics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ function verifyNextSequenceRecv(
return client.verifyNextSequenceRecv(connection, height, connection.delayPeriodTime, connection.delayPeriodBlocks, connection.counterpartyPrefix, proof, portIdentifier, channelIdentifier, sequence, nextSequenceRecv)
}

function verifyMultihopProof(
function verifyMultihopMembership(
connection: ConnectionEnd,
height: Height,
proof: MultihopProof,
Expand All @@ -253,12 +253,36 @@ function verifyMultihopProof(
abortTransactionUnless(client.Status() === "active")
abortTransactionUnless(client.GetLatestHeight() >= height)

delayPeriod = abortTransactionUnless(getMaximumDelayPeriod(proof, connection))
expectedTimePerBlock := queryMaxExpectedTimePerBlock()
// verify maximum delay period has passed
maxDelayPeriod = abortTransactionUnless(getMaximumDelayPeriod(proof, connection))
expectedTimePerBlock = queryMaxExpectedTimePerBlock()
blockDelay = getBlockDelay(timeDelay, expectedTimePerBlock)
abortTransactionUnless(verifyDelayPeriodPassed(height, maxDelayPeriod, blockDelay))

return multihop.VerifyMultihopMembership(consensusState, connectionHops, proof, prefix, key, value) // see ics-033
}

function verifyMultihopNonMembership(
connection: ConnectionEnd,
height: Height,
proof: MultihopProof,
connectionHops: String[],
key: String) {
multihopConnectionEnd = abortTransactionUnless(getMultihopConnectionEnd(proof))
prefix = multihopConnectionEnd.GetCounterparty().GetPrefix()
client = queryClient(connection.clientIdentifier)
consensusState = queryConsensusState(connection.clientIdentifier, height)

abortTransactionUnless(client.Status() === "active")
abortTransactionUnless(client.GetLatestHeight() >= height)

abortTransactionUnless(multihop.VerifyDelayPeriodPassed(height, delayPeriod, expectedTimePerBlock)) // see ics-033
// verify maximum delay period has passed
maxDelayPeriod = abortTransactionUnless(getMaximumDelayPeriod(proof, connection))
expectedTimePerBlock = queryMaxExpectedTimePerBlock()
blockDelay = getBlockDelay(timeDelay, expectedTimePerBlock)
abortTransactionUnless(verifyDelayPeriodPassed(height, maxDelayPeriod, blockDelay))

return multihop.VerifyMultihopProof(consensusState, connectionHops, proof, prefix, key, value) // see ics-033
return multihop.VerifyMultihopNonMembership(consensusState, connectionHops, proof, prefix, key) // see ics-033
}

function getTimestampAtHeight(
Expand All @@ -275,9 +299,9 @@ function getMultihopConnectionEnd(proof: MultihopProof): ConnectionEnd {

// Return the maximum delay period across all connections in the channel path.
function getMaximumDelayPeriod(proof: MultihopProof, lastConnection: ConnectionEnd): number {
let delayPeriod = lastConnection.GetDelayPeriod()
delayPeriod = lastConnection.GetDelayPeriod()
for connData in range proofs.ConnectionProofs {
let connectionEnd = abortTransactionUnless(Unmarshal(connData.Value))
connectionEnd = abortTransactionUnless(Unmarshal(connData.Value))
if (connectionEnd.DelayPeriod > delayPeriod) {
delayPeriod = connectionEnd.DelayPeriod
}
Expand Down
48 changes: 18 additions & 30 deletions spec/core/ics-004-channel-and-packet-semantics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,6 @@ function chanOpenTry(

if (connectionHops.length > 1) {
key = host.ChannelPath(counterparty.PortId, counterparty.ChannelId)

abortTransactionUnless(connection.verifyMultihopProof(
connection,
proofHeight,
Expand Down Expand Up @@ -405,7 +404,7 @@ function chanOpenAck(
abortTransactionUnless(connection.state === OPEN)

let counterpartyHops: []string = []
if (connectionHops.length > 1) {
if (channel.connectionHops.length > 1) {
counterpartyHops = getCounterPartyHops(proofTry)
} else {
counterpartyHops = [connection.counterpartyConnectionIdentifier]
Expand All @@ -414,18 +413,14 @@ function chanOpenAck(
expected = ChannelEnd{TRYOPEN, channel.order, portIdentifier,
channelIdentifier, counterpartyHops, counterpartyVersion}

if (connectionHops.length > 1) {
consensusState = provableStore.get(consensusStatePath(connection.ClientId, proofHeight))
if (channel.connectionHops.length > 1) {
key = host.ChannelPath(counterparty.PortId, counterparty.ChannelId)
counterpartyConnectionEnd = abortTransactionUnless(getMultihopConnectionEnd(proofTry))
prefix = counterpartyConnectionEnd.GetCounterparty().GetPrefix()

abortTransactionUnless(connection.verifyMultihopProof(
consensusState,
connectionHops,
connection,
proofHeight,
proofTry,
prefix,
key
channel.connectionHops,
key,
expected))
} else {
abortTransactionUnless(connection.verifyChannelState(
Expand Down Expand Up @@ -472,16 +467,12 @@ function chanOpenConfirm(
channelIdentifier, counterpartyHops, channel.version}

if (connectionHops.length > 1) {
consensusState = provableStore.get(consensusStatePath(connection.ClientId, proofHeight))
key = host.ChannelPath(counterparty.PortId, counterparty.ChannelId)
counterpartyConnectionEnd = abortTransactionUnless(getMultihopConnectionEnd(proofAck))
prefix = counterpartyConnectionEnd.GetCounterparty().GetPrefix()

abortTransactionUnless(connection.verifyMultihopProof(
consensusState,
connectionHops,
proofTry,
prefix,
abortTransactionUnless(connection.verifyMultihopMembership(
connection,
proofHeight,
proofAck,
channel.connectionHops,
key
expected))
} else {
Expand Down Expand Up @@ -561,16 +552,13 @@ function chanCloseConfirm(
channelIdentifier, counterpartyHops, channel.version}

if (connectionHops.length > 1) {
consensusState = provableStore.get(consensusStatePath(connection.ClientId, proofHeight))
key = host.ChannelPath(counterparty.PortId, counterparty.ChannelId)
counterpartyConnectionEnd = abortTransactionUnless(getMultihopConnectionEnd(proofInit))
prefix = counterpartyConnectionEnd.GetCounterparty().GetPrefix()

abortTransactionUnless(connection.verifyMultihopProof(
consensusState,
connectionHops,
proofTry,
prefix,
abortTransactionUnless(connection.verifyMultihopMembership(
connection,
proofHeight,
proofInit,
channel.connectionHops,
key
expected))
} else {
Expand Down Expand Up @@ -637,7 +625,7 @@ function chanCloseFrozen(
// ensure client state is frozen by checking FrozenHeight
abortTransactionUnless(frozenClientState.FrozenHeight !== Height(0,0)

abortTransactionUnless(connection.verifyMultihopProof(
abortTransactionUnless(connection.verifyMultihopMembership(
connection,
proofHeight,
proofFrozen,
Expand Down Expand Up @@ -801,7 +789,7 @@ function recvPacket(

if (len(channel.connectionHops) > 1) {
key = host.PacketCommitmentPath(packet.GetSourcePort(), packet.GetSourceChannel(), packet.GetSequence())
abortTransactionUnless(connection.verifyMultihopProof(
abortTransactionUnless(connection.verifyMultihopMembership(
connection,
proofHeight,
proof,
Expand Down
103 changes: 72 additions & 31 deletions spec/core/ics-033-multi-hop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,14 +239,14 @@ For more details see [ICS4](https://github.com/cosmos/ibc/tree/main/spec/core/ic
Pseudocode proof generation for a channel between `N` chains `C[0] --> C[i] --> C[N]`

```go
// VerifyMultihopProof verifies a multihop proof.
// VerifyMultihopMembership verifies a multihop membership proof.
// Inputs: consensusState - The consensusState for chain[N-1], which is known on the destination chain (chain[N]).
// connectionHops - The expected connectionHops for the channel from the source chain to the destination chain.
// proof - The serialized multihop proof data.
// prefix - Merkleprefix to be combined with key to generate Merklepath for the key/value proof verification.
// key - The key to prove in the source chain's state root.
// value - The value to prove in the source chain's state root.
func VerifyMultihopProof(
// key - The key to prove in the indexed consensus state.
// value - The value to prove in the indexed consensus state.
func VerifyMultihopMembership(
consensusState exported.ConsensusState,
connectionHops []string,
proof []byte,
Expand All @@ -259,14 +259,42 @@ func VerifyMultihopProof(
abortTransactionUnless(len(proofs.ConsensusProofs) >= 1)
abortTransactionUnless(len(proofs.ConnectionProofs) == len(proofs.ConsensusProofs))

// verify connection states and ordering
abortTransactionUnless(VerifyConnectionStates(proofs.ConnectionProofs, connectionHops))
// verify connection hop ordering and connections are in OPEN state
abortTransactionUnless(VerifyConnectionHops(proofs.ConnectionProofs, connectionHops))

// verify intermediate consensus and connection states from destination --> source
abortTransactionUnless(VerifyIntermediateStateProofs(consensusState, proofs.ConsensusProofs, proofs.ConnectionProofs))
abortTransactionUnless(VerifyConsensusAndConnectionStates(consensusState, proofs.ConsensusProofs, proofs.ConnectionProofs))

// verify a key/value proof on source chain's consensus state.
abortTransactionUnless(VerifyKeyValueProof(proofs, prefix, key, value))
abortTransactionUnless(VerifyKeyMembership(proofs, prefix, key, value))
}

// VerifyMultihopNonMembership verifies a multihop non-membership proof.
// Inputs: consensusState - The consensusState for chain[N-1], which is known on the destination chain (chain[N]).
// connectionHops - The expected connectionHops for the channel from the source chain to the destination chain.
// proof - The serialized multihop proof data.
// prefix - Merkleprefix to be combined with key to generate Merklepath for the key/value proof verification.
// key - The key to prove absent in the indexed consensus state
func VerifyMultihopNonMembership(
consensusState exported.ConsensusState,
connectionHops []string,
proof []byte,
prefix exported.Prefix,
key string,
) {
// deserialize proof bytes into multihop proofs
proofs := abortTransactionUnless(Unmarshal(proof))
abortTransactionUnless(len(proofs.ConsensusProofs) >= 1)
abortTransactionUnless(len(proofs.ConnectionProofs) == len(proofs.ConsensusProofs))

// verify connection hop ordering and connections are in OPEN state
abortTransactionUnless(VerifyConnectionHops(proofs.ConnectionProofs, connectionHops))

// verify intermediate consensus and connection states from destination --> source
abortTransactionUnless(VerifyConsensusAndConnectionStates(consensusState, proofs.ConsensusProofs, proofs.ConnectionProofs))

// verify a key/value proof on source chain's consensus state.
abortTransactionUnless(VerifyKeyNonMembership(proofs, prefix, key))
}

// VerifyDelayPeriodPassed will ensure that at least delayTimePeriod amount of time and delayBlockPeriod number of blocks have passed
Expand All @@ -280,11 +308,11 @@ func VerifyDelayPeriodPassed(
blockDelay := getBlockDelay(timeDelay, expectedTimePerBlock)

// tendermint client implementation
return tmclient.VerifyDelayPeriodPassed(proofHeight, timeDelay, blockDelay)
return tendermint.VerifyDelayPeriodPassed(proofHeight, timeDelay, blockDelay)
}

// VerifyConnectionStates checks that each connection in the multihop proof is OPEN and matches the connections in connectionHops.
func VerifyConnectionStates(
// VerifyConnectionHops checks that each connection in the multihop proof is OPEN and matches the connections in connectionHops.
func VerifyConnectionHops(
connectionProofs []*MultihopProof,
connectionHops []string,
) {
Expand All @@ -302,10 +330,10 @@ func VerifyConnectionStates(
}
}

// VerifyIntermediateStateProofs verifies the state of each intermediate consensus, connection, and
// VerifyConsensusAndConnectionStates verifies the state of each intermediate consensus, connection, and
// client state starting from chain[N-1] on the destination (chain[N]) and finally proving the source
// chain consensus, connection, and client state.
func VerifyIntermediateStateProofs(
func VerifyConsensusAndConnectionStates(
consensusState exported.ConsensusState,
consensusProofs []*MultihopProof,
connectionProofs []*MultihopProof,
Expand Down Expand Up @@ -336,8 +364,8 @@ func VerifyIntermediateStateProofs(
}
}

// VerifyKeyValueProof verifies a key in the source chain consensus state.
func VerifyKeyValueProof(
// VerifyKeyMembership verifies a key in the indexed chain consensus state.
func VerifyKeyMembership(
proofs *MsgMultihopProof,
prefix exported.Prefix,
key string,
Expand All @@ -352,24 +380,37 @@ func VerifyKeyValueProof(
// assign the key proof to verify on the source chain
keyProof := abortTransactionUnless(Unmarshal(proofs.KeyProof.Proof))

// prove membership if value provided else prove non-membership
if (value != nil) {
abortTransactionUnless(keyProof.VerifyMembership(
commitmenttypes.GetSDKSpecs(),
consensusState.GetRoot(),
prefixedKey,
value,
))
} else {
abortTransactionUnless(keyProof.VerifyNonMembership(
commitmenttypes.GetSDKSpecs(),
consensusState.GetRoot(),
prefixedKey,
))
}
abortTransactionUnless(keyProof.VerifyMembership(
commitmenttypes.GetSDKSpecs(),
consensusState.GetRoot(),
prefixedKey,
value,
))

}
```

// VerifyKeyNonMembership verifies a key in the indexed chain consensus state.
func VerifyKeyNonMembership(
proofs *MsgMultihopProof,
prefix exported.Prefix,
key string,
) {
// create prefixed key for proof verification
prefixedKey := abortTransactionUnless(commitmenttypes.ApplyPrefix(prefix, commitmenttypes.NewMerklePath(key)))

// extract indexed consensus state from consensus proofs
consensusState := abortTransactionUnless(UnmarshalInterface(proofs.ConsensusProofs[proofs.KeyProofIndex].Value))

// assign the key proof to verify on the source chain
keyProof := abortTransactionUnless(Unmarshal(proofs.KeyProof.Proof))

abortTransactionUnless(keyProof.VerifyNonMembership(
commitmenttypes.GetSDKSpecs(),
consensusState.GetRoot(),
prefixedKey,
))
}
```

### Path Forgery Protection

Expand Down

0 comments on commit 015b26b

Please sign in to comment.