diff --git a/AGENTS.md b/AGENTS.md index a01acae..ab74b52 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ VAAS (Validator-as-a-Service) is a simplified Interchain Security (ICS) implementation for Cosmos blockchains, derived from [interchain-security](https://github.com/cosmos/interchain-security). It lets a provider chain lease its proof-of-stake security to consumer chains via automatic validator synchronization. All active validators validate all consumers — no opt-in/opt-out. -Supports both IBC v1 (channel-based, ordered) and IBC v2 (client-based, out-of-order). IBC v2 uses application IDs `vaas/provider` and `vaas/consumer` instead of port IDs. +VAAS runs on **IBC v2 only** (client-based, out-of-order). There is no IBC v1 channel handshake; the VAAS modules register on `ibcRouterV2` under application IDs `vaasprovider` and `vaasconsumer` (see [x/vaas/types/keys.go](x/vaas/types/keys.go)). Consumer launch relies on a relayer (ts-relayer in localnet and e2e) creating IBC v2 clients on both sides; the provider then discovers its consumer client at the next epoch boundary. ## Build & Test Commands @@ -25,11 +25,11 @@ make proto-format # clang-format make proto-lint # buf lint # Localnet (3 terminals, or use localnet-start for all-in-one) -make localnet-start # provider + consumer + Hermes relayer +make localnet-start # provider + consumer + ts-relayer (Docker) make localnet-clean # stop all, clean data # E2E (Docker-based) -make docker-build-all # build chain + Hermes images +make docker-build-all # build chain image (ts-relayer image is pulled) make test-e2e # run e2e suite ``` @@ -45,7 +45,7 @@ The core protocol lives in `x/vaas/` with two symmetric modules: - **`x/vaas/consumer/`** — runs on consumer chains. Receives VSC packets, maintains cross-chain validator set, reports evidence back to provider. - **`x/vaas/types/`** — shared types, errors, constants, and `expected_keepers.go` (interfaces for external dependencies like staking/slashing). -Each module has: `keeper/` (business logic + state), `types/` (data structures + params), `client/cli/` (CLI commands), `module.go`, `ibc_module.go` (v1 callbacks), `ibc_module_v2.go` (v2 callbacks). +Each module has: `keeper/` (business logic + state), `types/` (data structures + params), `client/cli/` (CLI commands), `module.go`, and `ibc_module.go` (IBC v2 callbacks implementing `api.IBCModule` from `ibc-go/v10/modules/core/api`). Two helper modules replace standard Cosmos modules to prevent automatic validator set updates on consumer chains: @@ -62,10 +62,11 @@ Built with `make build-apps` into `build/`. ### Key Data Flow -1. Provider detects staking changes → queues VSC packets (every `blocks_per_epoch` blocks, default 600) -2. VSC packets sent via IBC to all registered consumers -3. Consumer receives packet → calls `ApplyCCValidatorChanges()` to update its validator set -4. Double-voting evidence flows consumer → provider, where per-consumer infraction parameters determine slash/jail +1. Once a consumer reaches `LAUNCHED`, a relayer creates an IBC v2 client on the provider pointing to the consumer (and the counterparty on the other side). The provider discovers this client at the next epoch boundary (`discoverActiveConsumerClient`), it never creates the client itself. +2. Provider computes validator set changes once per epoch (`blocks_per_epoch`, default 600) and queues a VSC packet per launched consumer. +3. Provider sends each queued VSC packet over the discovered IBC v2 client. Packets are out-of-order; the consumer deduplicates via `HighestValsetUpdateID`. +4. Consumer's `OnRecvPacket` calls `ApplyCCValidatorChanges()` and the new set is flushed to CometBFT on the next `EndBlock`. +5. Double-voting / light-client evidence flows consumer → provider via `MsgSubmitConsumerDoubleVoting` / `MsgSubmitConsumerMisbehaviour`; per-consumer infraction parameters determine slash/jail. ### Consumer Lifecycle @@ -85,7 +86,7 @@ Under `proto/vaas/`: `v1/` (shared wire types like `ValidatorSetChangePacketData - **Unit tests**: alongside source in `*_test.go`. Use `testutil/keeper/unit_test_helpers.go` for in-memory keeper setup with `MockedKeepers` (gomock). - **Mock generation**: `make mocks-gen` from `x/vaas/types/expected_keepers.go` → `testutil/keeper/mocks.go` -- **E2E tests**: `tests/e2e/` — Docker-based, spins up real chains with Hermes relayer. +- **E2E tests**: `tests/e2e/` — Docker-based, spins up real provider/consumer chains plus the `ghcr.io/allinbits/ibc-v2-ts-relayer` container; see [tests/e2e/e2e_tsrelayer_test.go](tests/e2e/e2e_tsrelayer_test.go). ## Lint / Import Ordering diff --git a/DESIGN_RATIONALE.md b/DESIGN_RATIONALE.md new file mode 100644 index 0000000..2baa41c --- /dev/null +++ b/DESIGN_RATIONALE.md @@ -0,0 +1,134 @@ +# VAAS Design Rationale + +> This document supersedes [`PLAN.old.md`](PLAN.old.md), the original rewrite +> plan drafted before the port from `interchain-security`. `PLAN.old.md` is +> kept for historical reference only; the present document is the +> authoritative statement of why VAAS is shaped the way it is. + +This is a forward-facing reference for contributors. For the per-feature +historical diff against the `interchain-security` codebase VAAS was ported +from, see [`REWRITE_SUMMARY.md`](REWRITE_SUMMARY.md); for protocol-level +mechanics, see [`docs/consumer-lifecycle.md`](docs/consumer-lifecycle.md) and +[`AGENTS.md`](AGENTS.md). + +VAAS is a **simplified** Interchain Security: a Cosmos provider chain lends +its full validator set to one or more consumer chains, automatically, with no +opt-in/out and no power shaping. The simplification is the product, not a +work-in-progress. + +--- + +## Guiding Principles + +1. **All validators validate everything.** There is no per-consumer + selection. The active provider set, capped at + `MaxProviderConsensusValidators`, is the consumer set. +2. **No cross-chain economics inside the protocol.** No reward distribution, + no per-consumer commission rates, no slash throttling, no slash meters. + Consumers stand up their own fee/reward models; the protocol carries only + validator-set state and security signals. +3. **IBC v2 only.** No channel handshake, no ordered channels, no port + reservations. VAAS modules register on `ibcRouterV2` under the application + IDs `vaasprovider` and `vaasconsumer`; consumer launch relies on a relayer + creating the IBC v2 clients and the provider discovering its consumer + client at the next epoch boundary. +4. **Forward-only consumer lifecycle.** `REGISTERED → INITIALIZED → LAUNCHED + → STOPPED → DELETED`, with a single rollback path (failed launch → back to + `REGISTERED`). Standalone-to-consumer changeover is not currently + supported; see [`docs/consumer-transition.md`](docs/consumer-transition.md) + for the future-work considerations. +5. **Provider authority for control-plane messages.** `OnSendPacket` requires + the keeper's authority as signer; consumers never send packets back + (`OnRecvPacket` on the provider is a failure path; `OnSendPacket` on the + consumer is rejected). Misbehaviour reports travel as ordinary provider + transactions, not IBC packets. + +--- + +## Why the Simplifications + +### Removed: Partial Set Security (PSS), Top N, Opt-in, Power Shaping + +The `interchain-security` codebase supports renting *subsets* of the +validator set per consumer with caps, allowlists, denylists, priority lists, +and inactive-validator participation (ADR-017). VAAS targets deployments +where the provider guarantees its entire active set to every consumer. +Removing PSS deletes a large surface area: per-validator opt-in state, +per-consumer power-shaping parameters, "has-to-validate" queries, and the +messages that maintain them (`MsgOptIn`, `MsgOptOut`, +`MsgSetConsumerCommissionRate`). + +The trade-off is rigidity: a consumer cannot pick a smaller validator set. +That is intentional. Smaller sets do not inherit the BFT assumption of the +full set, there is no assumption that can be made about smaller sets, no +security guarantee. The simplification also drastically reduces the +complexity of the system. + +### Removed: Slash Packet Throttling, Slash Meters, Slash Retry + +ICS throttles slash packets to bound the impact of a misbehaving or +adversarial consumer on the provider's validator set. VAAS removes the +throttle, the meter, and the retry queue. Consumer-initiated slashing for +downtime is *not currently performed by the provider*; equivocation evidence +(double-sign, light-client) is submitted as a provider transaction +(`MsgSubmitConsumerDoubleVoting`, `MsgSubmitConsumerMisbehaviour`) and +slashed using per-consumer infraction parameters. Downtime slashing via slash +packets is an open design question (see open issues / PRs). + +### Removed: Consumer Reward Distribution + +ICS pipes a fraction of consumer fees back to the provider as validator +rewards. VAAS leaves the consumer fee model entirely to the consumer chain. +This eliminates a category of cross-chain accounting and the related +provider-side state (reward denom registration, fee pool addressing, etc.). + +### Kept: Per-Consumer Infraction Parameters + +Each consumer carries its own `infraction_parameters` (double-sign and +downtime slash fractions, jail durations, tombstone flag). This is the one +place per-consumer customisation survives — the protocol cannot decide +slash severity centrally because consumers vary in security profile. + +### Kept: Key Assignment + +Validators may assign per-consumer consensus keys via +`MsgAssignConsumerKey`. Keys are ed25519 only and prune on unbonding, with +checks that prevent key reuse across consumers. + +--- + +## Active Work and Open Questions + +Live work is tracked in GitHub issues and pull requests, not in this file — +this list points to the rough areas, not the specific tickets. + +- **Genesis import/export**. Several entry points in the provider and + consumer modules still need correct round-trip support. The provider + `InitGenesis` path uses the chain-ID field as the consumer-id key, which + needs cleaning up. +- **Per-consumer infraction parameters at runtime**. The parameters are + stored per consumer but the equivocation handling path needs to consume + them consistently. +- **Provider-side downtime slashing.** Whether (and how) consumers should be + able to request downtime slashing on the provider is an open design + question; a draft PR proposes a slash-packet-based path. +- **Timeout policy.** A VSC packet timeout currently has heavy consequences + (consumer removal); whether this is the right default is open. +- **Standalone-to-consumer transition.** The `PreVAAS` / `PrevStandaloneChain` + collections and the `standaloneStakingKeeper` plumbing in the consumer + module are dead today but preserved deliberately as a reference for a + future transition implementation; see + [`docs/consumer-transition.md`](docs/consumer-transition.md). + +## Explicit Non-Goals + +The following are intentionally **not** part of VAAS and should not be added +back without a strong, documented reason: + +- Partial Set Security (Top N, opt-in/out, allow/deny lists) +- Per-consumer power shaping (caps, priority lists) +- Slash packet throttling / slash meters +- Cross-chain reward distribution +- Per-consumer commission rates +- IBC v1 channel routing for VAAS messages +- Inactive provider validators participating in consumer security (ADR-017) diff --git a/PLAN.md b/PLAN.old.md similarity index 95% rename from PLAN.md rename to PLAN.old.md index 67964dc..a8b06e8 100644 --- a/PLAN.md +++ b/PLAN.old.md @@ -1,5 +1,12 @@ # VAAS Rewrite Plan +> **Historical document.** This was the original plan drafted *before* the +> port of `interchain-security` into VAAS, when the new module was still +> being scoped. Paths, function names, and tooling assumptions reflect that +> moment and are no longer accurate. It is kept verbatim for archival +> reference. For the current authoritative statement of why VAAS is shaped +> the way it is, see [`DESIGN_RATIONALE.md`](DESIGN_RATIONALE.md). + Rewrite `x/vaas/provider` and `x/vaas/consumer` in a new `vaas` folder as a **new Go module** with package path `github.com/allinbits/vaas`. --- diff --git a/README.md b/README.md index 425c09d..f2a53d4 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,20 @@ VAAS allows Cosmos blockchains to lease their proof-of-stake security to consumer chains. All active validators on the provider chain automatically validate all consumer chains - there is no opt-in/opt-out mechanism. -## Features +## IBC v2 only -## IBC v2 support +VAAS uses IBC v2 exclusively — no channel handshake, no port reservations. +The provider and consumer modules register on `ibcRouterV2` under the +application IDs `vaasprovider` and `vaasconsumer`. After a consumer launches, +a relayer (the localnet and e2e suites use +[`ts-relayer`](https://github.com/allinbits/ibc-v2-ts-relayer)) creates an +IBC v2 client on each chain pointing at the counterparty and registers the +path. The provider then discovers its consumer client at the next epoch +boundary; all VSC packets flow over that client. Wiring VAAS into a host app +just means adding the v2 routes — see [`app/provider/app.go`](app/provider/app.go) +and [`app/consumer/app.go`](app/consumer/app.go) for reference. -The VAAS implementation supports IBC v2 only. -IBC v2 is easily wireable by adding the IBC router v2 in a ibc-go >= 10.x.y compatible chain. +## Features ### Kept from ICS @@ -37,7 +45,10 @@ IBC v2 is easily wireable by adding the IBC router v2 in a ibc-go >= 10.x.y comp | Slash Packet Throttling | Simplified slash handling | | Per-Consumer Commission Rates | Validators use same commission as provider | | IBC v1 Channel Support | IBC v2 only | -| Standalone-to-Consumer Changeover | Only new chains as consumers | +| Standalone-to-Consumer Changeover | Not currently supported (future work) | + +See [docs/consumer-transition.md](docs/consumer-transition.md) for the +consequences and requirements of a future standalone-to-consumer transition. ## Build & Test @@ -51,6 +62,15 @@ make docker-build-all make test-e2e ``` +## Documentation + +- [Localnet setup](app/README.md) — run a provider, a consumer, and `ts-relayer` locally +- [Consumer lifecycle](docs/consumer-lifecycle.md) — phases, on-chain effects, operator/relayer responsibilities +- [Contributor guide (AGENTS.md)](AGENTS.md) — architecture, build/test commands, code layout +- [Design rationale (DESIGN_RATIONALE.md)](DESIGN_RATIONALE.md) — why VAAS is shaped the way it is +- [Diff vs the ICS codebase VAAS ported from (REWRITE_SUMMARY.md)](REWRITE_SUMMARY.md) — what was removed/kept in the port +- [PLAN.old.md](PLAN.old.md) — the original pre-port rewrite plan, kept for archival reference + ## Learn More - [ICS Documentation](https://cosmos.github.io/interchain-security/) diff --git a/REWRITE_SUMMARY.md b/REWRITE_SUMMARY.md index b7f6e31..e8335ac 100644 --- a/REWRITE_SUMMARY.md +++ b/REWRITE_SUMMARY.md @@ -1,6 +1,18 @@ # VAAS Module Rewrite Summary -This document summarizes the rewrite of the CCV (Cross-Chain Validation) provider and consumer modules from `github.com/cosmos/interchain-security/v7` into a new `vaas` folder as a standalone Go module with package path `github.com/allinbits/vaas`. +This document records how VAAS was ported from +[`github.com/cosmos/interchain-security/v7`](https://github.com/cosmos/interchain-security) +into the standalone Go module `github.com/allinbits/vaas`. Its purpose is to +explain the **diff against the ICS codebase VAAS was ported from** — what was +removed, what was kept, and why — for readers familiar with the original +Interchain Security code. VAAS is now an independent project; there is no +intent or path to upstream changes back to `interchain-security`. + +> **NOTE:** this is a historical comparison. The function-level diffs in +> the "Code Changes Made" section below describe edits performed during the +> port; subsequent cleanup has removed some of the resulting no-op stubs +> entirely. For current behavior, consult the code, [AGENTS.md](AGENTS.md), +> and [`docs/consumer-lifecycle.md`](docs/consumer-lifecycle.md). ## Overview @@ -26,7 +38,7 @@ The rewrite simplifies the interchain security modules by removing several featu | **Slash Packet Sending** | All slash-related removed | | **Slash Packet Throttling/Retry** | All slash-related removed | | **Reward Distribution to Provider** | Consistent with provider removal | -| **Standalone-to-Consumer Changeover** | Only new chains as consumers | +| **Standalone-to-Consumer Changeover** | Not currently supported; the consumer-keeper wiring is preserved as a reference for future transition work (see [docs/consumer-transition.md](docs/consumer-transition.md)) | | **Outstanding Downtime Flag** | Part of slash removal | ## Features Kept @@ -129,50 +141,53 @@ The rewrite simplifies the interchain security modules by removing several featu ## File Structure -``` +```text vaas/ ├── go.mod # module github.com/allinbits/vaas ├── go.sum ├── REWRITE_SUMMARY.md # This document +├── app/ # provider + consumer Cosmos SDK apps and CLIs +├── docs/ # protocol documentation +├── proto/vaas/ # protobuf definitions +├── tests/e2e/ # Docker-based e2e suite (ts-relayer) ├── testutil/ -│ ├── crypto/ # Test crypto utilities -│ └── keeper/ # Test keeper utilities -└── x/ - └── ccv/ - ├── provider/ - │ ├── keeper/ # Provider keeper implementation - │ ├── types/ # Provider types (includes .pb.go files) - │ ├── client/cli/ # CLI commands - │ ├── module.go # Module definition - │ └── ibc_module.go # IBC callbacks - ├── consumer/ - │ ├── keeper/ # Consumer keeper implementation - │ ├── types/ # Consumer types (includes .pb.go files) - │ ├── client/cli/ # CLI commands - │ ├── module.go # Module definition - │ └── ibc_module.go # IBC callbacks - └── types/ # Shared types +│ ├── crypto/ # test crypto utilities +│ └── keeper/ # in-memory keeper + generated mocks +└── x/vaas/ + ├── provider/ + │ ├── keeper/ # provider keeper + │ ├── types/ # provider types (includes .pb.go files) + │ ├── client/cli/ # CLI commands + │ ├── module.go # module definition + │ └── ibc_module.go # IBC v2 callbacks (api.IBCModule) + ├── consumer/ + │ ├── keeper/ # consumer keeper + │ ├── types/ # consumer types (includes .pb.go files) + │ ├── client/cli/ # CLI commands + │ ├── module.go # module definition + │ └── ibc_module.go # IBC v2 callbacks (api.IBCModule) + ├── no_valupdates_staking/ # staking without valset exports (consumer) + ├── no_valupdates_genutil/ # genutil without gentx valset init (consumer) + └── types/ # shared types ``` -## Test Status +## Testing -### Tests Passing -- `x/vaas/consumer/keeper` - Genesis, keeper, params, validators tests -- `x/vaas/consumer/types` - Genesis, keys, params tests -- `x/vaas/provider/keeper` - Consumer equivocation, hooks, keeper, key assignment, msg server, params, permissionless tests -- `x/vaas/provider/types` - Genesis, keys, msg, params tests -- `x/vaas/types` - Shared params, utils, wire tests +Unit tests live alongside their source files (`*_test.go`) and rely on the +in-memory keeper helpers in `testutil/keeper/`. Mocks are regenerated from +`x/vaas/types/expected_keepers.go` via `make mocks-gen`. Docker-based e2e +tests are under `tests/e2e/`. -### Tests Removed (tested removed features) -- Provider: relay, staking keeper interface, validator set update, consumer lifecycle, genesis, grpc query -- Consumer: relay (slash packet sending) +When tests for features that were removed entirely (e.g. slash throttling, +reward distribution, PSS, power shaping) were not ported. ## Build & Test Commands ```bash -cd vaas -go build ./... -go test ./... +make build # go build ./... +make test # unit tests (25m timeout, excludes e2e) +make lint # golangci-lint +make test-e2e # Docker-based e2e (requires Docker) ``` ## Migration Notes @@ -186,9 +201,15 @@ go test ./... ## Dependencies -The module uses the same dependencies as the original interchain-security v7: -- Cosmos SDK v0.53 -- IBC-Go v10.1.1 -- CometBFT +VAAS targets the same family of dependencies as interchain-security v7, +adjusted for the AtomOne ecosystem. See [`go.mod`](go.mod) for the exact +pinned versions; at the time of writing: + +- Cosmos SDK v0.53.0, replaced via `go.mod` `replace` directive with the + AtomOne fork (`github.com/atomone-hub/cosmos-sdk v0.50.14-atomone.x`). +- IBC-Go v10.2.0 (IBC v2 routing is used; see + [`x/vaas/provider/ibc_module.go`](x/vaas/provider/ibc_module.go) and + [`x/vaas/consumer/ibc_module.go`](x/vaas/consumer/ibc_module.go)). +- CometBFT v0.38.20. All imports were updated from `github.com/cosmos/interchain-security/v7` to `github.com/allinbits/vaas`. diff --git a/app/README.md b/app/README.md index 5a1e73a..e42a741 100644 --- a/app/README.md +++ b/app/README.md @@ -1,28 +1,16 @@ # VAAS Localnet Setup -This guide explains how to run a local VAAS (Validator-as-a-Service) testnet with a provider chain, consumer chain, and IBC relayer. +This guide explains how to run a local VAAS (Validator-as-a-Service) testnet: +a provider chain, a consumer chain, and the IBC v2 relayer (`ts-relayer`) that +connects them. -## Prerequisites - -- Go 1.21+ -- [Hermes IBC Relayer](https://hermes.informal.systems/) v1.10+ - -### Install Hermes - -Install hermes in the root directory of the repository. - -```bash -# macOS ARM64 -curl -L https://github.com/informalsystems/hermes/releases/download/v1.13.3/hermes-v1.13.3-aarch64-apple-darwin.tar.gz | tar -xz -C . - - -# linux amd64 -curl -L https://github.com/informalsystems/hermes/releases/download/v1.13.3/hermes-v1.13.3-x86_64-unknown-linux-gnu.tar.gz | tar -xz -C . +For protocol details, see [`docs/consumer-lifecycle.md`](../docs/consumer-lifecycle.md). -# Verify installation -./hermes version -``` +## Prerequisites +- Go (version pinned in [`go.mod`](../go.mod)) +- Docker — the relayer runs as a container; the image is pulled from + `ghcr.io/allinbits/ibc-v2-ts-relayer:latest`. ## Quick Start @@ -33,22 +21,28 @@ make localnet-start ``` This will: -1. Build the provider and consumer binaries -2. Start the provider chain (`provider-localnet`) in the background -3. Wait for the provider RPC to be ready -4. Initialize and start the consumer chain (`consumer-localnet`) in the background -5. Wait for the consumer RPC to be ready -6. Configure and set up the Hermes relayer (keys, connection, channel) -7. Start the relayer in the background + +1. Build the `provider` and `consumer` binaries into `build/`. +2. Start the provider chain (`provider-localnet`) in the background and wait + for it to produce blocks. +3. Initialize and start the consumer chain (`consumer-localnet`) in the + background, registering it on the provider with an immediate `spawn_time` + and fetching its consumer genesis automatically. +4. Start the `ts-relayer` Docker container, fund relayer keys on both chains + (test mnemonic), and create an **IBC v2 path** between the two chains + (`add-path ... --ibc-version 2`). +5. Trigger a delegation on the provider so the first VSC packet is queued and + relayed to the consumer. Log files: + - Provider: `/tmp/vaas-provider.log` - Consumer: `/tmp/vaas-consumer.log` -- Relayer: `/tmp/vaas-hermes.log` +- Relayer: `/tmp/vaas-ts-relayer.log` (plus `docker logs vaas-ts-relayer`) ## Manual Setup (3-Terminal) -For debugging or development, you can run each component in a separate terminal. +For debugging or development, run each component in a separate terminal. ### Terminal 1: Provider Chain @@ -65,22 +59,28 @@ Wait until you see blocks being produced before continuing. make consumer-start ``` -Wait until you see blocks being produced before continuing. +`consumer-start` runs `consumer-init`, registers the consumer on the provider +(`consumer-create`), polls until the consumer genesis is available, patches +the local consumer `genesis.json`, copies the validator key from the provider +(single-validator localnet), and starts the consumer node. ### Terminal 3: Relayer ```bash # After both chains are running -make relayer-setup -make relayer-start +make ts-relayer-start ``` +This launches the `vaas-ts-relayer` Docker container, registers test keys on +both chains, and creates the IBC v2 path. Stop it later with +`make ts-relayer-stop`. + ## Network Configuration -| Chain | RPC | gRPC | P2P | -|-------|-----|------|-----| -| Provider | localhost:26657 | localhost:9090 | localhost:26656 | -| Consumer | localhost:26667 | localhost:9092 | localhost:26666 | +| Chain | RPC | gRPC | P2P | +| -------- | --------------- | --------------- | --------------- | +| Provider | localhost:26657 | localhost:9090 | localhost:26656 | +| Consumer | localhost:26667 | localhost:9092 | localhost:26666 | ## Useful Commands @@ -90,28 +90,28 @@ make relayer-start # List consumer chains ./build/provider --home ~/.provider-localnet query provider list-consumer-chains -# Get consumer genesis +# Get consumer genesis (built by the provider at launch) ./build/provider --home ~/.provider-localnet query provider consumer-genesis 0 ``` ### Query Consumer ```bash -# Check provider info +# Check provider info on the consumer ./build/consumer --home ~/.consumer-localnet query vaasconsumer provider-info -# List IBC channels -./build/consumer --home ~/.consumer-localnet query ibc channel channels +# Inspect IBC v2 clients (the consumer learns of the provider via its IBC client) +./build/consumer --home ~/.consumer-localnet query ibc client states ``` -### Check Relayer Status +### Inspect the Relayer ```bash -# View Hermes logs -tail -f /tmp/vaas-hermes.log +# Follow the captured config output +tail -f /tmp/vaas-ts-relayer.log -# Query channel status -hermes --config ~/.vaas-hermes/config.toml query channels --chain consumer-localnet +# Live container logs +docker logs -f vaas-ts-relayer ``` ## Clean Up @@ -122,38 +122,48 @@ Stop all processes and remove all localnet data: make localnet-clean ``` -This will kill running provider/consumer/hermes processes, remove chain data directories (`~/.provider-localnet`, `~/.consumer-localnet`), Hermes config (`~/.vaas-hermes`), log files, and temporary files. +This kills the provider/consumer processes, removes the `ts-relayer` +container, deletes chain data directories (`~/.provider-localnet`, +`~/.consumer-localnet`), log files, and `/tmp/vaas-test/`. ## Troubleshooting ### Consumer fails to start with "consumer genesis not found" -The provider needs time to process the consumer creation transaction. The `consumer-start` target includes a retry loop, but if it still fails: +The provider needs time to process the `MsgCreateConsumer` transaction and +reach the consumer's `spawn_time`. `make consumer-start` already includes a +retry loop; if it still fails, check that the consumer was registered: ```bash -# Check if consumer is registered ./build/provider --home ~/.provider-localnet query provider list-consumer-chains -# Manually run genesis fetch +# Re-fetch and re-run manually make consumer-genesis CONSUMER_ID=0 make consumer-run ``` -### Relayer channel creation fails +### Relayer can't find a path / no VSC packets arriving -The VAAS channel must use the genesis client (`07-tendermint-0`). If you see "invalid client" errors: +The provider does not create the IBC v2 client to the consumer itself — the +relayer does, and the provider then discovers it at the next epoch boundary +(`blocks_per_epoch=10` in localnet). If you started the relayer but no VSC +packet arrives within ~30s after a delegation: ```bash -# Check which clients exist -./build/consumer --home ~/.consumer-localnet query ibc client states +# Confirm ts-relayer is running +docker ps --filter name=vaas-ts-relayer + +# Confirm the IBC v2 path was registered +docker exec vaas-ts-relayer /bin/with_keyring ibc-v2-ts-relayer list-paths -# The genesis client should track provider-localnet -# Ensure you're using: --a-client 07-tendermint-0 --b-client 07-tendermint-0 +# Confirm IBC v2 clients exist on both ends +./build/provider --home ~/.provider-localnet query ibc client states +./build/consumer --home ~/.consumer-localnet query ibc client states ``` ### Port conflicts -If ports are already in use, clean up any existing processes: +If ports are already in use, find the offending process: ```bash lsof -i :26657 # Provider RPC @@ -164,15 +174,21 @@ lsof -i :9092 # Consumer gRPC ## Architecture -``` -┌─────────────────┐ IBC/VAAS ┌─────────────────┐ -│ │◄────────Channel──────────►│ │ -│ Provider │ │ Consumer │ -│ Chain │ Hermes Relayer │ Chain │ -│ │◄─────────────────────────►│ │ -│ Port: provider │ │ Port: consumer │ -└─────────────────┘ └─────────────────┘ - :26657 :26667 +```text + ┌─────────────────┐ IBC v2 (clients) ┌─────────────────┐ + │ │◄────── VSC packets ────────────►│ │ + │ Provider │ │ Consumer │ + │ chain │ ts-relayer (Docker) │ chain │ + │ vaasprovider │◄───────────────────────────────►│ vaasconsumer │ + │ app ID │ │ app ID │ + └─────────────────┘ └─────────────────┘ + :26657 :26667 ``` -The provider chain manages consumer chain registration and validator sets. The consumer chain receives validator updates from the provider via the VAAS IBC channel. The Hermes relayer handles packet relay between the two chains. +VAAS is IBC v2 only. There is no channel handshake. Instead, the relayer +creates an IBC v2 client on each side pointing at the counterparty chain and +registers the counterparty (`add-path`). The provider scans its clients once +per epoch and adopts the one targeting the consumer (see +`discoverActiveConsumerClient`). All VSC packets after that point are sent +over that client; packets are out-of-order and deduplicated on the consumer +via `HighestValsetUpdateID`. diff --git a/docs/consumer-transition.md b/docs/consumer-transition.md new file mode 100644 index 0000000..fab88b1 --- /dev/null +++ b/docs/consumer-transition.md @@ -0,0 +1,196 @@ +# Consumer Transition (Standalone → VAAS Consumer) + +> **Status:** future consideration. VAAS does **not** currently support +> transitioning an existing standalone Cosmos chain into a VAAS consumer; +> the consumer module only supports launching as a new chain. This document +> captures the consequences, requirements, and current code state so that +> future work has a starting point. It is **not** an implementation +> specification. Moreover, transition from non-canonical Cosmos chains +> may require additional design work. + +A standalone-to-consumer transition lets an existing sovereign Cosmos +chain — one already producing blocks under its own `x/staking` module +(or equivalent) — swap its local proof-of-stake for the provider's +validator set without a chain-id change, a halt-and-restart, or a fork. +The chain keeps its account state, balances, and history; it gains the +provider's validator set as the consensus signer. + +The [`interchain-security`](https://github.com/cosmos/interchain-security) +implementation supports this path. VAAS inherited the wiring, then the +rewrite simplified it away on the assumption that consumers would only +ever launch fresh. However, this initial simplification will need to be +revisited, we expect this transition path to be a requirement in the future. + +--- + +## What a transition does + +Before transition, the chain is a standard Cosmos chain: + +- Local `x/staking` selects validators from local bonded stake +- Local validators sign blocks and earn local rewards. +- Local slashing handles equivocation and downtime. + +After transition, the chain is a VAAS consumer: + +- The provider's active validator set signs blocks (via VSC packets). +- Local staking remains *registered* (for slashing/jailing of validators + that misbehaved while the chain was still standalone) but stops + selecting block proposers. +- Slashing and unbonding-period semantics shift to the provider's + parameters where applicable. + +The transition is **atomic at a specific block height**: at the chosen +height, the consumer module receives the provider's initial validator set +and replaces the local set in `EndBlock`. There is no observable downtime +for users, balances, or contracts. + +--- + +## Consequences + +**For chain operators** +- The chain commits to the provider's security guarantees and accepts the + provider's MaxProviderConsensusValidators cap. +- Local governance, fees, and application modules continue unchanged. +- The chain must coordinate the transition height in advance with the + provider (via `MsgCreateConsumer` lifecycle, off-chain coordination, or + governance proposal). + +**For local validators** +- Validators that were local-only and are not in the provider's set lose + block-signing rights at the transition height. They remain technically + bonded for the unbonding period to allow slashing of past misbehaviour. +- Validators that exist in both the local set and the provider set should + use `MsgAssignConsumerKey` ahead of the transition height so they + continue signing under the same consensus identity. + +**For delegators** +- Delegations to local validators continue to exist on-chain but cease + earning local rewards once block-signing moves to the provider's set. +- Delegators may re-delegate to provider-side validators or unbond + normally. + +**For IBC connections to other chains** +- Existing IBC light clients on third-party chains that track this chain + go **stale** at the transition height. Tendermint light clients accept a + new header only if validators signing it overlap with the previously + trusted set by at least the client's trust level (1/3 by default). A + standalone-to-consumer transition rotates the validator set wholesale + to the provider's set, so the overlap is effectively zero and the light + client cannot follow the update — it gets stuck at the pre-transition + height and any packets relayed against it will fail to verify. +- Counterparty chains have two recovery paths, both off-chain + coordination: + - **Gov-gated client substitution** ([`MsgRecoverClient`](https://ibc.cosmos.network/main/ibc/proto-docs.html#ibc.core.client.v1.MsgRecoverClient) + in IBC-go): the counterparty chain's governance votes to substitute + the stale client with a freshly-created one tracking the new + (provider-driven) validator set. This preserves the existing + connection and channels, so balances and packet sequences are + retained. + - **Full reconnection**: tear down the existing client/connection/ + channels and create new ones from scratch. Cheaper to execute but + loses channel state, in-flight packets, and any client-side + invariants the counterparty relied on. +- New IBC v2 clients between the consumer (post-transition) and the + provider must be created by the relayer as part of the standard + consumer launch flow. +- **Operational implication.** Chain operators and counterparty teams + must coordinate the transition height well in advance so counterparty + governance proposals (or reconnection runbooks) can be staged and + executed. This is the highest user-visible cost of a transition and + should be treated as a hard prerequisite more than a follow-up. + +--- + +## Requirements for implementation + +1. **Genesis-time `preVAAS` flag.** The consumer's `InitGenesis` must + accept a flag indicating the chain is mid-transition. When set, the + consumer module: + - Skips applying the provider's initial validator set to CometBFT + (the local staking keeper continues to manage validators for one + more block). + - Marks the chain as previously-standalone for later cleanup. + +2. **`standaloneStakingKeeper` plumbing.** The consumer module needs an + explicit reference to the chain's prior `x/staking` keeper so it can: + - Query the last local bonded validator set during the transition. + - Allow the slashing module to jail/slash validators for infractions + that occurred while the chain was standalone, even after the + provider set takes over. + - This reference is set after the keeper constructor by the app via + `SetStandaloneStakingKeeper`. + +3. **Upgrade handler.** The chain operator runs a coordinated software + upgrade at the transition height that: + - Adds the VAAS consumer module to the app. + - Provides genesis state with `preVAAS = true` and the provider's + client/consensus state. + - Stops the local staking module from emitting validator-set updates + (handled today by `x/vaas/no_valupdates_staking`). + +4. **Provider-side `MsgCreateConsumer`.** The provider chain must already + have the consumer registered through the standard lifecycle + (`REGISTERED → INITIALIZED → LAUNCHED`) so that by the transition height + the provider is ready to send VSC packets. + +5. **Relayer coordination.** The IBC v2 clients must exist on both sides + at the transition height. In VAAS today, client creation is the + relayer's responsibility; for a transition this needs to be scheduled + to land just before the transition height. + +6. **Counterparty client-recovery coordination.** Every third-party chain + that runs an IBC light client tracking the transitioning chain must + pre-stage a `MsgRecoverClient` governance proposal (or a full + reconnection runbook) targeting the transition height — see the + *For IBC connections to other chains* note above. Without this, + existing connections go stale and packet traffic halts on those + lanes. This is a prerequisite for transition more than a follow-up. + +7. **Slashing window for prior misbehaviour.** The provider must respect + the chain's prior unbonding period for slashing equivocations that + happened on the chain *before* the transition. Implementation needs + to decide whether to forward this evidence to the consumer's residual + local staking keeper or handle it provider-side. + +--- + +## Current code state + +The consumer module retains the following wiring as a reference; **none +of it is reachable** in the current launch flow: + +- `consumer/keeper/keeper.go` — fields `PreVAAS`, `InitialValSet`, + `PrevStandaloneChain`, `standaloneStakingKeeper`; methods + `IsPreVAAS`, `SetPreVAASTrue`, `DeletePreVAAS`, `SetInitialValSet`, + `GetInitialValSet`, `GetLastStandaloneValidators`, + `GetLastBondedValidators`, `MarkAsPrevStandaloneChain`, + `IsPrevStandaloneChain`, `SetStandaloneStakingKeeper`. +- `consumer/keeper/genesis.go` — the `if state.PreVAAS { … }` branches in + `InitGenesis`. +- `consumer/types/keys.go` — `PreVAASPrefix`, `InitialValSetPrefix`, + `PrevStandaloneChainPrefix`. +- `x/vaas/types/shared_consumer.proto` — `ConsumerGenesisState.preVAAS` + field. +- `proto/vaas/consumer/v1/genesis.proto` — `GenesisState.preVAAS` field. +- The provider always passes `preVAAS = false` to + `NewInitialConsumerGenesisState` in `provider/keeper/consumer_lifecycle.go`. + +This code is preserved deliberately as a reference for the eventual +transition implementation. It is **not** a working blueprint: any future +implementation will need a fresh design pass because the surrounding flow +(IBC v2, `cosmossdk.io/collections`, the simplified lifecycle) has +changed materially since the original ICS implementation. Treat the +existing wiring as a sketch of the data dependencies, not a guide to the +control flow. + +--- + +## References + +- ICS implementation: [`x/ccv/consumer/keeper`](https://github.com/cosmos/interchain-security) + (look for `PreCCV`, `SovereignChangeover`, and related state). +- ICS docs: [Sovereign chain to consumer chain changeover](https://cosmos.github.io/interchain-security/consumer-development/changeover-procedure). +- VAAS architecture: [`DESIGN_RATIONALE.md`](../DESIGN_RATIONALE.md), + [`docs/consumer-lifecycle.md`](consumer-lifecycle.md).