KeyAssignment is the name of the feature that allows validator operators to use different consensus keys for each consumer chain validator node that they operate.
Validators can improve their security by using different consensus keys for each chain. That way, different teams in an organization can operate a subset (can be size 1) of the total number of consumer chains associated to a provider chain. If one key leaks the other keys will not be at risk. It is possible to change the keys at any time by submitting a transaction.
The KeyAssignment feature is available via a provider chain API (transactions and queries). The provider chain validator operator submits an assignment transaction to the provider chain with a consumer chain ID and desired consensus key as parameters. The over-IBC protocol used by Interchain Security takes care of forwarding the assignment to the specified consumer chain. When the consumer chain receives the key, it will immediately start using it with Tendermint.
It is possible to start validating a consumer chain with the same key as used for the provider. This is the default behavior. It is also possible to specify another key to use when joining the validator set. Moreover it is possible to change the used key at any time, any multiple times, with some minor restrictions.
TXs
// Assign a new public consensus key to be used by a validator
// on the provider when it signs transactions on the consumer chain.
// The TX must be signed by the private key associated to the provider
// validator address.
//
// The assignment can fail if the consumer consensus key is already
// in use for the chain, currently, or in the recent past.
AssignConsensusPublicKeyToConsumerChain(
ChainId string, // consumer chain
ProviderValidatorAddress string, // must sign TX
ConsumerConsensusPubKey *types.Any
)
Queries
// Returns the last consumer key associated to the provider key and
// the consumer chain by a call to AssignConsensusPublicKeyToConsumerChain.
QueryConsumerChainValidatorKeyAssignment (
ChainId string, // consumer chain
ProviderValidatorAddress string, // validator address for the provider chain
)
The external API is specified in api.tla. An 'internal' API is also specified. The external API supports the TXs and Queries listed above. The internal API documents the API that the implementation of KeyAssignment exposes for integration in the implementation of the wider system.
Once a validator is created on the provider chain it is possible for the validator to assign a consensus key for a particular consumer chain by submitting a transaction to the provider chain. The assigned key is stored, and all future validator set updates which are sent to the consumer chain are mapped through the assigned keys.
Once a consumer addition proposal is submitted it is possible for validators to assign keys for the chain in question. When a proposal voting period expires and the proposal doesn't pass, the assigned keys are deleted. When the proposal does pass, the channel initialisation sub protocol creates a ibc client for the consumer chain on the provider and produces a genesis state for the consumer chain. The initial validator set in the consumer genesis state is computed according to the assigned keys, much like in the 'Validator set update' sub protocol. A hash of the this set is included.
When validator set updates are emitted by the staking module and sent by the VSU sub protocol, the updates are mapped through key assignments. Each provider consensus key referenced in an update is mapped to its assigned key. If no key is assigned, the default behavior is that a provider key is mapped to itself. Thus the VSU packets contain validator updates which, on delivery to the consumer, can immediately be passed to Tendermint (after aggregation). The update mapping algorithm takes care to produce a valid set of updates even when the assigned key is changed and the validator joins/leaves the active set.
When a VSUMaturity packet is delivered to the provider chain the KeyAssignment internal data structure is pruned by passing the matured VSCID as argument. The pruning step removes any keys which have not been used for consensus by the consumer chain since the consumer received the VSU with vscid VSCID.
When a double signing or downtime slash action occurs on the consumer chain the slash packet is sent as normal to the provider chain. On delivery to the provider chain, the consensus address referenced in the slash packet is mapped back to the address of the validator to be slashed on the provider. This is possible even in the presence of changes to the assigned key of a validator, as previously used keys are stored for some (sufficient) time.
Assumption:
A correct consumer chain will not send a double sign slash request for a consensus address A
if the last time that A
was present in a positive power update to Tendermint was at height h0
, and A
was also present in a zero power update to Tendermint at height h1
(h0 < h1
) associated to a vscid v
and a VSUMaturity packet for v
was sent by the consumer chain.
In other words: correct consumer chains shall not send double sign slashes for validators which they have already unbonded. This assumption is supported by setting appropriate parameters in the evidence module (see relevant code).
There is no interaction with the rewards distribution sub protocol.
When a consumer chain is removed on the provider chain all the associated key assignment data is deleted. When a validator is destroyed (not merely unbonded) on the provider, its associated key assignment data is deleted.
There are some integration points with the system
KeyAssignment.ComputeUpdates(..)
in providerEndBlocker
, duringsendValidatorUpdates
The validator updates from the staking module on the provider are transformed by mapping the validator consensus key to their assigned consumer consensus key. If no assignment has been explicitly made, then the provider consensus key is used.KeyAssignment.PruneUnusedKeys(..)
in providerOnRecvVSCMaturedPacket
On receiving a VSCID for a consumer in a maturity packet, it is certain that any consensus keys which were sent to the consumer at a vscid <= VSCID with a zero power update, and have not since been sent to the consumer with a positive power update, can be pruned, as these keys are no longer valid for slashing.KeyAssignment.GetProviderPubKeyFromConsumerConsAddress(..)
in providerHandleSlashPacket
Slash packets delivered to the provider contain the consensus address for the validator to be slashed, as it is known to Tendermint on the consumer chain. The consensus address is used as a key in a lookup table to retrieve the provider validator consensus address. This can be then be used to slash the validator accordingly.KeyAssignment.DeleteProviderKey(..)
in provider staking hookAfterValidatorRemoved
When a validator is entirely removed on the provider chain, the associated index data is deleted from the state.SetProviderPubKeyToConsumerPubKey(..)
in providerCreateConsumerClient
duringMakeConsumerGenesis
When a new consumer is added, the provider exports a genesis state for that consumer. At this time, the initial validator set must be computed. It is mapped through the assigned consensus keys, exactly as happens in the providerEndBlocker
(see bullet 1).DeleteKeyAssignment(chainID)
in providerStopConsumerChain
When a consumer chain is stopped, all of the associated state is deleted.- The internal state is serialized during provider
ExportGenesis
Self explanatory. - The internal state is loaded during provider
InitGenesis
Self explanatory. AssignConsensusPublicKeyToConsumerChain(..)
TX handler in providermsg_server
A tx is exposed on the provider, which takes a valid consumer chainID, and a Tendermint public key. A validator on the provider must sign the tx. On receipt, the specified public key will be used to send updates for that validator to the specified consumer chain.QueryConsumerChainValidatorKeyAssignment(..)
query handler in providergrpc_query
Query the currently assigned Tendermint public key for a given (validator, consumer chain) pair.
KeyAssignment has some properties relevant to the external user:
- Validator Set Replication
When the Interchain Security property Validator Set Replication holds for an implementation without KeyAssignment, then the property holds when KeyAssignment is used. - Slashable Consumer Misbehavior
When the Interchain Security property Slashable Consumer Misbehavior holds for an implementation without KeyAssignment, then the property holds when KeyAssignment is used.
All Interchain Security properties still hold when KeyAssignment is used, the above are just the most relevant.
Additionally
- Timeliness
When aAssignConsensusPublicKeyToConsumerChain
operation succeeds for a given(chainID, ProviderValidatorAddress, ConsumerConsensusPubKey)
tuple at block heighthp0
, and is not followed by a subsequent call for a tuple starting(chainID, ProviderValidatorAddress,)
before or during a heighthp1
(hp0 <= hp1
), and athp1
a validator set update packet is committed at the provider chain, then at the next earliest heighthc2
on the consumer chain that the packet committed athp1
is received, the keyConsumerConsensusPubKey
is passed as consensus key to Tendermint. Thus Tendermint will expect a signature fromConsumerConsensusPubKey
from heighthc2 + 1
.
The internal properties section in api.tla specifies abstract but precise properties. In particular, at a high level:
- The consumer validator set is always defined as per the validator set replication property.
- It is always possible to lookup the provider consensus address, for a given consumer consensus public key, when the consumer has been sent that public key and that key is still liable for double signing or downtime slashing.
- The storage requirements are reasonable.
Please see api.tla and key_assignment_test.go::externalInvariants and key_assignment.go::internalInvariants for precise formulations.
This document (at v4) describes a minimally complex and minimally invasive minimal-viable-product. Some extensions can be made to the protocol and implementation in future to address feature / UX requirements.
The most obvious extension would be to provider a way for the consumer chain to locally identify consensus addresses that it receives in VSU packets to validators on the provider chain. This might be desirable for UX reasons.
It is possible for validators to assign keys for chainIDs that are not yet consumer chains or active proposals. This is to give validators enough time to assign a key before the consumer genesis state is produced. In future, assignment may be restricted to chainIDs that have an associated active proposal, or for which the proposal has passed and is pending processing. It will always be possible to assign keys to active chains.