Skip to content
Merged
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
3c550ac
feat(ibc): foundation layer for IBC v2 migration
clockworkgr Feb 4, 2026
d97d99d
feat(ibc): provider and consumer module updates for IBC v2
clockworkgr Feb 4, 2026
ea27723
feat(ibc): add IBC v2 deprecation notes to provider proto
clockworkgr Feb 4, 2026
c79d3e5
feat(ibc): add IBC v2 deprecation notes to IBC module callbacks
clockworkgr Feb 4, 2026
636d0ad
feat(ibc): add IBC v2 notes to registration flow and genesis
clockworkgr Feb 4, 2026
924dadf
feat(ibc): regenerate mocks with IBCPacketHandler interface
clockworkgr Feb 4, 2026
9fc86eb
feat(ibc): implement IBC v2 client-based packet sending
clockworkgr Feb 4, 2026
0066c14
feat(ibc): add IBC v2 acknowledgement and timeout handlers
clockworkgr Feb 4, 2026
d82c03b
feat(ibc): add IBC v2 packet reception for consumer
clockworkgr Feb 5, 2026
2777063
feat: add IBC v2 module implementations for provider and consumer
clockworkgr Feb 5, 2026
5f3aae6
test: add IBC v2 relay tests for provider and consumer
clockworkgr Feb 5, 2026
5d89e76
feat(ibc): add interface compliance checks for IBC v2 modules
clockworkgr Feb 5, 2026
3f547e4
test: add HighestValsetUpdateID test for IBC v2 out-of-order handling
clockworkgr Feb 5, 2026
2baf2e1
test: add SendVSCPacketsToChainV2 tests for IBC v2 packet sending
clockworkgr Feb 5, 2026
0853d78
feat(ibc): add GetProviderInfoV2 for IBC v2 client-based queries
clockworkgr Feb 5, 2026
38e8065
test: add IBC v2 integration tests for provider and consumer
clockworkgr Feb 5, 2026
206ae10
docs: update README with IBC v2 support documentation
clockworkgr Feb 5, 2026
de47f78
docs: add IBC v2 API documentation and deployment guide
clockworkgr Feb 5, 2026
a1e7063
chore: Minor lint fixes
clockworkgr Feb 5, 2026
56844dd
fix: Leave go.mod / Dep versions untouched for now
clockworkgr Feb 5, 2026
011afec
fix: Add client validation to CreateConsumer and improve/document reg…
clockworkgr Feb 5, 2026
f81fce1
Merge branch 'main' into feat/ibcv2
clockworkgr Feb 5, 2026
a866c3e
Merge branch 'main' into feat/ibcv2
julienrbrt Mar 3, 2026
6fb95c5
fix go.mod
julienrbrt Mar 3, 2026
8eba2e4
tidy all;
julienrbrt Mar 3, 2026
28de34c
wip
julienrbrt Mar 3, 2026
7e9b589
lint
julienrbrt Mar 3, 2026
1a2ae4f
linting
julienrbrt Mar 4, 2026
f9d8ce4
fix(ibc): address provider v2 review feedback and restore isolated de…
Pantani Mar 31, 2026
a245b9b
Update GetHighestValsetUpdateID to return found status
julienrbrt Mar 31, 2026
4248b35
wire ibc v2
julienrbrt Apr 1, 2026
0c1c0f6
rm
julienrbrt Apr 1, 2026
1174043
rework as v2 only
julienrbrt Apr 1, 2026
fc68d1b
fix keys
julienrbrt Apr 2, 2026
63d80b0
remove fallbacks and deprecated fields
julienrbrt Apr 2, 2026
a9be2fc
more cleanup of deprecated v1 fields
julienrbrt Apr 2, 2026
96a192e
attempt ts-relayer implementation
julienrbrt Apr 2, 2026
dfe56fc
remove packet handler abstraction for channelv2
julienrbrt Apr 2, 2026
ad69841
Merge branch 'main' into feat/ibcv2
julienrbrt Apr 2, 2026
f2f5749
lint
julienrbrt Apr 2, 2026
19020ed
chore: remove hermes
julienrbrt Apr 7, 2026
e257916
Merge branch 'main' into feat/ibcv2
julienrbrt Apr 7, 2026
46eec1f
replace hermes by ts-relayer
julienrbrt Apr 7, 2026
b936cb4
updates
julienrbrt Apr 7, 2026
2d0804f
chore: add debug logs
julienrbrt Apr 8, 2026
d0c7cca
use https://github.com/allinbits/ibc-v2-ts-relayer/pull/25
julienrbrt Apr 9, 2026
0bd19f1
let relayer create client
julienrbrt Apr 9, 2026
2883392
fix expected keeper
julienrbrt Apr 10, 2026
16aa80a
wire clientv2keeper
julienrbrt Apr 13, 2026
c242749
cleanup
julienrbrt Apr 13, 2026
a7fe2b5
fix prefix
julienrbrt Apr 13, 2026
6b6ca4f
cleanup old commands
julienrbrt Apr 13, 2026
2e82ee4
remove counterparty hack
julienrbrt Apr 13, 2026
ed20cd8
cleanup docs
julienrbrt Apr 13, 2026
c096da1
clean helpers
julienrbrt Apr 13, 2026
afc45e2
no cached tests
julienrbrt Apr 13, 2026
e89d9a0
re-add hack merkle fix
julienrbrt Apr 13, 2026
fd86b5f
refactor: improve consumer validation to trust IBC v2 and auto-create
julienrbrt Apr 14, 2026
9abce8e
fix: proto field numbers
julienrbrt Apr 15, 2026
72993a9
add v2 keeper interface
julienrbrt Apr 15, 2026
e08887b
add comment
julienrbrt Apr 15, 2026
2947c8d
switch client when fronzen/expired
julienrbrt Apr 15, 2026
4ea2132
update deps
julienrbrt Apr 15, 2026
b4539cb
fix first time connection
julienrbrt Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/Eureka (client-based, out-of-order). IBC v2 uses application IDs `vaas/provider` and `vaas/consumer` instead of port IDs.
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.

## Build & Test Commands

Expand Down Expand Up @@ -48,6 +48,7 @@ The core protocol lives in `x/vaas/` with two symmetric modules:
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).

Two helper modules replace standard Cosmos modules to prevent automatic validator set updates on consumer chains:

- `x/vaas/no_valupdates_staking/` — staking without EndBlock valset exports
- `x/vaas/no_valupdates_genutil/` — genutil without gentx-based valset init

Expand Down Expand Up @@ -89,6 +90,7 @@ Under `proto/vaas/`: `v1/` (shared wire types like `ValidatorSetChangePacketData
## Lint / Import Ordering

Import groups (enforced by gci in `.golangci.yml`):

1. Standard library
2. Third-party
3. `github.com/cometbft/cometbft`
Expand Down
129 changes: 50 additions & 79 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ provider-start: build-apps
$(providerd) config set client chain-id provider-localnet
$(providerd) config set client keyring-backend test
$(providerd) keys add val
$(providerd) genesis add-genesis-account val 1000000000000uatone
$(providerd) genesis add-genesis-account val 1000000000000uatone
$(providerd) keys add user
$(providerd) genesis add-genesis-account user 1000000000uatone
$(providerd) genesis add-genesis-account user 1000000000uatone
$(providerd) genesis gentx val 1000000000uatone
$(providerd) genesis collect-gentxs

Expand Down Expand Up @@ -170,63 +170,45 @@ consumer-start: consumer-init consumer-create
.PHONY: consumer-init consumer-create consumer-genesis consumer-run consumer-start

###############################################################################
### Relayer ###
### TS Relayer ###
###############################################################################

HERMES ?= ./hermes
HERMES_CONFIG ?= $(HOME)/.vaas-hermes/config.toml
TS_RELAYER ?= ghcr.io/allinbits/ibc-v2-ts-relayer:latest

HERMES_CMD = $(HERMES) --config $(HERMES_CONFIG)

# Create Hermes configuration
relayer-config:
@chmod +x ./scripts/hermes-config.sh
@./scripts/hermes-config.sh

# Create relayer keys and fund them (requires both chains running)
RELAYER_MNEMONIC_FILE = /tmp/vaas-test/relayer_mnemonic.txt

relayer-keys:
@echo "Creating relayer mnemonic file..."
@mkdir -p /tmp/vaas-test
@echo "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" > $(RELAYER_MNEMONIC_FILE)
@echo "Setting up relayer account on provider..."
@chmod +x ./scripts/add-relayer-key.sh
@./scripts/add-relayer-key.sh ./build/provider $(provider_home)
@echo "Funding relayer on provider..."
$(providerd) tx bank send val $$($(providerd) keys show relayer -a) 100000000uatone --fees 5000uatone -y
@echo "Waiting for provider tx..."
@sleep 5
@echo "Removing old Hermes keys if they exist..."
@rm -rf $(HOME)/.vaas-hermes/keys
@echo "Adding relayer key to Hermes for provider..."
$(HERMES_CMD) keys add --chain provider-localnet --mnemonic-file $(RELAYER_MNEMONIC_FILE) --key-name relayer
@echo "Adding relayer key to Hermes for consumer..."
$(HERMES_CMD) keys add --chain consumer-localnet --mnemonic-file $(RELAYER_MNEMONIC_FILE) --key-name relayer
@echo "Relayer keys setup complete"
@echo "Relayer address: $$($(providerd) keys show relayer -a)"

# Create the CCV/VAAS channel between provider and consumer
# IMPORTANT: Must use genesis clients (07-tendermint-0) for VAAS channel
# After a fresh localnet-clean, this will create connection-0
relayer-channel:
@echo "Creating connection using genesis clients (07-tendermint-0)..."
$(HERMES_CMD) create connection --a-chain consumer-localnet --a-client 07-tendermint-0 --b-client 07-tendermint-0
@sleep 2
@echo "Creating VAAS channel on connection-0..."
$(HERMES_CMD) create channel --a-chain consumer-localnet --a-connection connection-0 --a-port consumer --b-port provider --order ordered --channel-version 1
@echo "Channel created"

# Start the relayer
relayer-start:
@echo "Starting Hermes relayer..."
$(HERMES_CMD) start
ts-relayer-start:
@echo "Starting ts-relayer..."
@docker rm -f vaas-ts-relayer 2>/dev/null || true
@docker run -d --name vaas-ts-relayer --network host \
--cap-add IPC_LOCK \
$(TS_RELAYER)
@sleep 3
@echo "Configuring ts-relayer..."
@docker exec vaas-ts-relayer /bin/with_keyring ibc-v2-ts-relayer add-mnemonic \
-c provider-localnet \
--mnemonic "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"
@docker exec vaas-ts-relayer /bin/with_keyring ibc-v2-ts-relayer add-mnemonic \
-c consumer-localnet \
--mnemonic "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art"
@docker exec vaas-ts-relayer /bin/with_keyring ibc-v2-ts-relayer add-gas-price \
-c provider-localnet --gas-adjustment 2.0 0.025uatone
@docker exec vaas-ts-relayer /bin/with_keyring ibc-v2-ts-relayer add-gas-price \
-c consumer-localnet --gas-adjustment 2.0 0.025uatone
@echo "Creating IBC v2 path..."
@docker exec vaas-ts-relayer /bin/with_keyring ibc-v2-ts-relayer add-path \
-s provider-localnet \
-d consumer-localnet \
--surl http://127.0.0.1:26657 \
--durl http://127.0.0.1:26667 \
--ibc-version 2
@echo "ts-relayer configured and running (log: /tmp/vaas-ts-relayer.log)"

ts-relayer-stop:
@echo "Stopping ts-relayer..."
-@docker rm -f vaas-ts-relayer 2>/dev/null || true

.PHONY: ts-relayer-start ts-relayer-stop

# Full relayer setup (requires both chains running)
relayer-setup: relayer-config relayer-keys relayer-channel
@echo "Relayer setup complete. Run 'make relayer-start' to start relaying."

.PHONY: relayer-install relayer-config relayer-keys relayer-channel relayer-start relayer-setup

###############################################################################
### Full Localnet ###
Expand All @@ -237,21 +219,20 @@ localnet-clean:
@echo "Stopping running processes..."
-@pkill -f "provider.*start" 2>/dev/null || true
-@pkill -f "consumer.*start" 2>/dev/null || true
-@pkill -f hermes 2>/dev/null || true
@$(MAKE) ts-relayer-stop
@sleep 2
rm -rf $(provider_home) $(consumer_home)
rm -rf $(HOME)/.vaas-hermes
rm -f /tmp/vaas-provider.log /tmp/vaas-consumer.log /tmp/vaas-hermes.log
rm -f /tmp/vaas-provider.log /tmp/vaas-consumer.log /tmp/vaas-ts-relayer.log
rm -rf /tmp/vaas-test
@echo "Localnet data cleaned"

# Start the full localnet: provider, consumer, relayer — all in one command
# Start the full localnet: provider, consumer, and ts-relayer
localnet-start: build-apps
@echo "=== Starting VAAS Localnet ==="
@echo ""
@echo "Step 1/6: Starting provider chain in background..."
@echo "Step 1/4: Starting provider chain in background..."
@$(MAKE) provider-start > /tmp/vaas-provider.log 2>&1 &
@echo " Waiting for provider to produce blocks (http://localhost:26657) ..."
@echo " Waiting for provider to produce blocks..."
@for i in $$(seq 1 60); do \
HEIGHT=$$(curl -sf http://localhost:26657/status 2>/dev/null | sed -n 's/.*"latest_block_height":"\([0-9]*\)".*/\1/p'); \
if [ -n "$$HEIGHT" ] && [ "$$HEIGHT" -gt 0 ] 2>/dev/null; then \
Expand All @@ -265,9 +246,9 @@ localnet-start: build-apps
sleep 2; \
done
@echo ""
@echo "Step 2/6: Starting consumer chain in background..."
@echo "Step 2/4: Starting consumer chain in background..."
@$(MAKE) consumer-start > /tmp/vaas-consumer.log 2>&1 &
@echo " Waiting for consumer to produce blocks (http://localhost:26667) ..."
@echo " Waiting for consumer to produce blocks..."
@for i in $$(seq 1 90); do \
HEIGHT=$$(curl -sf http://localhost:26667/status 2>/dev/null | sed -n 's/.*"latest_block_height":"\([0-9]*\)".*/\1/p'); \
if [ -n "$$HEIGHT" ] && [ "$$HEIGHT" -gt 0 ] 2>/dev/null; then \
Expand All @@ -281,15 +262,10 @@ localnet-start: build-apps
sleep 2; \
done
@echo ""
@echo "Step 3/6: Configuring relayer..."
@if [ -x $(HERMES) ]; then echo " Found Hermes binary"; else echo " Could not find Hermes binary. Please follow app/README.md to install it." && $(MAKE) localnet-clean && exit 1; fi
@$(MAKE) relayer-setup > /tmp/vaas-hermes.log 2>&1
@echo "Step 3/4: Starting ts-relayer..."
@$(MAKE) ts-relayer-start > /tmp/vaas-ts-relayer.log 2>&1
@echo ""
@echo "Step 4/6: Starting relayer in background..."
@$(MAKE) relayer-start >> /tmp/vaas-hermes.log 2>&1 &
@sleep 3
@echo ""
@echo "Step 5/6: Triggering valset change to send first VSC packet..."
@echo "Step 4/4: Triggering valset change to send first VSC packet..."
@VALOPER=$$($(providerd) keys show val --bech val -a 2> /dev/null); \
$(providerd) tx staking delegate $$VALOPER 1000000uatone --from user --fees 5000uatone -y > /dev/null 2>&1
@echo " Delegation sent. Waiting for VSC packet to be relayed..."
Expand All @@ -309,7 +285,7 @@ localnet-start: build-apps
@echo ""
@echo " Provider: http://localhost:26657 (log: /tmp/vaas-provider.log)"
@echo " Consumer: http://localhost:26667 (log: /tmp/vaas-consumer.log)"
@echo " Relayer: (log: /tmp/vaas-hermes.log)"
@echo " Relayer: ts-relayer container (log: /tmp/vaas-ts-relayer.log)"
@echo ""
@echo " To stop: make localnet-clean"

Expand All @@ -324,17 +300,12 @@ docker-build-debug:
@echo "Building VAAS e2e chain image..."
docker build -t cosmos/vaas-e2e -f tests/e2e/docker/e2e.Dockerfile .

# Build the Hermes relayer Docker image
docker-build-hermes:
@echo "Building Hermes e2e image..."
cd tests/e2e/docker && docker build -t ghcr.io/cosmos/hermes-e2e:1.13.1 -f hermes.Dockerfile .

# Build all Docker images needed for e2e tests
docker-build-all: docker-build-debug docker-build-hermes
docker-build-all: docker-build-debug

# Run the e2e integration test suite
test-e2e: docker-build-all
@echo "Running e2e tests..."
cd tests/e2e && go test -timeout=25m -v ./...
cd tests/e2e && go test -timeout=25m -v ./... --count=1

.PHONY: docker-build-debug docker-build-hermes docker-build-all test-e2e
.PHONY: docker-build-debug docker-build-all test-e2e
56 changes: 37 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,48 @@ VAAS allows Cosmos blockchains to lease their proof-of-stake security to consume

## Features

## IBC v2 support

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.

### Kept from ICS

| Feature | Description |
|---------|-------------|
| Consumer Lifecycle | Full lifecycle management (REGISTERED → INITIALIZED → LAUNCHED → STOPPED → DELETED) |
| Key Assignment | Validators can use different consensus keys per consumer chain |
| Per-Consumer Infraction Parameters | Customizable slash/jail parameters per consumer |
| VSC Packets | Validator set updates sent at epoch boundaries |
| Double Voting Evidence | Handle double voting evidence from consumers |
| Light Client Misbehavior | Detection and logging of misbehavior |
| Consumer Metadata | Name, description, metadata for chain discovery |
| Client/Connection Reuse | Reuse existing IBC client/connection when creating consumer |
| Feature | Description |
| ---------------------------------- | ----------------------------------------------------------------------------------- |
| Consumer Lifecycle | Full lifecycle management (REGISTERED → INITIALIZED → LAUNCHED → STOPPED → DELETED) |
| Key Assignment | Validators can use different consensus keys per consumer chain |
| Per-Consumer Infraction Parameters | Customizable slash/jail parameters per consumer |
| VSC Packets | Validator set updates sent at epoch boundaries |
| Double Voting Evidence | Handle double voting evidence from consumers |
| Light Client Misbehavior | Detection and logging of misbehavior |
| Consumer Metadata | Name, description, metadata for chain discovery |
| Client/Connection Reuse | Reuse existing IBC client when creating consumer |

### Removed from ICS

| Feature | Reason |
|---------|--------|
| Partial Set Security (PSS) | All validators validate all consumers |
| Top N / Opt-In Chains | No validator selection per consumer |
| Power Shaping | No caps, allowlists, denylists, priority lists |
| Consumer Reward Distribution | No cross-chain rewards |
| Slash Packet Throttling | Simplified slash handling |
| Per-Consumer Commission Rates | Validators use same commission as provider |
| Standalone-to-Consumer Changeover | Only new chains as consumers |
| Feature | Reason |
| --------------------------------- | ---------------------------------------------- |
| Partial Set Security (PSS) | All validators validate all consumers |
| Top N / Opt-In Chains | No validator selection per consumer |
| Power Shaping | No caps, allowlists, denylists, priority lists |
| Consumer Reward Distribution | No cross-chain rewards |
| 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 |

## Build & Test

```bash
make build # go build ./...
make test # unit tests (excludes e2e)
make lint # golangci-lint

# E2E (Docker-based, spins up provider + consumer + ts-relayer)
make docker-build-all
make test-e2e
```

## Learn More

Expand Down
4 changes: 2 additions & 2 deletions app/consumer/ante/msg_filter_ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
type (
// ConsumerKeeper defines the interface required by a consumer module keeper.
ConsumerKeeper interface {
GetProviderChannel(ctx context.Context) (string, bool)
GetProviderClientID(ctx context.Context) (string, bool)
}

// MsgFilterDecorator defines an AnteHandler decorator that enables message
Expand All @@ -32,7 +32,7 @@ func (mfd MsgFilterDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bo

// If the CCV channel has not yet been established, then we must only allow certain
// message types.
if _, ok := mfd.ConsumerKeeper.GetProviderChannel(ctx); !ok {
if _, ok := mfd.ConsumerKeeper.GetProviderClientID(ctx); !ok {
if !hasValidMsgsPreCCV(tx.GetMsgs()) {
return ctx, fmt.Errorf("tx contains unsupported message types at height %d", currHeight)
}
Expand Down
Loading
Loading