diff --git a/spec/ics-002-client-semantics/README.md b/spec/ics-002-client-semantics/README.md index eceb534d8..034807e03 100644 --- a/spec/ics-002-client-semantics/README.md +++ b/spec/ics-002-client-semantics/README.md @@ -318,6 +318,8 @@ at a particular finalised height (necessarily associated with a particular commi Client types must define functions to authenticate internal state of the state machine which the client tracks. Internal implementation details may differ (for example, a loopback client could simply read directly from the state and require no proofs). +- The `delayPeriod` is passed to packet-related verification functions in order to allow packets to specify a period which must pass after a header is verified before the packet is allowed to be processed. + ##### Required functions `verifyClientConsensusState` verifies a proof of the consensus state of the specified client stored on the target machine. @@ -366,6 +368,7 @@ type verifyChannelState = ( type verifyPacketData = ( clientState: ClientState, height: Height, + delayPeriod: uint64, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -381,6 +384,7 @@ type verifyPacketData = ( type verifyPacketAcknowledgement = ( clientState: ClientState, height: Height, + delayPeriod: uint64, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -396,6 +400,7 @@ type verifyPacketAcknowledgement = ( type verifyPacketReceiptAbsence = ( clientState: ClientState, height: Height, + delayPeriod: uint64, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -410,6 +415,7 @@ type verifyPacketReceiptAbsence = ( type verifyNextSequenceRecv = ( clientState: ClientState, height: Height, + delayPeriod: uint64, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -770,6 +776,7 @@ function verifyChannelState( function verifyPacketData( clientState: ClientState, height: Height, + delayPeriod: uint64, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -784,6 +791,7 @@ function verifyPacketData( function verifyPacketAcknowledgement( clientState: ClientState, height: Height, + delayPeriod: uint64, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -799,6 +807,7 @@ function verifyPacketReceiptAbsence( clientState: ClientState, height: Height, prefix: CommitmentPrefix, + delayPeriod: uint64, proof: CommitmentProof, portIdentifier: Identifier, channelIdentifier: Identifier, @@ -811,6 +820,7 @@ function verifyPacketReceiptAbsence( function verifyNextSequenceRecv( clientState: ClientState, height: Height, + delayPeriod: uint64, 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 23ac4e72f..9b5e3a26b 100644 --- a/spec/ics-003-connection-semantics/README.md +++ b/spec/ics-003-connection-semantics/README.md @@ -82,6 +82,7 @@ interface ConnectionEnd { clientIdentifier: Identifier counterpartyClientIdentifier: Identifier version: string | []string + delayPeriod: uint64 } ``` @@ -94,6 +95,7 @@ interface ConnectionEnd { - The `counterpartyClientIdentifier` field identifies the client on the counterparty chain associated with this connection. - The `version` field is an opaque string which can be utilised to determine encodings or protocols for channels or packets utilising this connection. If not specified, a default `version` of `""` should be used. +- The `delayPeriod` indicates a period that must elapse after validation of a header before a packet, acknowledgement, proof of receipt, or timeout can be processed. ### Store paths @@ -173,7 +175,7 @@ function verifyPacketData( sequence: Height, data: bytes) { client = queryClient(connection.clientIdentifier) - return client.verifyPacketData(connection, height, connection.counterpartyPrefix, proof, portIdentifier, channelIdentifier, data) + return client.verifyPacketData(connection, height, connection.delayPeriod, connection.counterpartyPrefix, proof, portIdentifier, channelIdentifier, data) } function verifyPacketAcknowledgement( @@ -185,7 +187,7 @@ function verifyPacketAcknowledgement( sequence: uint64, acknowledgement: bytes) { client = queryClient(connection.clientIdentifier) - return client.verifyPacketAcknowledgement(connection, height, connection.counterpartyPrefix, proof, portIdentifier, channelIdentifier, acknowledgement) + return client.verifyPacketAcknowledgement(connection, height, connection.delayPeriod, connection.counterpartyPrefix, proof, portIdentifier, channelIdentifier, acknowledgement) } function verifyPacketReceiptAbsence( @@ -196,7 +198,7 @@ function verifyPacketReceiptAbsence( channelIdentifier: Identifier, sequence: uint64) { client = queryClient(connection.clientIdentifier) - return client.verifyPacketReceiptAbsence(connection, height, connection.counterpartyPrefix, proof, portIdentifier, channelIdentifier) + return client.verifyPacketReceiptAbsence(connection, height, connection.delayPeriod, connection.counterpartyPrefix, proof, portIdentifier, channelIdentifier) } function verifyNextSequenceRecv( @@ -207,7 +209,7 @@ function verifyNextSequenceRecv( channelIdentifier: Identifier, nextSequenceRecv: uint64) { client = queryClient(connection.clientIdentifier) - return client.verifyNextSequenceRecv(connection, height, connection.counterpartyPrefix, proof, portIdentifier, channelIdentifier, nextSequenceRecv) + return client.verifyNextSequenceRecv(connection, height, connection.delayPeriod, connection.counterpartyPrefix, proof, portIdentifier, channelIdentifier, nextSequenceRecv) } function getTimestampAtHeight( @@ -296,7 +298,8 @@ function connOpenInit( counterpartyPrefix: CommitmentPrefix, clientIdentifier: Identifier, counterpartyClientIdentifier: Identifier, - version: string) { + version: string, + delayPeriod: uint64) { identifier = generateIdentifier() abortTransactionUnless(provableStore.get(connectionPath(identifier)) == null) state = INIT @@ -308,7 +311,7 @@ function connOpenInit( versions = getCompatibleVersions() } connection = ConnectionEnd{state, "", counterpartyPrefix, - clientIdentifier, counterpartyClientIdentifier, versions} + clientIdentifier, counterpartyClientIdentifier, versions, delayPeriod} provableStore.set(connectionPath(identifier), connection) addConnectionToClient(clientIdentifier, identifier) } @@ -324,6 +327,7 @@ function connOpenTry( counterpartyClientIdentifier: Identifier, clientIdentifier: Identifier, counterpartyVersions: string[], + delayPeriod: uint64, proofInit: CommitmentProof, proofConsensus: CommitmentProof, proofHeight: Height, @@ -336,7 +340,8 @@ function connOpenTry( previous.counterpartyConnectionIdentifier === "" && previous.counterpartyPrefix === counterpartyPrefix && previous.clientIdentifier === clientIdentifier && - previous.counterpartyClientIdentifier === counterpartyClientIdentifier)) + previous.counterpartyClientIdentifier === counterpartyClientIdentifier && + previous.delayPeriod === delayPeriod)) identifier = previousIdentifier } else { // generate a new identifier if the passed identifier was the sentinel empty-string @@ -345,11 +350,11 @@ function connOpenTry( abortTransactionUnless(consensusHeight < getCurrentHeight()) expectedConsensusState = getConsensusState(consensusHeight) expected = ConnectionEnd{INIT, "", getCommitmentPrefix(), counterpartyClientIdentifier, - clientIdentifier, counterpartyVersions} + clientIdentifier, counterpartyVersions, delayPeriod} versionsIntersection = intersection(counterpartyVersions, previous !== null ? previous.version : getCompatibleVersions()) version = pickVersion(versionsIntersection) // throws if there is no intersection connection = ConnectionEnd{TRYOPEN, counterpartyConnectionIdentifier, counterpartyPrefix, - clientIdentifier, counterpartyClientIdentifier, version} + clientIdentifier, counterpartyClientIdentifier, version, delayPeriod} abortTransactionUnless(connection.verifyConnectionState(proofHeight, proofInit, counterpartyConnectionIdentifier, expected)) abortTransactionUnless(connection.verifyClientConsensusState( proofHeight, proofConsensus, counterpartyClientIdentifier, consensusHeight, expectedConsensusState)) @@ -377,7 +382,7 @@ function connOpenAck( expectedConsensusState = getConsensusState(consensusHeight) expected = ConnectionEnd{TRYOPEN, identifier, getCommitmentPrefix(), connection.counterpartyClientIdentifier, connection.clientIdentifier, - version} + version, connection.delayPeriod} abortTransactionUnless(connection.verifyConnectionState(proofHeight, proofTry, counterpartyIdentifier, expected)) abortTransactionUnless(connection.verifyClientConsensusState( proofHeight, proofConsensus, connection.counterpartyClientIdentifier, consensusHeight, expectedConsensusState)) @@ -398,7 +403,7 @@ function connOpenConfirm( connection = provableStore.get(connectionPath(identifier)) abortTransactionUnless(connection.state === TRYOPEN) expected = ConnectionEnd{OPEN, identifier, getCommitmentPrefix(), connection.counterpartyClientIdentifier, - connection.clientIdentifier, connection.version} + connection.clientIdentifier, connection.version, connection.delayPeriod} abortTransactionUnless(connection.verifyConnectionState(proofHeight, proofAck, connection.counterpartyConnectionIdentifier, expected)) connection.state = OPEN provableStore.set(connectionPath(identifier), connection) diff --git a/spec/ics-007-tendermint-client/README.md b/spec/ics-007-tendermint-client/README.md index 28b29da92..d07a25565 100644 --- a/spec/ics-007-tendermint-client/README.md +++ b/spec/ics-007-tendermint-client/README.md @@ -214,8 +214,9 @@ function checkValidityAndUpdateState( // update latest timestamp clientState.latestTimestamp = header.timestamp // create recorded consensus state, save it - consensusState = ConsensusState{header.validatorSet, header.commitmentRoot, header.timestamp} + consensusState = ConsensusState{header.timestamp, header.validatorSet, header.commitmentRoot} set("clients/{identifier}/consensusStates/{header.height}", consensusState) + set("clients/{identifier}/processedTimes/{header.height}", currentTimestamp()) // save the client set("clients/{identifier}", clientState) } @@ -347,6 +348,7 @@ function verifyChannelState( function verifyPacketData( clientState: ClientState, height: Height, + delayPeriod: uint64, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -358,6 +360,10 @@ function verifyPacketData( 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 processed time + processedTime = get("clients/{identifier}/processedTimes/{height}") + // assert that enough time has elapsed + assert(currentTimestamp() >= processedTime + delayPeriod) // fetch the previously verified commitment root & verify membership root = get("clients/{identifier}/consensusStates/{height}") // verify that the provided commitment has been stored @@ -367,6 +373,7 @@ function verifyPacketData( function verifyPacketAcknowledgement( clientState: ClientState, height: Height, + delayPeriod: uint64, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -378,6 +385,10 @@ function verifyPacketAcknowledgement( 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 processed time + processedTime = get("clients/{identifier}/processedTimes/{height}") + // assert that enough time has elapsed + assert(currentTimestamp() >= processedTime + delayPeriod) // fetch the previously verified commitment root & verify membership root = get("clients/{identifier}/consensusStates/{height}") // verify that the provided acknowledgement has been stored @@ -387,6 +398,7 @@ function verifyPacketAcknowledgement( function verifyPacketReceiptAbsence( clientState: ClientState, height: Height, + delayPeriod: uint64, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -397,6 +409,10 @@ function verifyPacketReceiptAbsence( 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 processed time + processedTime = get("clients/{identifier}/processedTimes/{height}") + // assert that enough time has elapsed + assert(currentTimestamp() >= processedTime + delayPeriod) // fetch the previously verified commitment root & verify membership root = get("clients/{identifier}/consensusStates/{height}") // verify that no acknowledgement has been stored @@ -406,6 +422,7 @@ function verifyPacketReceiptAbsence( function verifyNextSequenceRecv( clientState: ClientState, height: Height, + delayPeriod: uint64, prefix: CommitmentPrefix, proof: CommitmentProof, portIdentifier: Identifier, @@ -416,6 +433,10 @@ function verifyNextSequenceRecv( 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 processed time + processedTime = get("clients/{identifier}/processedTimes/{height}") + // assert that enough time has elapsed + assert(currentTimestamp() >= processedTime + delayPeriod) // fetch the previously verified commitment root & verify membership root = get("clients/{identifier}/consensusStates/{height}") // verify that the nextSequenceRecv is as claimed