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

Put ClientState in provableStore and use it in connOpen{Try,Ack} #802

Merged
merged 13 commits into from
Aug 1, 2022
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ Ref: https://keepachangelog.com/en/1.0.0/
# Changelog

## [Unreleased]

### Improvements
* \#764 Move `ClientState` to the `provableStore` and add `ClientState` validation in `connOpenTry` and `connOpenAck`
plafer marked this conversation as resolved.
Show resolved Hide resolved
24 changes: 19 additions & 5 deletions spec/core/ics-002-client-semantics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,18 @@ type verifyClientConsensusState = (
=> boolean
```

`verifyClientState` verifies a proof of the client state of the specified client stored on the target machine.

```typescript
type verifyClientState = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than adding this function to the specification, we should transition to using the generic verify membership methods. Though I suppose that can be done in a separate PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you would like to open an issue describing specifically what's to be done, I'd be happy to take a stab at it in another PR

height: Height,
prefix: CommitmentPrefix,
proof: CommitmentProof,
clientIdentifier: Identifier,
clientState: ClientState)
=> boolean
```

`verifyConnectionState` verifies a proof of the connection state of the specified connection end stored on the target state machine.

```typescript
Expand Down Expand Up @@ -474,7 +486,7 @@ This specification defines a single function to query the state of a client by-i

```typescript
function queryClientState(identifier: Identifier): ClientState {
return privateStore.get(clientStatePath(identifier))
return provableStore.get(clientStatePath(identifier))
}
```

Expand Down Expand Up @@ -616,7 +628,7 @@ function createClient(
clientType: ClientType,
consensusState: ConsensusState) {
abortTransactionUnless(validateClientIdentifier(id))
abortTransactionUnless(privateStore.get(clientStatePath(id)) === null)
abortTransactionUnless(provableStore.get(clientStatePath(id)) === null)
abortSystemUnless(provableStore.get(clientTypePath(id)) === null)
clientType.initialise(consensusState)
provableStore.set(clientTypePath(id), clientType)
Expand Down Expand Up @@ -651,7 +663,7 @@ function updateClient(
header: Header) {
clientType = provableStore.get(clientTypePath(id))
abortTransactionUnless(clientType !== null)
clientState = privateStore.get(clientStatePath(id))
clientState = provableStore.get(clientStatePath(id))
abortTransactionUnless(clientState !== null)
clientType.checkValidityAndUpdateState(clientState, header)
}
Expand All @@ -668,7 +680,7 @@ function submitMisbehaviourToClient(
misbehaviour: bytes) {
clientType = provableStore.get(clientTypePath(id))
abortTransactionUnless(clientType !== null)
clientState = privateStore.get(clientStatePath(id))
clientState = provableStore.get(clientStatePath(id))
abortTransactionUnless(clientState !== null)
clientType.checkMisbehaviourAndUpdateState(clientState, misbehaviour)
}
Expand Down Expand Up @@ -739,7 +751,7 @@ function initialise(consensusState: ConsensusState): () {
pastPublicKeys: Set.singleton(consensusState.publicKey),
verifiedRoots: Map.empty()
}
privateStore.set(identifier, clientState)
provableStore.set(identifier, clientState)
}

// validity predicate function defined by the client type
Expand Down Expand Up @@ -903,6 +915,8 @@ Jan 13, 2020 - Revisions for client type separation & path alterations

Jan 26, 2020 - Addition of query interface

Jul 27, 2022 - Addition of `verifyClientState` function, and move `ClientState` to the `provableStore`

## Copyright

All content herein is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0).
30 changes: 25 additions & 5 deletions spec/core/ics-003-connection-semantics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,16 @@ function verifyClientConsensusState(
return client.verifyClientConsensusState(connection, height, connection.counterpartyPrefix, proof, clientIdentifier, consensusStateHeight, consensusState)
}

function verifyClientState(
connection: ConnectionEnd,
height: Height,
proof: CommitmentProof,
clientState: ClientState
) {
client = queryClient(connection.clientIdentifier)
return client.verifyClientState(height, connection.counterpartyPrefix, proof, connection.clientIdentifier, clientState)
}

function verifyConnectionState(
connection: ConnectionEnd,
height: Height,
Expand Down Expand Up @@ -342,27 +352,31 @@ function connOpenTry(
counterpartyPrefix: CommitmentPrefix,
counterpartyClientIdentifier: Identifier,
clientIdentifier: Identifier,
clientState: ClientState,
counterpartyVersions: string[],
delayPeriodTime: uint64,
delayPeriodBlocks: uint64,
proofInit: CommitmentProof,
proofClient: CommitmentProof,
proofConsensus: CommitmentProof,
proofHeight: Height,
consensusHeight: Height) {
// generate a new identifier
identifier = generateIdentifier()


abortTransactionUnless(validateSelfClient(clientState))
abortTransactionUnless(consensusHeight < getCurrentHeight())
expectedConsensusState = getConsensusState(consensusHeight)
expected = ConnectionEnd{INIT, "", getCommitmentPrefix(), counterpartyClientIdentifier,
expectedConnectionEnd = ConnectionEnd{INIT, "", getCommitmentPrefix(), counterpartyClientIdentifier,
clientIdentifier, counterpartyVersions, delayPeriodTime, delayPeriodBlocks}

versionsIntersection = intersection(counterpartyVersions, getCompatibleVersions())
version = pickVersion(versionsIntersection) // throws if there is no intersection

connection = ConnectionEnd{TRYOPEN, counterpartyConnectionIdentifier, counterpartyPrefix,
clientIdentifier, counterpartyClientIdentifier, version, delayPeriodTime, delayPeriodBlocks}
abortTransactionUnless(connection.verifyConnectionState(proofHeight, proofInit, counterpartyConnectionIdentifier, expected))
abortTransactionUnless(connection.verifyConnectionState(proofHeight, proofInit, counterpartyConnectionIdentifier, expectedConnectionEnd))
abortTransactionUnless(connection.verifyClientState(proofHeight, proofClient, clientState))
abortTransactionUnless(connection.verifyClientConsensusState(
proofHeight, proofConsensus, counterpartyClientIdentifier, consensusHeight, expectedConsensusState))

Expand All @@ -376,20 +390,24 @@ function connOpenTry(
```typescript
function connOpenAck(
identifier: Identifier,
clientState: ClientState,
version: string,
counterpartyIdentifier: Identifier,
proofTry: CommitmentProof,
proofClient: CommitmentProof,
proofConsensus: CommitmentProof,
proofHeight: Height,
consensusHeight: Height) {
abortTransactionUnless(consensusHeight < getCurrentHeight())
abortTransactionUnless(validateSelfClient(clientState))
connection = provableStore.get(connectionPath(identifier))
abortTransactionUnless((connection.state === INIT && connection.version.indexOf(version) !== -1)
expectedConsensusState = getConsensusState(consensusHeight)
expected = ConnectionEnd{TRYOPEN, identifier, getCommitmentPrefix(),
expectedConnectionEnd = ConnectionEnd{TRYOPEN, identifier, getCommitmentPrefix(),
connection.counterpartyClientIdentifier, connection.clientIdentifier,
version, connection.delayPeriodTime, connection.delayPeriodBlocks}
abortTransactionUnless(connection.verifyConnectionState(proofHeight, proofTry, counterpartyIdentifier, expected))
abortTransactionUnless(connection.verifyConnectionState(proofHeight, proofTry, counterpartyIdentifier, expectedConnectionEnd))
abortTransactionUnless(connection.verifyClientState(proofHeight, proofClient, clientState))
abortTransactionUnless(connection.verifyClientConsensusState(
proofHeight, proofConsensus, connection.counterpartyClientIdentifier, consensusHeight, expectedConsensusState))
connection.state = OPEN
Expand Down Expand Up @@ -467,6 +485,8 @@ May 17, 2019 - Draft finalised

Jul 29, 2019 - Revisions to track connection set associated with client

Jul 27, 2022 - Addition of `ClientState` validation in `connOpenTry` and `connOpenAck`

## Copyright

All content herein is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0).
78 changes: 77 additions & 1 deletion spec/core/ics-024-host-requirements/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Note that the client-related paths listed below reflect the Tendermint client as
| Store | Path format | Value type | Defined in |
| -------------- | ------------------------------------------------------------------------------ | ----------------- | ---------------------- |
| provableStore | "clients/{identifier}/clientType" | ClientType | [ICS 2](../ics-002-client-semantics) |
| privateStore | "clients/{identifier}/clientState" | ClientState | [ICS 2](../../client/ics-007-tendermint-client) |
| provableStore | "clients/{identifier}/clientState" | ClientState | [ICS 2](../ics-002-client-semantics) |
| provableStore | "clients/{identifier}/consensusStates/{height}" | ConsensusState | [ICS 7](../../client/ics-007-tendermint-client) |
| privateStore | "clients/{identifier}/connections | []Identifier | [ICS 3](../ics-003-connection-semantics) |
| provableStore | "connections/{identifier}" | ConnectionEnd | [ICS 3](../ics-003-connection-semantics) |
Expand Down Expand Up @@ -181,6 +181,80 @@ Host state machines MUST provide the ability to introspect this stored recent co
type getStoredRecentConsensusStateCount = () => Height
```

### Client state validation
Host state machines MUST define a unique `ClientState` type fulfilling the requirements of [ICS 2](../ics-002-client-semantics).

Host state machines MUST provide the ability to construct a `ClientState` representation of their own state for the purposes of client state validation, with `getHostClientState`:

```typescript
type getHostClientState = (height: Height) => ClientState
```

Host state machines MUST provide the ability to validate the `ClientState` of a light client running on a counterparty chain, with `validateSelfClient`:

```typescript
type validateSelfClient = (counterpartyClientState: ClientState) => boolean
```

`validateSelfClient` validates the client parameters for a client of the host chain. For example, below is the implementation for Tendermint hosts, using `ClientState` as defined in [ICS 7](../../client/ics-007-tendermint-client/):

```typescript
function validateSelfClient(counterpartyClientState: ClientState) {
hostClientState = getHostClientState()

// assert that the counterparty client is not frozen
if counterpartyClientState.frozenHeight !== null {
return false
}

// assert that the chain ids are the same
if counterpartyClientState.chainID !== hostClientState.chainID {
return false
}

// assert that the counterparty client is in the same revision as the host chain
counterpartyRevisionNumber = parseRevisionNumber(counterpartyClientState.chainID)
if counterpartyRevisionNumber !== hostClientState.latestHeight.revisionNumber {
return false
}

// assert that the counterparty client has a height less than the host height
if counterpartyClientState.latestHeight >== hostClientState.latestHeight {
return false
}

// assert that the counterparty client has the same ProofSpec as the host
if counterpartyClientState.proofSpecs !== hostClientState.proofSpecs {
return false
}

// assert that the trustLevel is within the allowed range. 1/3 is the minimum amount
// of trust needed which does not break the security model.
if counterpartyClientState.trustLevel < 1/3 || counterpartyClientState.trustLevel > 1 {
return false
}

// assert that the unbonding periods are the same
if counterpartyClientState.unbondingPeriod != hostClientState.unbondingPeriod {
return false
}

// assert that the unbonding period is greater than or equal to the trusting period
if counterpartyClientState.unbondingPeriod < counterpartyClientState.trustingPeriod {
return false
}

// assert that the upgrade paths are the same
hostUpgradePath = applyPrefix(hostClientState.upgradeCommitmentPrefix, hostClientState.upgradeKey)
counterpartyUpgradePath = applyPrefix(counterpartyClientState.upgradeCommitmentPrefix, counterpartyClientState.upgradeKey)
if counterpartyUpgradePath !== hostUpgradePath {
return false
}

return true
}
```

### Commitment path introspection

Host chains MUST provide the ability to inspect their commitment path, with `getCommitmentPrefix`:
Expand Down Expand Up @@ -323,6 +397,8 @@ Aug 18, 2019 - Revisions to module system, definitions

Jul 05, 2022 - Lower the minimal allowed length of a channel identifier to 8

Jul 27, 2022 - Move `ClientState` to the `provableStore`, and add "Client state validation" section

## Copyright

All content herein is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0).
Expand Down