diff --git a/misc/aspell_dict b/misc/aspell_dict index 0974bef44..5d76d0b80 100644 --- a/misc/aspell_dict +++ b/misc/aspell_dict @@ -1,6 +1,7 @@ -personal_ws-1.1 en 579 +personal_ws-1.1 en 588 ABCI ABI +ADR Agoric Agoric's Anca @@ -102,6 +103,7 @@ RegisterIBCAccountPacketData RegisterLightClient RootOfTrust RunTxPacketData +ScopedCapabilityKeeper SignatureAndData SourceIdentifier SpeckleOS @@ -140,6 +142,7 @@ addConnectionToClient applyPrefix atomicity auth +authenticateCapability authenticateTx authenticationKey authenticationPath @@ -170,6 +173,7 @@ chanOpenInit chanOpenTimeout chanOpenTry changelog +channelCapability channelCapabilityPath channelEnd channelEscrowAddresses @@ -184,6 +188,7 @@ checkMisbehaviourAndUpdateState checkSignature checkValidityAndUpdateState checkVersion +claimCapability cleanupPacket clientConnectionsKey clientConnectionsPath @@ -292,6 +297,7 @@ frozenKey fungibility generateAccount generateAddress +getCapability getChannelsUsingConnections getCommitmentPrefix getCompatibleVersions @@ -376,6 +382,7 @@ namespaces namespacing newAddress newCallbacks +newCapability newCapabilityKey newCapabilityPath newPublicKey @@ -421,6 +428,7 @@ permissionless pickVersion plaintext png +portCapability portId portIdentifier portKey @@ -475,6 +483,7 @@ refundTokens relayer relayerModule relayers +releaseCapability releasePort remoteEnd removeChannelFromConnection diff --git a/spec.pdf b/spec.pdf index 9cb0c55b0..fcc2619fb 100644 Binary files a/spec.pdf and b/spec.pdf differ diff --git a/spec/ics-004-channel-and-packet-semantics/README.md b/spec/ics-004-channel-and-packet-semantics/README.md index 84c78013d..091703c6c 100644 --- a/spec/ics-004-channel-and-packet-semantics/README.md +++ b/spec/ics-004-channel-and-packet-semantics/README.md @@ -30,7 +30,7 @@ In order to provide the desired ordering, exactly-once delivery, and module perm `Connection` is as defined in [ICS 3](../ics-003-connection-semantics). -`Port` and `authenticate` are as defined in [ICS 5](../ics-005-port-allocation). +`Port` and `authenticateCapability` are as defined in [ICS 5](../ics-005-port-allocation). `hash` is a generic collision-resistant hash function, the specifics of which must be agreed on by the modules utilising the channel. `hash` can be defined differently by different chains. @@ -280,15 +280,14 @@ function chanOpenInit( // optimistic channel handshakes are allowed abortTransactionUnless(connection !== null) - abortTransactionUnless(authenticate(privateStore.get(portPath(portIdentifier)))) + abortTransactionUnless(authenticateCapability(portPath(portIdentifier), portCapability)) channel = ChannelEnd{INIT, order, counterpartyPortIdentifier, counterpartyChannelIdentifier, connectionHops, version} provableStore.set(channelPath(portIdentifier, channelIdentifier), channel) - key = generate() - provableStore.set(channelCapabilityPath(portIdentifier, channelIdentifier), key) + channelCapability = newCapability(channelCapabilityPath(portIdentifier, channelIdentifier)) provableStore.set(nextSequenceSendPath(portIdentifier, channelIdentifier), 1) provableStore.set(nextSequenceRecvPath(portIdentifier, channelIdentifier), 1) - return key + return channelCapability } ``` @@ -318,7 +317,7 @@ function chanOpenTry( previous.connectionHops === connectionHops && previous.version === version) ) - abortTransactionUnless(authenticate(privateStore.get(portPath(portIdentifier)))) + abortTransactionUnless(authenticateCapability(portPath(portIdentifier), portCapability)) connection = provableStore.get(connectionPath(connectionHops[0])) abortTransactionUnless(connection !== null) abortTransactionUnless(connection.state === OPEN) @@ -334,11 +333,10 @@ function chanOpenTry( channel = ChannelEnd{TRYOPEN, order, counterpartyPortIdentifier, counterpartyChannelIdentifier, connectionHops, version} provableStore.set(channelPath(portIdentifier, channelIdentifier), channel) - key = generate() - provableStore.set(channelCapabilityPath(portIdentifier, channelIdentifier), key) + channelCapability = newCapability(channelCapabilityPath(portIdentifier, channelIdentifier)) provableStore.set(nextSequenceSendPath(portIdentifier, channelIdentifier), 1) provableStore.set(nextSequenceRecvPath(portIdentifier, channelIdentifier), 1) - return key + return channelCapability } ``` @@ -354,7 +352,7 @@ function chanOpenAck( proofHeight: uint64) { channel = provableStore.get(channelPath(portIdentifier, channelIdentifier)) abortTransactionUnless(channel.state === INIT || channel.state === TRYOPEN) - abortTransactionUnless(authenticate(privateStore.get(channelCapabilityPath(portIdentifier, channelIdentifier)))) + abortTransactionUnless(authenticateCapability(channelCapabilityPath(portIdentifier, channelIdentifier), capability)) connection = provableStore.get(connectionPath(channel.connectionHops[0])) abortTransactionUnless(connection !== null) abortTransactionUnless(connection.state === OPEN) @@ -385,7 +383,7 @@ function chanOpenConfirm( channel = provableStore.get(channelPath(portIdentifier, channelIdentifier)) abortTransactionUnless(channel !== null) abortTransactionUnless(channel.state === TRYOPEN) - abortTransactionUnless(authenticate(privateStore.get(channelCapabilityPath(portIdentifier, channelIdentifier)))) + abortTransactionUnless(authenticateCapability(channelCapabilityPath(portIdentifier, channelIdentifier), capability)) connection = provableStore.get(connectionPath(channel.connectionHops[0])) abortTransactionUnless(connection !== null) abortTransactionUnless(connection.state === OPEN) @@ -415,7 +413,7 @@ Any in-flight packets can be timed-out as soon as a channel is closed. function chanCloseInit( portIdentifier: Identifier, channelIdentifier: Identifier) { - abortTransactionUnless(authenticate(privateStore.get(channelCapabilityPath(portIdentifier, channelIdentifier)))) + abortTransactionUnless(authenticateCapability(channelCapabilityPath(portIdentifier, channelIdentifier), capability)) channel = provableStore.get(channelPath(portIdentifier, channelIdentifier)) abortTransactionUnless(channel !== null) abortTransactionUnless(channel.state !== CLOSED) @@ -440,7 +438,7 @@ function chanCloseConfirm( channelIdentifier: Identifier, proofInit: CommitmentProof, proofHeight: uint64) { - abortTransactionUnless(authenticate(privateStore.get(channelCapabilityPath(portIdentifier, channelIdentifier)))) + abortTransactionUnless(authenticateCapability(channelCapabilityPath(portIdentifier, channelIdentifier), capability)) channel = provableStore.get(channelPath(portIdentifier, channelIdentifier)) abortTransactionUnless(channel !== null) abortTransactionUnless(channel.state !== CLOSED) @@ -514,7 +512,7 @@ function sendPacket(packet: Packet) { // optimistic sends are permitted once the handshake has started abortTransactionUnless(channel !== null) abortTransactionUnless(channel.state !== CLOSED) - abortTransactionUnless(authenticate(privateStore.get(channelCapabilityPath(packet.sourcePort, packet.sourceChannel)))) + abortTransactionUnless(authenticateCapability(channelCapabilityPath(packet.sourcePort, packet.sourceChannel), capability)) abortTransactionUnless(packet.destPort === channel.counterpartyPortIdentifier) abortTransactionUnless(packet.destChannel === channel.counterpartyChannelIdentifier) connection = provableStore.get(connectionPath(channel.connectionHops[0])) @@ -567,7 +565,7 @@ function recvPacket( channel = provableStore.get(channelPath(packet.destPort, packet.destChannel)) abortTransactionUnless(channel !== null) abortTransactionUnless(channel.state === OPEN) - abortTransactionUnless(authenticate(privateStore.get(channelCapabilityPath(packet.destPort, packet.destChannel)))) + abortTransactionUnless(authenticateCapability(channelCapabilityPath(packet.destPort, packet.destChannel), capability)) abortTransactionUnless(packet.sourcePort === channel.counterpartyPortIdentifier) abortTransactionUnless(packet.sourceChannel === channel.counterpartyChannelIdentifier) @@ -630,7 +628,7 @@ function acknowledgePacket( channel = provableStore.get(channelPath(packet.sourcePort, packet.sourceChannel)) abortTransactionUnless(channel !== null) abortTransactionUnless(channel.state === OPEN) - abortTransactionUnless(authenticate(privateStore.get(channelCapabilityPath(packet.sourcePort, packet.sourceChannel)))) + abortTransactionUnless(authenticateCapability(channelCapabilityPath(packet.sourcePort, packet.sourceChannel), capability)) abortTransactionUnless(packet.destChannel === channel.counterpartyChannelIdentifier) connection = provableStore.get(connectionPath(channel.connectionHops[0])) @@ -693,7 +691,7 @@ function timeoutPacket( abortTransactionUnless(channel !== null) abortTransactionUnless(channel.state === OPEN) - abortTransactionUnless(authenticate(privateStore.get(channelCapabilityPath(packet.sourcePort, packet.sourceChannel)))) + abortTransactionUnless(authenticateCapability(channelCapabilityPath(packet.sourcePort, packet.sourceChannel), capability)) abortTransactionUnless(packet.destChannel === channel.counterpartyChannelIdentifier) connection = provableStore.get(connectionPath(channel.connectionHops[0])) @@ -763,7 +761,7 @@ function timeoutOnClose( channel = provableStore.get(channelPath(packet.sourcePort, packet.sourceChannel)) // note: the channel may have been closed - abortTransactionUnless(authenticate(privateStore.get(channelCapabilityPath(packet.sourcePort, packet.sourceChannel)))) + abortTransactionUnless(authenticateCapability(channelCapabilityPath(packet.sourcePort, packet.sourceChannel), capability)) abortTransactionUnless(packet.destChannel === channel.counterpartyChannelIdentifier) connection = provableStore.get(connectionPath(channel.connectionHops[0])) @@ -832,7 +830,7 @@ function cleanupPacket( channel = provableStore.get(channelPath(packet.sourcePort, packet.sourceChannel)) abortTransactionUnless(channel !== null) abortTransactionUnless(channel.state === OPEN) - abortTransactionUnless(authenticate(privateStore.get(channelCapabilityPath(packet.sourcePort, packet.sourceChannel)))) + abortTransactionUnless(authenticateCapability(channelCapabilityPath(packet.sourcePort, packet.sourceChannel), capability)) abortTransactionUnless(packet.destChannel === channel.counterpartyChannelIdentifier) connection = provableStore.get(connectionPath(channel.connectionHops[0])) diff --git a/spec/ics-005-port-allocation/README.md b/spec/ics-005-port-allocation/README.md index e3a1b509e..d06554576 100644 --- a/spec/ics-005-port-allocation/README.md +++ b/spec/ics-005-port-allocation/README.md @@ -75,9 +75,43 @@ and object references as used in Agoric's Javascript runtime ([reference](https: type CapabilityKey object ``` +`newCapability` must take a name and generate a unique capability key, such that the name is locally mapped to the capability key and can be used with `getCapability` later. + +```typescript +function newCapability(name: string): CapabilityKey { + // provided by host state machine, e.g. ADR 3 / ScopedCapabilityKeeper in Cosmos SDK +} +``` + +`authenticateCapability` must take a name & a capability and check whether the name is locally mapped to the provided capability. The name can be untrusted user input. + +```typescript +function authenticateCapability(name: string, capability: CapabilityKey): bool { + // provided by host state machine, e.g. ADR 3 / ScopedCapabilityKeeper in Cosmos SDK +} +``` + +`claimCapability` must take a name & a capability (provided by another module) and locally map the name to the capability, "claiming" it for future usage. + ```typescript -function newCapabilityPath(): CapabilityKey { - // provided by host state machine, e.g. pointer address in Cosmos SDK +function claimCapability(name: string, capability: CapabilityKey) { + // provided by host state machine, e.g. ADR 3 / ScopedCapabilityKeeper in Cosmos SDK +} +``` + +`getCapability` must allow a module to lookup a capability which it has previously created or claimed by name. + +```typescript +function getCapability(name: string): CapabilityKey { + // provided by host state machine, e.g. ADR 3 / ScopedCapabilityKeeper in Cosmos SDK +} +``` + +`releaseCapability` must allow a module to release a capability which it owns. + +```typescript +function releaseCapability(capability: CapabilityKey) { + // provided by host state machine, e.g. ADR 3 / ScopedCapabilityKeeper in Cosmos SDK } ``` @@ -95,33 +129,36 @@ function callingModuleIdentifier(): SourceIdentifier { } ``` -`generate` and `authenticate` functions are then defined as follows. - -In the former case, `generate` returns a new object-capability key, which must be returned by the outer-layer function, and `authenticate` requires that the outer-layer function take an extra argument `capability`, which is an object-capability key with uniqueness enforced by the host state machine. Outer-layer functions are any functions exposed by the IBC handler ([ICS 25](../ics-025-handler-interface)) or routing module ([ICS 26](../ics-026-routing-module)) to modules. +`newCapability`, `authenticateCapability`, `claimCapability`, `getCapability`, and `releaseCapability` are then implemented as follows: ``` -function generate(): CapabilityKey { - return newCapabilityPath() +function newCapability(name: string): CapabilityKey { + return callingModuleIdentifier() } ``` ``` -function authenticate(key: CapabilityKey): boolean { - return capability === key +function authenticateCapability(name: string, capability: CapabilityKey) { + return callingModuleIdentifier() === name } ``` -In the latter case, `generate` returns the calling module's identifier and `authenticate` merely checks it. +``` +function claimCapability(name: string, capability: CapabilityKey) { + // no-op +} +``` -```typescript -function generate(): SourceIdentifier { - return callingModuleIdentifier() +``` +function getCapability(name: string): CapabilityKey { + // not actually used + return nil } ``` -```typescript -function authenticate(id: SourceIdentifier): boolean { - return callingModuleIdentifier() === id +``` +function releaseCapability(capability: CapabilityKey) { + // no-op } ``` @@ -135,7 +172,6 @@ function portPath(id: Identifier): Path { } ``` - ### Sub-protocols #### Identifier validation @@ -157,28 +193,17 @@ The IBC handler MUST implement `bindPort`. `bindPort` binds to an unallocated po If the host state machine does not implement a special module manager to control port allocation, `bindPort` SHOULD be available to all modules. If it does, `bindPort` SHOULD only be callable by the module manager. ```typescript -function bindPort(id: Identifier) { +function bindPort(id: Identifier): CapabilityKey { abortTransactionUnless(validatePortIdentifier(id)) - abortTransactionUnless(privateStore.get(portPath(id)) === null) - key = generate() - privateStore.set(portPath(id), key) - return key + abortTransactionUnless(getCapability(portPath(id)) === null) + capability = newCapability(portPath(id)) + return capability } ``` #### Transferring ownership of a port -If the host state machine supports object-capabilities, no additional protocol is necessary, since the port reference is a bearer capability. If it does not, the IBC handler MAY implement the following `transferPort` function. - -`transferPort` SHOULD be available to all modules. - -```typescript -function transferPort(id: Identifier) { - abortTransactionUnless(authenticate(privateStore.get(portPath(id)))) - key = generate() - privateStore.set(portPath(id), key) -} -``` +If the host state machine supports object-capabilities, no additional protocol is necessary, since the port reference is a bearer capability. #### Releasing a port @@ -189,9 +214,9 @@ The IBC handler MUST implement the `releasePort` function, which allows a module > Warning: releasing a port will allow other modules to bind to that port and possibly intercept incoming channel opening handshakes. Modules should release ports only when doing so is safe. ```typescript -function releasePort(id: Identifier) { - abortTransactionUnless(authenticate(privateStore.get(portPath(id)))) - privateStore.delete(portPath(id)) +function releasePort(capability: CapabilityKey) { + abortTransactionUnless(authenticateCapability(portPath(id), capability)) + releaseCapability(capability) } ``` diff --git a/spec/ics-020-fungible-token-transfer/README.md b/spec/ics-020-fungible-token-transfer/README.md index b0b5537bd..b1575b3e5 100644 --- a/spec/ics-020-fungible-token-transfer/README.md +++ b/spec/ics-020-fungible-token-transfer/README.md @@ -72,7 +72,7 @@ The `setup` function must be called exactly once when the module is created (per ```typescript function setup() { - routingModule.bindPort("bank", ModuleCallbacks{ + capability = routingModule.bindPort("bank", ModuleCallbacks{ onChanOpenInit, onChanOpenTry, onChanOpenAck, @@ -84,6 +84,7 @@ function setup() { onAcknowledgePacket, onTimeoutPacketClose }) + claimCapability("port", capability) } ``` @@ -221,7 +222,7 @@ function createOutgoingPacket( bank.BurnCoins(sender, denomination, amount) } FungibleTokenPacketData data = FungibleTokenPacketData{denomination, amount, sender, receiver} - handler.sendPacket(Packet{destPort, destChannel, sourcePort, sourceChannel, data}) + handler.sendPacket(Packet{destPort, destChannel, sourcePort, sourceChannel, data}, getCapability("port")) } ``` diff --git a/spec/ics-026-routing-module/README.md b/spec/ics-026-routing-module/README.md index 4b8dc5818..6c83b1ff0 100644 --- a/spec/ics-026-routing-module/README.md +++ b/spec/ics-026-routing-module/README.md @@ -26,7 +26,7 @@ logic to determine when modules are allowed to bind to ports and what those port All functions provided by the IBC handler interface are defined as in [ICS 25](../ics-025-handler-interface). -The functions `generate` & `authenticate` are defined as in [ICS 5](../ics-005-port-allocation). +The functions `newCapability` & `authenticateCapability` are defined as in [ICS 5](../ics-005-port-allocation). ### Desired Properties @@ -159,12 +159,12 @@ The function `bindPort` can be called by a module in order to bind to a port, th ```typescript function bindPort( id: Identifier, - callbacks: Callbacks) { + callbacks: Callbacks): CapabilityKey { abortTransactionUnless(privateStore.get(callbackPath(id)) === null) - handler.bindPort(id) - capability = generate() - privateStore.set(authenticationPath(id), capability) privateStore.set(callbackPath(id), callbacks) + capability = handler.bindPort(id) + claimCapability(authenticationPath(id), capability) + return capability } ``` @@ -173,8 +173,9 @@ The function `updatePort` can be called by a module in order to alter the callba ```typescript function updatePort( id: Identifier, + capability: CapabilityKey, newCallbacks: Callbacks) { - abortTransactionUnless(authenticate(privateStore.get(authenticationPath(id)))) + abortTransactionUnless(authenticateCapability(authenticationPath(id), capability)) privateStore.set(callbackPath(id), newCallbacks) } ``` @@ -184,8 +185,10 @@ The function `releasePort` can be called by a module in order to release a port > Warning: releasing a port will allow other modules to bind to that port and possibly intercept incoming channel opening handshakes. Modules should release ports only when doing so is safe. ```typescript -function releasePort(id: Identifier) { - abortTransactionUnless(authenticate(privateStore.get(authenticationPath(id)))) +function releasePort( + id: Identifier, + capability: CapabilityKey) { + abortTransactionUnless(authenticateCapability(authenticationPath(id), capability)) handler.releasePort(id) privateStore.delete(callbackPath(id)) privateStore.delete(authenticationPath(id))