ics | title | stage | category | kind | implements | author | created | modified |
---|---|---|---|---|---|---|---|---|
6 |
Solo Machine Client |
draft |
IBC/TAO |
instantiation |
2 |
Christopher Goes <[email protected]> |
2019-12-09 |
2019-12-09 |
This specification document describes a client (verification algorithm) for a solo machine with a single updateable public key which implements the ICS 2 interface.
Solo machines — which might be devices such as phones, browsers, or laptops — might like to interface with other machines & replicated ledgers which speak IBC, and they can do so through the uniform client interface.
Functions & terms are as defined in ICS 2.
This specification must satisfy the client interface defined in ICS 2.
Conceptually, we assume "big table of signatures in the universe" - that signatures produced are public - and incorporate replay protection accordingly.
This specification contains implementations for all of the functions defined by ICS 2.
The ClientState
of a solo machine is simply whether or not the client is frozen.
interface ClientState {
frozen: boolean
}
The ConsensusState
of a solo machine consists of the current public key & sequence number.
interface ConsensusState {
sequence: uint64
publicKey: PublicKey
}
Header
s must only be provided by a solo machine when the machine wishes to update the public key.
interface Header {
sequence: uint64
signature: Signature
newPublicKey: PublicKey
}
Evidence
of solo machine misbehaviour consists of a sequence and two signatures over different messages at that sequence.
interface Evidence {
sequence: uint64
signatureOne: Signature
signatureTwo: Signature
}
The solo machine client initialise
function starts an unfrozen client with the initial consensus state.
function initialise(consensusState: ConsensusState): ClientState {
return {
frozen: false,
consensusState
}
}
The solo machine client checkValidityAndUpdateState
function checks that the currently registered public key has signed over the new public key with the correct sequence.
function checkValidityAndUpdateState(
clientState: ClientState,
header: Header) {
assert(sequence === clientState.consensusState.sequence)
assert(checkSignature(header.newPublicKey, header.sequence, header.signature))
clientState.consensusState.publicKey = header.newPublicKey
clientState.consensusState.sequence++
}
Any duplicate signature on different messages by the current public key freezes a solo machine client.
function checkMisbehaviourAndUpdateState(
clientState: ClientState,
evidence: Evidence) {
h1 = evidence.h1
h2 = evidence.h2
pubkey = clientState.consensusState.publicKey
assert(evidence.h1.signature.data !== evidence.h2.signature.data)
assert(checkSignature(pubkey, evidence.sequence, evidence.h1.signature))
assert(checkSignature(pubkey, evidence.sequence, evidence.h2.signature))
clientState.frozen = true
}
All solo machine client state verification functions simply check a signature, which must be provided by the solo machine.
function verifyClientConsensusState(
clientState: ClientState,
height: uint64,
prefix: CommitmentPrefix,
proof: CommitmentProof,
clientIdentifier: Identifier,
consensusState: ConsensusState) {
path = applyPrefix(prefix, "clients/{clientIdentifier}/consensusState")
abortTransactionUnless(!clientState.frozen)
value = clientState.consensusState.sequence + path + consensusState
assert(checkSignature(clientState.consensusState.pubKey, value, proof))
clientState.consensusState.sequence++
}
function verifyConnectionState(
clientState: ClientState,
height: uint64,
prefix: CommitmentPrefix,
proof: CommitmentProof,
connectionIdentifier: Identifier,
connectionEnd: ConnectionEnd) {
path = applyPrefix(prefix, "connection/{connectionIdentifier}")
abortTransactionUnless(!clientState.frozen)
value = clientState.consensusState.sequence + path + connectionEnd
assert(checkSignature(clientState.consensusState.pubKey, value, proof))
clientState.consensusState.sequence++
}
function verifyChannelState(
clientState: ClientState,
height: uint64,
prefix: CommitmentPrefix,
proof: CommitmentProof,
portIdentifier: Identifier,
channelIdentifier: Identifier,
channelEnd: ChannelEnd) {
path = applyPrefix(prefix, "ports/{portIdentifier}/channels/{channelIdentifier}")
abortTransactionUnless(!clientState.frozen)
value = clientState.consensusState.sequence + path + channelEnd
assert(checkSignature(clientState.consensusState.pubKey, value, proof))
clientState.consensusState.sequence++
}
function verifyPacketCommitment(
clientState: ClientState,
height: uint64,
prefix: CommitmentPrefix,
proof: CommitmentProof,
portIdentifier: Identifier,
channelIdentifier: Identifier,
sequence: uint64,
commitment: bytes) {
path = applyPrefix(prefix, "ports/{portIdentifier}/channels/{channelIdentifier}/packets/{sequence}")
abortTransactionUnless(!clientState.frozen)
value = clientState.consensusState.sequence + path + commitment
assert(checkSignature(clientState.consensusState.pubKey, value, proof))
clientState.consensusState.sequence++
}
function verifyPacketAcknowledgement(
clientState: ClientState,
height: uint64,
prefix: CommitmentPrefix,
proof: CommitmentProof,
portIdentifier: Identifier,
channelIdentifier: Identifier,
sequence: uint64,
acknowledgement: bytes) {
path = applyPrefix(prefix, "ports/{portIdentifier}/channels/{channelIdentifier}/acknowledgements/{sequence}")
abortTransactionUnless(!clientState.frozen)
value = clientState.consensusState.sequence + path + acknowledgement
assert(checkSignature(clientState.consensusState.pubKey, value, proof))
clientState.consensusState.sequence++
}
function verifyPacketAcknowledgementAbsence(
clientState: ClientState,
height: uint64,
prefix: CommitmentPrefix,
proof: CommitmentProof,
portIdentifier: Identifier,
channelIdentifier: Identifier,
sequence: uint64) {
path = applyPrefix(prefix, "ports/{portIdentifier}/channels/{channelIdentifier}/acknowledgements/{sequence}")
abortTransactionUnless(!clientState.frozen)
value = clientState.consensusState.sequence + path
assert(checkSignature(clientState.consensusState.pubKey, value, proof))
clientState.consensusState.sequence++
}
function verifyNextSequenceRecv(
clientState: ClientState,
height: uint64,
prefix: CommitmentPrefix,
proof: CommitmentProof,
portIdentifier: Identifier,
channelIdentifier: Identifier,
nextSequenceRecv: uint64) {
path = applyPrefix(prefix, "ports/{portIdentifier}/channels/{channelIdentifier}/nextSequenceRecv")
abortTransactionUnless(!clientState.frozen)
value = clientState.consensusState.sequence + path + nextSequenceRecv
assert(checkSignature(clientState.consensusState.pubKey, value, proof))
clientState.consensusState.sequence++
}
Instantiates the interface defined in ICS 2.
Not applicable.
Not applicable. Alterations to the client verification algorithm will require a new client standard.
None yet.
None at present.
December 9th, 2019 - Initial version December 17th, 2019 - Final first draft
All content herein is licensed under Apache 2.0.