From 1e43fcc8253b3e43263ea6de95d595c3fd6ecfbe Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 8 Jul 2020 11:49:39 +0200 Subject: [PATCH 01/11] Update ICS 2 --- spec.pdf | Bin 132 -> 132 bytes spec/ics-002-client-semantics/README.md | 78 +++++++++++++++++------- 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/spec.pdf b/spec.pdf index 7dbd36b42e8951c9a247c672ce9cee9a26767a6a..19d55c150c3f32c643ece8f743be588c9d841648 100644 GIT binary patch delta 85 zcmWN_yA6Oa3Y{ ge?4j$GzUgNji-cm;o1sXWSthA?(2zNp)LjK1I=(2k^lez delta 85 zcmV~$u@QhU2nEnf>lBW_CqiHecZdjg)^^qo;K;uBwzqw{mji)06Rw-i+{p4|zy~=+ ejfgrB;y4ULoqBRoI Ord +``` + +A height is either `LT` (less than), `EQ` (equal to), or `GT` (greater than) another height. + #### ClientState `ClientState` is an opaque data structure defined by a client type. @@ -279,7 +300,7 @@ Client types MUST define a method to fetch the current height (height of the mos ```typescript type latestClientHeight = ( clientState: ClientState) - => uint64 + => Height ``` #### CommitmentProof @@ -300,10 +321,10 @@ Internal implementation details may differ (for example, a loopback client could ```typescript type verifyClientConsensusState = ( clientState: ClientState, - height: uint64, + height: Height, proof: CommitmentProof, clientIdentifier: Identifier, - consensusStateHeight: uint64, + consensusStateHeight: Height, consensusState: ConsensusState) => boolean ``` @@ -313,7 +334,7 @@ type verifyClientConsensusState = ( ```typescript type verifyConnectionState = ( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, connectionIdentifier: Identifier, @@ -326,7 +347,7 @@ type verifyConnectionState = ( ```typescript type verifyChannelState = ( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -340,7 +361,7 @@ type verifyChannelState = ( ```typescript type verifyPacketData = ( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -355,7 +376,7 @@ type verifyPacketData = ( ```typescript type verifyPacketAcknowledgement = ( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -370,7 +391,7 @@ type verifyPacketAcknowledgement = ( ```typescript type verifyPacketAcknowledgementAbsence = ( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -384,7 +405,7 @@ type verifyPacketAcknowledgementAbsence = ( ```typescript type verifyNextSequenceRecv = ( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -402,7 +423,7 @@ These query endpoints are assumed to be exposed over HTTP or an equivalent RPC A `queryHeader` MUST be defined by the chain which is validated by a particular client, and should allow for retrieval of headers by height. This endpoint is assumed to be untrusted. ```typescript -type queryHeader = (height: uint64) => Header +type queryHeader = (height: Height) => Header ``` `queryChainConsensusState` MAY be defined by the chain which is validated by a particular client, to allow for the retrieval of the current consensus state which can be used to construct a new client. @@ -410,7 +431,7 @@ When used in this fashion, the returned `ConsensusState` MUST be manually confir `ConsensusState` may vary per client type. ```typescript -type queryChainConsensusState = (height: uint64) => ConsensusState +type queryChainConsensusState = (height: Height) => ConsensusState ``` Note that retrieval of past consensus states by height (as opposed to just the current consensus state) is convenient but not required. @@ -430,7 +451,7 @@ function queryClientState(identifier: Identifier): ClientState { The `ClientState` type SHOULD expose its latest verified height (from which the consensus state can then be retrieved using `queryConsensusState` if desired). ```typescript -type latestHeight = (state: ClientState) => uint64 +type latestHeight = (state: ClientState) => Height ``` Client types SHOULD define the following standardised query functions in order to allow relayers & other off-chain entities to interface with on-chain state in a standard API. @@ -440,7 +461,7 @@ Client types SHOULD define the following standardised query functions in order t ```typescript type queryConsensusState = ( identifier: Identifier, - height: uint64 + height: Height, ) => ConsensusState ``` @@ -453,46 +474,46 @@ These functions may constitute external queries over RPC to a full node as well ```typescript type queryAndProveClientConsensusState = ( clientIdentifier: Identifier, - height: uint64, + height: Height, prefix: CommitmentPrefix, - consensusStateHeight: uint64) => ConsensusState, Proof + consensusStateHeight: Height) => ConsensusState, Proof type queryAndProveConnectionState = ( connectionIdentifier: Identifier, - height: uint64, + height: Height, prefix: CommitmentPrefix) => ConnectionEnd, Proof type queryAndProveChannelState = ( portIdentifier: Identifier, channelIdentifier: Identifier, - height: uint64, + height: Height, prefix: CommitmentPrefix) => ChannelEnd, Proof type queryAndProvePacketData = ( portIdentifier: Identifier, channelIdentifier: Identifier, - height: uint64, + height: Height, prefix: CommitmentPrefix, sequence: uint64) => []byte, Proof type queryAndProvePacketAcknowledgement = ( portIdentifier: Identifier, channelIdentifier: Identifier, - height: uint64, + height: Height, prefix: CommitmentPrefix, sequence: uint64) => []byte, Proof type queryAndProvePacketAcknowledgementAbsence = ( portIdentifier: Identifier, channelIdentifier: Identifier, - height: uint64, + height: Height, prefix: CommitmentPrefix, sequence: uint64) => Proof type queryAndProveNextSequenceRecv = ( portIdentifier: Identifier, channelIdentifier: Identifier, - height: uint64, + height: Height, prefix: CommitmentPrefix) => uint64, Proof ``` @@ -523,11 +544,11 @@ For clients of state machines with Merklized state trees, these functions can be root stored in the `ClientState`, to verify presence or absence of particular key/value pairs in state at particular heights in accordance with [ICS 23](../ics-023-vector-commitments). ```typescript -type verifyMembership = (ClientState, uint64, CommitmentProof, Path, Value) => boolean +type verifyMembership = (ClientState, Height, CommitmentProof, Path, Value) => boolean ``` ```typescript -type verifyNonMembership = (ClientState, uint64, CommitmentProof, Path) => boolean +type verifyNonMembership = (ClientState, Height, CommitmentProof, Path) => boolean ``` ### Sub-protocols @@ -637,6 +658,17 @@ The client-specific types are then defined as follows: - `checkMisbehaviourAndUpdateState` checks for two headers with the same height & different commitment roots, then mutates the internal state ```typescript +type Height = uint64 + +function compare(h1: Height, h2: Height): Ord { + if h1 < h2 + return LT + else if h1 === h2 + return EQ + else + return GT +} + interface ClientState { frozen: boolean pastPublicKeys: Set From a00c2b9918d23a1b62c278de0f80ea8e5f3d1e48 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 8 Jul 2020 16:18:02 +0200 Subject: [PATCH 02/11] `compare` requires two heights --- spec/ics-002-client-semantics/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/ics-002-client-semantics/README.md b/spec/ics-002-client-semantics/README.md index c45a6eab4..db2249010 100644 --- a/spec/ics-002-client-semantics/README.md +++ b/spec/ics-002-client-semantics/README.md @@ -272,7 +272,7 @@ enum Ord { GT } -type compare = (height: Height) => Ord +type compare = (h1: Height, h2: Height) => Ord ``` A height is either `LT` (less than), `EQ` (equal to), or `GT` (greater than) another height. From e368c98ca4a259a755837cb5dd9e349e00a59252 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 9 Jul 2020 15:01:28 +0200 Subject: [PATCH 03/11] Update most of spec docs --- spec.pdf | Bin 132 -> 132 bytes spec/ics-002-client-semantics/README.md | 18 +++++++---- spec/ics-003-connection-semantics/README.md | 30 +++++++++--------- .../README.md | 18 +++++------ spec/ics-024-host-requirements/README.md | 8 ++--- 5 files changed, 39 insertions(+), 35 deletions(-) diff --git a/spec.pdf b/spec.pdf index 19d55c150c3f32c643ece8f743be588c9d841648..21993836d98416babf87c8a5745beaace3e68d63 100644 GIT binary patch delta 85 zcmWN_yAgmO3;@uxWeP_KBmuI7JCF}|)^^qm;K)+E=B;BLU%j-Y{ ge?4j$GzUgNji-cm;o1sXWSthA?(2zNp)LjK1I=(2k^lez diff --git a/spec/ics-002-client-semantics/README.md b/spec/ics-002-client-semantics/README.md index db2249010..04b0c4190 100644 --- a/spec/ics-002-client-semantics/README.md +++ b/spec/ics-002-client-semantics/README.md @@ -277,6 +277,10 @@ type compare = (h1: Height, h2: Height) => Ord A height is either `LT` (less than), `EQ` (equal to), or `GT` (greater than) another height. +`>=`, `>`, `===`, `<`, `<=` are defined through the rest of this specification as aliases to `compare`. + +There must also be a zero-element for a height type, referred to as `0`, which is less than all non-zero heights. + #### ClientState `ClientState` is an opaque data structure defined by a client type. @@ -728,7 +732,7 @@ function checkValidityAndUpdateState( function verifyClientConsensusState( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, clientIdentifier: Identifier, @@ -740,7 +744,7 @@ function verifyClientConsensusState( function verifyConnectionState( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, connectionIdentifier: Identifier, @@ -752,7 +756,7 @@ function verifyConnectionState( function verifyChannelState( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -765,7 +769,7 @@ function verifyChannelState( function verifyPacketData( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -779,7 +783,7 @@ function verifyPacketData( function verifyPacketAcknowledgement( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -793,7 +797,7 @@ function verifyPacketAcknowledgement( function verifyPacketAcknowledgementAbsence( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -806,7 +810,7 @@ function verifyPacketAcknowledgementAbsence( function verifyNextSequenceRecv( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, diff --git a/spec/ics-003-connection-semantics/README.md b/spec/ics-003-connection-semantics/README.md index b0c697a3e..843b66424 100644 --- a/spec/ics-003-connection-semantics/README.md +++ b/spec/ics-003-connection-semantics/README.md @@ -134,10 +134,10 @@ instead of directly calling the verification functions on the client. ```typescript function verifyClientConsensusState( connection: ConnectionEnd, - height: uint64, + height: Height, proof: CommitmentProof, clientIdentifier: Identifier, - consensusStateHeight: uint64, + consensusStateHeight: Height, consensusState: ConsensusState) { client = queryClient(connection.clientIdentifier) return client.verifyClientConsensusState(connection, height, connection.counterpartyPrefix, proof, clientIdentifier, consensusStateHeight, consensusState) @@ -145,7 +145,7 @@ function verifyClientConsensusState( function verifyConnectionState( connection: ConnectionEnd, - height: uint64, + height: Height, proof: CommitmentProof, connectionIdentifier: Identifier, connectionEnd: ConnectionEnd) { @@ -155,7 +155,7 @@ function verifyConnectionState( function verifyChannelState( connection: ConnectionEnd, - height: uint64, + height: Height, proof: CommitmentProof, portIdentifier: Identifier, channelIdentifier: Identifier, @@ -166,11 +166,11 @@ function verifyChannelState( function verifyPacketData( connection: ConnectionEnd, - height: uint64, + height: Height, proof: CommitmentProof, portIdentifier: Identifier, channelIdentifier: Identifier, - sequence: uint64, + sequence: Height, data: bytes) { client = queryClient(connection.clientIdentifier) return client.verifyPacketData(connection, height, connection.counterpartyPrefix, proof, portIdentifier, channelIdentifier, data) @@ -178,7 +178,7 @@ function verifyPacketData( function verifyPacketAcknowledgement( connection: ConnectionEnd, - height: uint64, + height: Height, proof: CommitmentProof, portIdentifier: Identifier, channelIdentifier: Identifier, @@ -190,7 +190,7 @@ function verifyPacketAcknowledgement( function verifyPacketAcknowledgementAbsence( connection: ConnectionEnd, - height: uint64, + height: Height, proof: CommitmentProof, portIdentifier: Identifier, channelIdentifier: Identifier, @@ -201,7 +201,7 @@ function verifyPacketAcknowledgementAbsence( function verifyNextSequenceRecv( connection: ConnectionEnd, - height: uint64, + height: Height, proof: CommitmentProof, portIdentifier: Identifier, channelIdentifier: Identifier, @@ -212,7 +212,7 @@ function verifyNextSequenceRecv( function getTimestampAtHeight( connection: ConnectionEnd, - height: uint64) { + height: Height) { client = queryClient(connection.clientIdentifier) return client.queryConsensusState(height).getTimestamp() } @@ -313,8 +313,8 @@ function connOpenTry( counterpartyVersions: string[], proofInit: CommitmentProof, proofConsensus: CommitmentProof, - proofHeight: uint64, - consensusHeight: uint64) { + proofHeight: Height, + consensusHeight: Height) { abortTransactionUnless(validateConnectionIdentifier(desiredIdentifier)) abortTransactionUnless(consensusHeight < getCurrentHeight()) expectedConsensusState = getConsensusState(consensusHeight) @@ -349,8 +349,8 @@ function connOpenAck( version: string, proofTry: CommitmentProof, proofConsensus: CommitmentProof, - proofHeight: uint64, - consensusHeight: uint64) { + proofHeight: Height, + consensusHeight: Height) { abortTransactionUnless(consensusHeight < getCurrentHeight()) connection = provableStore.get(connectionPath(identifier)) abortTransactionUnless(connection.state === INIT || connection.state === TRYOPEN) @@ -374,7 +374,7 @@ function connOpenAck( function connOpenConfirm( identifier: Identifier, proofAck: CommitmentProof, - proofHeight: uint64) { + proofHeight: Height) { connection = provableStore.get(connectionPath(identifier)) abortTransactionUnless(connection.state === TRYOPEN) expected = ConnectionEnd{OPEN, identifier, getCommitmentPrefix(), connection.counterpartyClientIdentifier, diff --git a/spec/ics-004-channel-and-packet-semantics/README.md b/spec/ics-004-channel-and-packet-semantics/README.md index 1706e8098..afa77adee 100644 --- a/spec/ics-004-channel-and-packet-semantics/README.md +++ b/spec/ics-004-channel-and-packet-semantics/README.md @@ -103,7 +103,7 @@ A `Packet`, in the interblockchain communication protocol, is a particular inter ```typescript interface Packet { sequence: uint64 - timeoutHeight: uint64 + timeoutHeight: Height timeoutTimestamp: uint64 sourcePort: Identifier sourceChannel: Identifier @@ -310,7 +310,7 @@ function chanOpenTry( version: string, counterpartyVersion: string, proofInit: CommitmentProof, - proofHeight: uint64): CapabilityKey { + proofHeight: Height): CapabilityKey { abortTransactionUnless(validateChannelIdentifier(portIdentifier, channelIdentifier)) abortTransactionUnless(connectionHops.length === 1) // for v1 of the IBC protocol previous = provableStore.get(channelPath(portIdentifier, channelIdentifier)) @@ -356,7 +356,7 @@ function chanOpenAck( channelIdentifier: Identifier, counterpartyVersion: string, proofTry: CommitmentProof, - proofHeight: uint64) { + proofHeight: Height) { channel = provableStore.get(channelPath(portIdentifier, channelIdentifier)) abortTransactionUnless(channel.state === INIT || channel.state === TRYOPEN) abortTransactionUnless(authenticateCapability(channelCapabilityPath(portIdentifier, channelIdentifier), capability)) @@ -386,7 +386,7 @@ function chanOpenConfirm( portIdentifier: Identifier, channelIdentifier: Identifier, proofAck: CommitmentProof, - proofHeight: uint64) { + proofHeight: Height) { channel = provableStore.get(channelPath(portIdentifier, channelIdentifier)) abortTransactionUnless(channel !== null) abortTransactionUnless(channel.state === TRYOPEN) @@ -450,7 +450,7 @@ function chanCloseConfirm( portIdentifier: Identifier, channelIdentifier: Identifier, proofInit: CommitmentProof, - proofHeight: uint64) { + proofHeight: Height) { abortTransactionUnless(authenticateCapability(channelCapabilityPath(portIdentifier, channelIdentifier), capability)) channel = provableStore.get(channelPath(portIdentifier, channelIdentifier)) abortTransactionUnless(channel !== null) @@ -572,7 +572,7 @@ The IBC handler performs the following steps in order: function recvPacket( packet: OpaquePacket, proof: CommitmentProof, - proofHeight: uint64, + proofHeight: Height, acknowledgement: bytes): Packet { channel = provableStore.get(channelPath(packet.destPort, packet.destChannel)) @@ -635,7 +635,7 @@ function acknowledgePacket( packet: OpaquePacket, acknowledgement: bytes, proof: CommitmentProof, - proofHeight: uint64): Packet { + proofHeight: Height): Packet { // abort transaction unless that channel is open, calling module owns the associated port, and the packet fields match channel = provableStore.get(channelPath(packet.sourcePort, packet.sourceChannel)) @@ -705,7 +705,7 @@ If relations are enforced between timeout heights of subsequent packets, safe bu function timeoutPacket( packet: OpaquePacket, proof: CommitmentProof, - proofHeight: uint64, + proofHeight: Height, nextSequenceRecv: Maybe): Packet { channel = provableStore.get(channelPath(packet.sourcePort, packet.sourceChannel)) @@ -776,7 +776,7 @@ function timeoutOnClose( packet: Packet, proof: CommitmentProof, proofClosed: CommitmentProof, - proofHeight: uint64, + proofHeight: Height, nextSequenceRecv: Maybe): Packet { channel = provableStore.get(channelPath(packet.sourcePort, packet.sourceChannel)) diff --git a/spec/ics-024-host-requirements/README.md b/spec/ics-024-host-requirements/README.md index ae13b1c04..388a4279a 100644 --- a/spec/ics-024-host-requirements/README.md +++ b/spec/ics-024-host-requirements/README.md @@ -155,7 +155,7 @@ Represented spatially, the layout of modules & their included specifications on Host state machines MUST provide the ability to introspect their current height, with `getCurrentHeight`: ``` -type getCurrentHeight = () => uint64 +type getCurrentHeight = () => Height ``` Host state machines MUST define a unique `ConsensusState` type fulfilling the requirements of [ICS 2](../ics-002-client-semantics), with a canonical binary serialisation. @@ -163,7 +163,7 @@ Host state machines MUST define a unique `ConsensusState` type fulfilling the re Host state machines MUST provide the ability to introspect their own consensus state, with `getConsensusState`: ```typescript -type getConsensusState = (height: uint64) => ConsensusState +type getConsensusState = (height: Height) => ConsensusState ``` `getConsensusState` MUST return the consensus state for at least some number `n` of contiguous recent heights, where `n` is constant for the host state machine. Heights older than `n` MAY be safely pruned (causing future calls to fail for those heights). @@ -171,7 +171,7 @@ type getConsensusState = (height: uint64) => ConsensusState Host state machines MUST provide the ability to introspect this stored recent consensus state count `n`, with `getStoredRecentConsensusStateCount`: ```typescript -type getStoredRecentConsensusStateCount = () => uint64 +type getStoredRecentConsensusStateCount = () => Height ``` ### Commitment path introspection @@ -277,7 +277,7 @@ type emitLogEntry = (topic: string, data: []byte) => void The function `queryByTopic` can be called by an external process (such as a relayer) to retrieve all log entries associated with a given topic written by transactions which were executed at a given height. ```typescript -type queryByTopic = (height: uint64, topic: string) => []byte[] +type queryByTopic = (height: Height, topic: string) => []byte[] ``` More complex query functionality MAY also be supported, and may allow for more efficient relayer process queries, but is not required. From 835b6ab09033aec1dbfc5120ce29022901f9e8b5 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 9 Jul 2020 17:50:09 +0200 Subject: [PATCH 04/11] Continue updating spec docs --- spec.pdf | Bin 132 -> 132 bytes spec/ics-006-solo-machine-client/README.md | 4 +++ spec/ics-007-tendermint-client/README.md | 4 +++ .../ics-020-fungible-token-transfer/README.md | 2 +- spec/ics-026-routing-module/README.md | 28 +++++++++--------- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/spec.pdf b/spec.pdf index 21993836d98416babf87c8a5745beaace3e68d63..437bf0e144d1d79c9979e49df945f4dd2b14f6b0 100644 GIT binary patch delta 85 zcmV~$u?@f=317zgf?X=T}%gmc8={je@@X!|GBn2*L ebgQR21HH^OpsU6y^r&1kQ{?A<=h3BW0rC$X } ``` @@ -623,7 +623,7 @@ function handlePacketTimeout(datagram: PacketTimeout) { interface PacketTimeoutOnClose { packet: Packet proof: CommitmentProof - proofHeight: uint64 + proofHeight: Height } ``` @@ -645,7 +645,7 @@ function handlePacketTimeoutOnClose(datagram: PacketTimeoutOnClose) { interface PacketCleanup { packet: Packet proof: CommitmentProof - proofHeight: uint64 + proofHeight: Height nextSequenceRecvOrAcknowledgement: Either } ``` From cca2d64ac4b13bba6c2250444a7ef0b49083250d Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 9 Jul 2020 18:00:37 +0200 Subject: [PATCH 05/11] Start updating ICS 7 --- misc/aspell_dict | 4 +- spec.pdf | Bin 132 -> 132 bytes spec/ics-007-tendermint-client/README.md | 58 +++++++++++++++++------ 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/misc/aspell_dict b/misc/aspell_dict index 2ce5271d8..95dc83f25 100644 --- a/misc/aspell_dict +++ b/misc/aspell_dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 608 +personal_ws-1.1 en 610 ABCI ABI ADR @@ -42,6 +42,7 @@ Devs Directionality Dogemos EIP +EQ ERC EVM EmptyLogStore @@ -86,6 +87,7 @@ OpHeaderProof OpValidityPredicate OpValidityPredicateBase OpaquePacket +Ord POC PacketAcknowledgement PacketCleanup diff --git a/spec.pdf b/spec.pdf index 437bf0e144d1d79c9979e49df945f4dd2b14f6b0..6bd58bc6683197784233bf77af722333533f6ef0 100644 GIT binary patch delta 85 zcmV~$yAgmO3;@uxWeP{gj|7%*2NJ@awVgEsII{2UV;`ShNa0{=EZ86kCJ~O6?Cemx fwmN0ZDbWWJaUh4JM710YzBtNrzj;17zgf?X=T}%gmc8={je@@X!|GBn2*L ebgQR21HH^OpsU6y^r&1kQ{?A<=h3BW0rC$X> trustingPeriod: uint64 unbondingPeriod: uint64 - latestHeight: uint64 + latestHeight: Height latestTimestamp: uint64 frozenHeight: Maybe } @@ -85,7 +85,31 @@ interface ConsensusState { ### Height -The height of a Tendermint client is just a `uint64`, with the usual comparison operations. +The height of a Tendermint client consists of two `uint64`s: the epoch number, and the height in the epoch. + +```typescript +interface Height { + epochNumber: uint64 + epochHeight: uint64 +} +``` + +Comparison between heights is implemented as follows: + +```typescript +function compare(a: TendermintHeight, b: TendermintHeight): Ord { + if (a.epochNumber < b.epochNumber) + return LT + else if (a.epochNumber === b.epochNumber) + if (a.epochHeight < b.epochHeight) + return LT + else if (a.epochHeight === b.epochHeight) + return EQ + return GT +} +``` + +This is designed to allow the height to reset to `0` while the epoch number increases by one in order to preserve timeouts through zero-height upgrades. ### Headers @@ -108,7 +132,7 @@ Tendermint client `Evidence` consists of two headers at the same height both of ```typescript interface Evidence { - fromHeight: uint64 + fromHeight: Height h1: Header h2: Header } @@ -121,7 +145,7 @@ Tendermint client initialisation requires a (subjectively chosen) latest consens ```typescript function initialise( consensusState: ConsensusState, validatorSet: List>, - height: uint64, trustingPeriod: uint64, unbondingPeriod: uint64): ClientState { + height: Height, trustingPeriod: uint64, unbondingPeriod: uint64): ClientState { assert(trustingPeriod < unbondingPeriod) assert(height > 0) set("clients/{identifier}/consensusStates/{height}", consensusState) @@ -139,7 +163,7 @@ function initialise( The Tendermint client `latestClientHeight` function returns the latest stored height, which is updated every time a new (more recent) header is validated. ```typescript -function latestClientHeight(clientState: ClientState): uint64 { +function latestClientHeight(clientState: ClientState): Height { return clientState.latestHeight } ``` @@ -151,7 +175,13 @@ Tendermint client validity checking uses the bisection algorithm described in th ```typescript function checkValidityAndUpdateState( clientState: ClientState, + epoch: uint64, header: Header) { + // assert epoch is correct + assert(epoch === clientState.currentHeight.epoch) + + // TODO: check epoch encoded in chain ID ? + // assert trusting period has not yet passed. This should fatally terminate a connection. assert(currentTimestamp() - clientState.latestTimestamp < clientState.trustingPeriod) // assert header timestamp is less than trust period in the future. This should be resolved with an intermediate header. @@ -211,11 +241,11 @@ Tendermint client state verification functions check a Merkle proof against a pr ```typescript function verifyClientConsensusState( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, clientIdentifier: Identifier, - consensusStateHeight: uint64, + consensusStateHeight: Height, consensusState: ConsensusState) { path = applyPrefix(prefix, "clients/{clientIdentifier}/consensusState/{consensusStateHeight}") // check that the client is at a sufficient height @@ -230,7 +260,7 @@ function verifyClientConsensusState( function verifyConnectionState( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, connectionIdentifier: Identifier, @@ -248,7 +278,7 @@ function verifyConnectionState( function verifyChannelState( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -267,7 +297,7 @@ function verifyChannelState( function verifyPacketData( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -287,7 +317,7 @@ function verifyPacketData( function verifyPacketAcknowledgement( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -307,7 +337,7 @@ function verifyPacketAcknowledgement( function verifyPacketAcknowledgementAbsence( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -326,7 +356,7 @@ function verifyPacketAcknowledgementAbsence( function verifyNextSequenceRecv( clientState: ClientState, - height: uint64, + height: Height, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, From 8a81e196720b65fbf3e0e79f5aaa327f50d28f96 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Wed, 15 Jul 2020 15:33:27 +0200 Subject: [PATCH 06/11] ... (wip) --- spec/ics-007-tendermint-client/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/spec/ics-007-tendermint-client/README.md b/spec/ics-007-tendermint-client/README.md index 692a3ce65..d79cba18a 100644 --- a/spec/ics-007-tendermint-client/README.md +++ b/spec/ics-007-tendermint-client/README.md @@ -234,6 +234,19 @@ function checkMisbehaviourAndUpdateState( } ``` +### Upgrades + +The chain which this light client is tracking can elect to write a special pre-determined key in state to allow the light client to update its client state (e.g. with a new chain ID or epoch) in preparation for an upgrade. + +```typescript +function upgradeClientState( + newClientState: ClientState, + proof: CommitmentPrefix) { + // check proof of updated client state in state at predetermined commitment prefix and key + // update client state +} +``` + ### State verification functions Tendermint client state verification functions check a Merkle proof against a previously validated commitment root. From 82d8418cae7898f05e0d8584c1b6c8b04c514864 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 16 Jul 2020 14:27:04 +0200 Subject: [PATCH 07/11] Update ICS 7 --- misc/aspell_dict | 3 +- spec.pdf | Bin 132 -> 132 bytes spec/ics-007-tendermint-client/README.md | 50 ++++++++++++++++++----- 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/misc/aspell_dict b/misc/aspell_dict index 95dc83f25..f09c0aed6 100644 --- a/misc/aspell_dict +++ b/misc/aspell_dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 610 +personal_ws-1.1 en 611 ABCI ABI ADR @@ -449,6 +449,7 @@ portOnB portOnD portPath positionally +pre precommit precommits prefixedPath diff --git a/spec.pdf b/spec.pdf index 6bd58bc6683197784233bf77af722333533f6ef0..7f040f2151fc6f41f95a2a649f2755376c4c507b 100644 GIT binary patch delta 85 zcmWN|yAgmO3;@uhWeP_iKoYWqJD3l5)^^qm;K)+Hdn>K<(OK2pqqb3)c-r))5~#%m g87#U2fs3)vUU^<5y#e7UdxUPv&;7=MK*E*s4;e-mLI3~& delta 85 zcmV~$yAgmO3;@uxWeP{gj|7%*2NJ@awVgEsII{2UV;`ShNa0{=EZ86kCJ~O6?Cemx fwmN0ZDbWWJaUh4JM710YzBtNrzj;> + trustLevel: Rational trustingPeriod: uint64 unbondingPeriod: uint64 latestHeight: Height latestTimestamp: uint64 frozenHeight: Maybe + upgradeCommitmentPrefix: CommitmentPrefix + upgradeKey: []byte + maxClockDrift: uint64 + proofSpecs: []ProofSpec } ``` @@ -144,18 +149,26 @@ Tendermint client initialisation requires a (subjectively chosen) latest consens ```typescript function initialise( - consensusState: ConsensusState, validatorSet: List>, - height: Height, trustingPeriod: uint64, unbondingPeriod: uint64): ClientState { + consensusState: ConsensusState, validatorSet: List>, trustLevel: Fraction, + height: Height, trustingPeriod: uint64, unbondingPeriod: uint64, + upgradeCommitmentPrefix: CommitmentPrefix, upgradeKey: []byte, + maxClockDrift: uint64, proofSpecs: []ProofSpec): ClientState { assert(trustingPeriod < unbondingPeriod) assert(height > 0) + assert(trustLevel > 0 && trustLevel < 1) set("clients/{identifier}/consensusStates/{height}", consensusState) return ClientState{ validatorSet, + trustLevel, latestHeight: height, latestTimestamp: consensusState.timestamp, trustingPeriod, unbondingPeriod, - frozenHeight: null + frozenHeight: null, + upgradeCommitmentPrefix, + upgradeKey, + maxClockDrift, + proofSpecs } } ``` @@ -191,7 +204,7 @@ function checkValidityAndUpdateState( // assert header height is newer than any we know assert(header.height > clientState.latestHeight) // call the `verify` function - assert(verify(clientState.validatorSet, clientState.latestHeight, header)) + assert(verify(clientState.validatorSet, clientState.latestHeight, clientState.trustingPeriod, maxClockDrift, header)) // update validator set clientState.validatorSet = header.validatorSet // update latest height @@ -238,19 +251,36 @@ function checkMisbehaviourAndUpdateState( The chain which this light client is tracking can elect to write a special pre-determined key in state to allow the light client to update its client state (e.g. with a new chain ID or epoch) in preparation for an upgrade. +As the client state change will be performed immediately, once the new client state information is written to the predetermined key, the client will no longer be able to follow blocks on the old chain, so it must upgrade promptly. + ```typescript function upgradeClientState( + clientState: ClientState, newClientState: ClientState, + height: Height, proof: CommitmentPrefix) { // check proof of updated client state in state at predetermined commitment prefix and key + path = applyPrefix(clientState.upgradeCommitmentPrefix, clientState.upgradeKey) + // check that the client is at a sufficient height + assert(clientState.latestHeight >= height) + // check that the client is unfrozen or frozen at a higher height + assert(clientState.frozenHeight === null || clientState.frozenHeight > height) + // fetch the previously verified commitment root & verify membership + root = get("clients/{identifier}/consensusStates/{height}") + // verify that the provided consensus state has been stored + assert(root.verifyMembership(path, clientState, proof)) // update client state -} + clientState = newClientState + set("clients/{identifier}", clientState) +} ``` ### State verification functions Tendermint client state verification functions check a Merkle proof against a previously validated commitment root. +These functions utilise the `proofSpecs` with which the client was initialised. + ```typescript function verifyClientConsensusState( clientState: ClientState, @@ -305,7 +335,7 @@ function verifyChannelState( // fetch the previously verified commitment root & verify membership root = get("clients/{identifier}/consensusStates/{height}") // verify that the provided channel end has been stored - assert(root.verifyMembership(path, channelEnd, proof)) + assert(root.verifyMembership(clientState.proofSpecs, path, channelEnd, proof)) } function verifyPacketData( @@ -325,7 +355,7 @@ function verifyPacketData( // fetch the previously verified commitment root & verify membership root = get("clients/{identifier}/consensusStates/{height}") // verify that the provided commitment has been stored - assert(root.verifyMembership(path, hash(data), proof)) + assert(root.verifyMembership(clientState.proofSpecs, path, hash(data), proof)) } function verifyPacketAcknowledgement( @@ -345,7 +375,7 @@ function verifyPacketAcknowledgement( // fetch the previously verified commitment root & verify membership root = get("clients/{identifier}/consensusStates/{height}") // verify that the provided acknowledgement has been stored - assert(root.verifyMembership(path, hash(acknowledgement), proof)) + assert(root.verifyMembership(clientState.proofSpecs, path, hash(acknowledgement), proof)) } function verifyPacketAcknowledgementAbsence( @@ -364,7 +394,7 @@ function verifyPacketAcknowledgementAbsence( // fetch the previously verified commitment root & verify membership root = get("clients/{identifier}/consensusStates/{height}") // verify that no acknowledgement has been stored - assert(root.verifyNonMembership(path, proof)) + assert(root.verifyNonMembership(clientState.proofSpecs, path, proof)) } function verifyNextSequenceRecv( @@ -383,7 +413,7 @@ function verifyNextSequenceRecv( // fetch the previously verified commitment root & verify membership root = get("clients/{identifier}/consensusStates/{height}") // verify that the nextSequenceRecv is as claimed - assert(root.verifyMembership(path, nextSequenceRecv, proof)) + assert(root.verifyMembership(clientState.proofSpecs, path, nextSequenceRecv, proof)) } ``` From 3a0671058f985b5e85b6982d9cc40b4f1c001e27 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Thu, 16 Jul 2020 14:32:24 +0200 Subject: [PATCH 08/11] Add epoch check --- misc/aspell_dict | 10 ++++++++++ spec/ics-007-tendermint-client/README.md | 10 ++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/misc/aspell_dict b/misc/aspell_dict index f09c0aed6..5cb8ffebd 100644 --- a/misc/aspell_dict +++ b/misc/aspell_dict @@ -1,6 +1,16 @@ personal_ws-1.1 en 611 ABCI ABI +trustLevel +upgradeCommitmentPrefix +newClientState +upgradeKey +maxClockDrift +proofSpecs +ProofSpec +epochNumber +epochHeight +TendermintHeight ADR Agoric Agoric's diff --git a/spec/ics-007-tendermint-client/README.md b/spec/ics-007-tendermint-client/README.md index 69ca7351e..8226008ba 100644 --- a/spec/ics-007-tendermint-client/README.md +++ b/spec/ics-007-tendermint-client/README.md @@ -62,6 +62,7 @@ The Tendermint client state tracks the current epoch, current validator set, tru ```typescript interface ClientState { + chainID: string validatorSet: List> trustLevel: Rational trustingPeriod: uint64 @@ -149,7 +150,8 @@ Tendermint client initialisation requires a (subjectively chosen) latest consens ```typescript function initialise( - consensusState: ConsensusState, validatorSet: List>, trustLevel: Fraction, + chainID: string, consensusState: ConsensusState, + validatorSet: List>, trustLevel: Fraction, height: Height, trustingPeriod: uint64, unbondingPeriod: uint64, upgradeCommitmentPrefix: CommitmentPrefix, upgradeKey: []byte, maxClockDrift: uint64, proofSpecs: []ProofSpec): ClientState { @@ -158,6 +160,7 @@ function initialise( assert(trustLevel > 0 && trustLevel < 1) set("clients/{identifier}/consensusStates/{height}", consensusState) return ClientState{ + chainID, validatorSet, trustLevel, latestHeight: height, @@ -192,9 +195,8 @@ function checkValidityAndUpdateState( header: Header) { // assert epoch is correct assert(epoch === clientState.currentHeight.epoch) - - // TODO: check epoch encoded in chain ID ? - + // check that epoch is encoded correctly in chain ID + assert(epoch === clientState.chainID.regex('[a-z]*-(0)')) // assert trusting period has not yet passed. This should fatally terminate a connection. assert(currentTimestamp() - clientState.latestTimestamp < clientState.trustingPeriod) // assert header timestamp is less than trust period in the future. This should be resolved with an intermediate header. From 4af88f0ec143c8c6e13f3e2c598d848b1c7b06e6 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 17 Jul 2020 15:00:13 +0200 Subject: [PATCH 09/11] Update spec/ics-007-tendermint-client/README.md Co-authored-by: Aditya --- spec/ics-007-tendermint-client/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/ics-007-tendermint-client/README.md b/spec/ics-007-tendermint-client/README.md index 8226008ba..aa93ed13a 100644 --- a/spec/ics-007-tendermint-client/README.md +++ b/spec/ics-007-tendermint-client/README.md @@ -270,7 +270,7 @@ function upgradeClientState( // fetch the previously verified commitment root & verify membership root = get("clients/{identifier}/consensusStates/{height}") // verify that the provided consensus state has been stored - assert(root.verifyMembership(path, clientState, proof)) + assert(root.verifyMembership(path, newClientState, proof)) // update client state clientState = newClientState set("clients/{identifier}", clientState) From c691b70f0d2031fb58904f657185f8c4b96566f6 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 17 Jul 2020 15:16:47 +0200 Subject: [PATCH 10/11] Address @adityasripal comments --- misc/aspell_dict | 22 +++++++++++----------- spec.pdf | Bin 132 -> 132 bytes spec/ics-007-tendermint-client/README.md | 4 +++- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/misc/aspell_dict b/misc/aspell_dict index 5cb8ffebd..c69e2491d 100644 --- a/misc/aspell_dict +++ b/misc/aspell_dict @@ -1,16 +1,6 @@ -personal_ws-1.1 en 611 +personal_ws-1.1 en 621 ABCI ABI -trustLevel -upgradeCommitmentPrefix -newClientState -upgradeKey -maxClockDrift -proofSpecs -ProofSpec -epochNumber -epochHeight -TendermintHeight ADR Agoric Agoric's @@ -109,6 +99,7 @@ PacketTimeoutOnClose Polkadot Pre Privkey +ProofSpec Protobuf PubKey QUIC @@ -130,6 +121,7 @@ TCP TLS TRYOPEN Tendermint +TendermintHeight Tezos TransferCoins Tribble @@ -301,6 +293,8 @@ emitLogEntry encodings endian enum +epochHeight +epochNumber errorCode escrowAccount escrowAddress @@ -391,6 +385,7 @@ localEnd logEntry lookupModule loopback +maxClockDrift md mempools middled @@ -406,6 +401,7 @@ newCallbacks newCapability newCapabilityKey newCapabilityPath +newClientState newPublicKey newState newpubkey @@ -472,6 +468,7 @@ proofConsensus proofHeight proofInit proofNonMembership +proofSpecs proofTimeout proofTry proto @@ -568,6 +565,7 @@ topologies tradeoff transactional transferPort +trustLevel trustingPeriod tx txBytes @@ -591,6 +589,8 @@ updateClient updateConsensusState updatePort updateable +upgradeCommitmentPrefix +upgradeKey validRound validValue validateChannelIdentifier diff --git a/spec.pdf b/spec.pdf index 7f040f2151fc6f41f95a2a649f2755376c4c507b..8296cb85c8354f1315f33043b1521a5992baf783 100644 GIT binary patch delta 85 zcmWN>u@QhE3K<(OK2pqqb3)c-r))5~#%m g87#U2fs3)vUU^<5y#e7UdxUPv&;7=MK*E*s4;e-mLI3~& diff --git a/spec/ics-007-tendermint-client/README.md b/spec/ics-007-tendermint-client/README.md index aa93ed13a..88f03c896 100644 --- a/spec/ics-007-tendermint-client/README.md +++ b/spec/ics-007-tendermint-client/README.md @@ -261,9 +261,11 @@ function upgradeClientState( newClientState: ClientState, height: Height, proof: CommitmentPrefix) { + // check that the epoch has been incremented + assert(newClientState.latestHeight.epochNumber > clientState.latestHeight.epochNumber) // check proof of updated client state in state at predetermined commitment prefix and key path = applyPrefix(clientState.upgradeCommitmentPrefix, clientState.upgradeKey) - // check that the client is at a sufficient height + // check that the client is at a sufficient height assert(clientState.latestHeight >= height) // check that the client is unfrozen or frozen at a higher height assert(clientState.frozenHeight === null || clientState.frozenHeight > height) From 87ec79b4e01252b3e0a6398f3dab0b66447834e6 Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Tue, 21 Jul 2020 11:28:53 +0200 Subject: [PATCH 11/11] Spellcheck --- misc/aspell_dict | 4 +++- spec.pdf | Bin 132 -> 132 bytes 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/misc/aspell_dict b/misc/aspell_dict index c69e2491d..074a8611b 100644 --- a/misc/aspell_dict +++ b/misc/aspell_dict @@ -1,4 +1,4 @@ -personal_ws-1.1 en 621 +personal_ws-1.1 en 623 ABCI ABI ADR @@ -175,6 +175,7 @@ callbackKey callbackPath calldata callingModuleIdentifier +chainID chainType chanCloseConfirm chanCloseInit @@ -589,6 +590,7 @@ updateClient updateConsensusState updatePort updateable +upgradeClientState upgradeCommitmentPrefix upgradeKey validRound diff --git a/spec.pdf b/spec.pdf index 8296cb85c8354f1315f33043b1521a5992baf783..00c3db36b50d845a000af3b204f8f7aafb8a26d6 100644 GIT binary patch delta 85 zcmV~$!3}^g2nEpe+9@0Xnh&%~xI+b+c=qPG130qp?Xt_Kr@$E;#61)Q4L6-vU}UP~ engK&o?KXWSu{8`hA!$e-9;-h0n}@J5LFy0eau(MB delta 85 zcmWN>u@QhE3