Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial PSS ADR and notes #1518

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
82 changes: 82 additions & 0 deletions docs/docs/adrs/adr-014-partial-set-security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
---
sidebar_position: 2
title: ADR Template
---
# ADR XX: Partial Set Security

## Changelog

## Status

> A decision may be "proposed" if it hasn't been agreed upon yet, or "accepted" once it is agreed upon. If a later ADR changes or reverses a decision, it may be marked as "deprecated" or "superseded" with a reference to its replacement.

[Deprecated|Proposed|Accepted]

## Context

> This section contains all the context one needs to understand the current state, and why there is a problem. It should be as succinct as possible and introduce the high level idea behind the solution.

## Decision

### Partial Set Logic

This logic keeps a record of which validators are opted in to which consumer chains, and modifies valset updates accordingly.

The simplest part of this is to filter out the validators that are not opted into a given chain. But we also need to handle validators opting out of a consumer chain. In this case, it is not sufficient to simply filter them out of validator updates. A synthetic update must also be added to the valset update after they opt out, setting their power to 0 to remove them. Without this, they would continue at the power they had before opting out.

The partial set logic also needs to supply information on whether a validator is running on a given consumer chain, or was running on it during the unbonding period. This is used to determine whether a validator is liable for slashing on a given chain.

This is very similar to the key assignment logic, so we can copy concepts from it.

### Top N

The top-n percentage is a parameter that consumer chains can set that obligates the top "n" percent of the validator set to run their chain. There is some logic in the current consumer chain soft opt out code that could be useful to calculate which validators are in this top n.

This logic must be run periodically (probably every block), and figures out the set of validators which must be automatically opted in to each consumer chain depending on that consumer chain's top-n percentage. After this, the partial set state is updated accordingly.

### Downtime

Downtime needs to be handled differently depending on whether a validator is in the top-n of a consumer chain or not. When receiving a downtime packet, if the validator is in the top-n of the chain, it can be handled exactly as it is in RS, by jailing that validator.

If a validator is not in the top-n, they should simply be opted out of that consumer chain so that they are no longer in the validator set.

If the validator is not opted in to the consumer chain at all, obviously nothing should happen.

### Consumer addition

For consumer chains with no top-n, we will need to call the consumer chain creation logic from a message handler instead of a proposal handler. Further study is needed on the genesis and IBC client setup of such a chain. Doesn't seem like it is possible to start a chain with an empty validator set. Maybe the chain's creator can name an initial set. This doesn't obligate those validators to run the chain, but if they do, at least it can start. Projects would have to make out-of-band arrangements with their initial set.

We will also want to keep the proposal logic for consumer chains with a top-n, since this must be governance gated.

There will be some tricky edge cases around preventing squatting and griefing chain IDs.

- Governance gated top-n consumer chains should be able to overwrite the chain ID of a permissionless consumer chain.
- We may want to think about how to clean up consumer chains, in case someone is registering hundreds of chain IDs without running any real chains.

#### Alternative governance based consumer addition

Another design would be to require all consumer chains to make a governance proposal. This proposal would need to pass for the chain to launch, but it would obligate all validators to run the chain like in Replicated Security (unless top n was being used etc). Instead, all validators who voted "YES" would be opted in, and validators who voted "ABSTAIN" would not be opted in.

This would mean that the barrier to joining as a consumer chain would just be to pass quorum.

This may also simplify implementation. We don't have to worry about chain ID squatting any more, and we can probably reuse more of the governance related logic.

### Rewards

When rewards are received, they need to be allocated to the opted-in set of the consumer chain, instead of the whole Hub. Luckily this is easy with SDK 47.

## Consequences

> This section describes the consequences, after applying the decision. All consequences should be summarized here, not just the "positive" ones.

### Positive

### Negative

### Neutral

## References

> Are there any relevant PR comments, issues that led up to this, or articles referenced for why we made the given design choice? If so link them here!

- [references]
2 changes: 2 additions & 0 deletions x/ccv/consumer/keeper/soft_opt_out.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"github.com/cosmos/interchain-security/v3/x/ccv/consumer/types"
)

// PSS-NOTES: we can use similar logic to compute top-n

// SetSmallestNonOptOutPower sets the smallest validator power that cannot soft opt out.
func (k Keeper) SetSmallestNonOptOutPower(ctx sdk.Context, power uint64) {
store := ctx.KVStore(k.storeKey)
Expand Down
4 changes: 4 additions & 0 deletions x/ccv/provider/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,10 @@ func (k Keeper) DeleteChannelToChain(ctx sdk.Context, channelID string) {
// is stored under keys with the following format:
// ChannelToChainBytePrefix | channelID
// Thus, the returned array is in ascending order of channelIDs.

// PSS-NOTES: IIRC this is the main record storing consumer chains. PSS needs some extra metadata like top-n, etc.
// We may want to look at adding it here or in a separate record.

func (k Keeper) GetAllChannelToChains(ctx sdk.Context) (channels []types.ChannelToChain) {
store := ctx.KVStore(k.storeKey)
iterator := sdk.KVStorePrefixIterator(store, []byte{types.ChannelToChainBytePrefix})
Expand Down
5 changes: 5 additions & 0 deletions x/ccv/provider/keeper/key_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import (
ccvtypes "github.com/cosmos/interchain-security/v3/x/ccv/types"
)

// PSS-NOTES: The opt-in mechanism of PSS will need to look a lot like the key assignment logic. Probably simpler though.
// A major commonality with key assignment is that "synthetic" validator updates need to be generated when a val opts in or out.
// We will also need to figure out how top-n works. It will be kind of an automatic opting in or out.
// TODO: Come up with a concrete algorithm.

// GetValidatorConsumerPubKey returns a validator's public key assigned for a consumer chain
func (k Keeper) GetValidatorConsumerPubKey(
ctx sdk.Context,
Expand Down
7 changes: 7 additions & 0 deletions x/ccv/provider/keeper/proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import (
// Note: This method implements SpawnConsumerChainProposalHandler in spec.
// See: https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#ccv-pcf-hcaprop1
// Spec tag: [CCV-PCF-HCAPROP.1]
// PSS-NOTES: If using "governance piggybacking" method, we will need to call gov.GetVotes here to get votes which will
// tell us which vals have voted YES/NO/ABSTAIN to find out who to opt in.
func (k Keeper) HandleConsumerAdditionProposal(ctx sdk.Context, p *types.ConsumerAdditionProposal) error {
// verify the consumer addition proposal execution
// in cached context and discard the cached writes
Expand All @@ -52,6 +54,9 @@ func (k Keeper) HandleConsumerAdditionProposal(ctx sdk.Context, p *types.Consume
//
// See: https://github.com/cosmos/ibc/blob/main/spec/app/ics-028-cross-chain-validation/methods.md#ccv-pcf-crclient1
// Spec tag: [CCV-PCF-CRCLIENT.1]
// PSS-NOTES: This will probably need to be called by a transaction handler instead of a gov proposal handler
// Need logic to overwrite permissionless consumer chains with permissioned top-n consumer chains
// Unless we use the "governance piggybacking" method described in the spec
func (k Keeper) CreateConsumerClient(ctx sdk.Context, prop *types.ConsumerAdditionProposal) error {
chainID := prop.ChainId
// check that a client for this chain does not exist
Expand Down Expand Up @@ -618,3 +623,5 @@ func (k Keeper) HandleConsumerRewardDenomProposal(ctx sdk.Context, p *types.Chan
}
return nil
}

// PSS-NOTES: Need to bring in the fraud vote proposal, a lot like the former equivocation proposals
4 changes: 4 additions & 0 deletions x/ccv/provider/keeper/relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ func (k Keeper) EndBlockCIS(ctx sdk.Context) {

// OnRecvSlashPacket delivers a received slash packet, validates it and
// then queues the slash packet as pending if valid.

// PSS-NOTES: We will need to modify the behavior of this function for PSS. If the validator is not in the top-n,
// we will just opt it out of that consumer chain instead of jailing it. If it is in the top-n, we will jail it as normal.
// Should also probably do kind of a cleanup and renaming now that this function never does any slashing.
func (k Keeper) OnRecvSlashPacket(
ctx sdk.Context,
packet channeltypes.Packet,
Expand Down