Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 4 additions & 0 deletions mise-test-setup/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/data/
/generated/
__pycache__/
mise.local.toml
142 changes: 142 additions & 0 deletions mise-test-setup/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Mise Test Setup

`mise test setup` is a local mise-driven setup for running the shutter service
flow with Ethereum, shuttermint, keypers, and supporting infrastructure.

For the normal happy path, the two main commands are:

```bash
mise run wait-for-initial-dkg
mise run test-decryption
```

## Main Tasks

- `mise run wait-for-initial-dkg`

- Bring the system up, add the initial keyper set, and wait until the initial
DKG succeeds.

- `mise run add-keyper-set --indices 0,1,2,3 --threshold 3`

- Add a new keyper set on-chain for the selected keypers.

- `mise run wait-for-dkg --keyper-set-index 2`

- Wait until a given keyper set finishes DKG successfully.

- `mise run wait-for-dkg --eon 5`

- Wait until a specific DKG finishes. This exits with a nonzero status if that
eon completes with failure.

- `mise run test-decryption`

- Submit a decryption trigger with default values and wait for the
corresponding decryption key.

- `mise run submit-identity-registration`

- Submit an identity registration transaction. By default it chooses the
latest eon, a random identity prefix, and a near-future timestamp.

- `mise run wait-for-decryption-key --eon 1 --identity-prefix 0x...`

- Wait until the decryption key for the given `(eon, identity-prefix)` becomes
available.

- `mise run clean`
- Stop the containers and reset the environment.

## Supporting Tasks

These are setup and intermediate tasks that the main flow uses internally. You
can still run them directly if you want to test specific parts of the system:

- `gen-compose`
- `up-db`
- `up-ethereum`
- `deploy`
- `gen-keyper-configs`
- `init-chain-seed`
- `init-chain-nodes`
- `patch-genesis`
- `init-keyper-dbs`
- `up`
- `down`
- `clean`
- `add-initial-keyper-set`

Dependency flow for `wait-for-initial-dkg`:

```text
- `wait-for-initial-dkg`
- `up`
- `patch-genesis`
- `init-chain-nodes`
- `init-chain-seed`
- `gen-compose`
- `gen-keyper-configs`
- `deploy`
- `up-ethereum`
- `gen-compose`
- `init-keyper-dbs`
- `up-db`
- `gen-compose`
- `gen-keyper-configs`
- `deploy`
- `up-ethereum`
- `gen-compose`
- `add-initial-keyper-set`
- `gen-keyper-configs`
- `deploy`
- `up-ethereum`
- `gen-compose`
```

## Relevant Env Vars

- `DEPLOYMENT_TYPE`

- Selects the deployment mode (`service` or `gnosis`). Only `service` is fully
supported.

- `NUM_KEYPERS`

- Total number of keypers and chain nodes to initialize and run.

- `INITIAL_KEYPER_SET_INDICES`

- Comma-separated list of keyper indices that belong to the initial
validator/keyper set.

- `INITIAL_THRESHOLD`

- Threshold for the initial keyper set.

- `ACTIVATION_DELTA`

- Activation delay used when adding a keyper set on-chain.

- `DECRYPTION_TRIGGER_TIMESTAMP_DELTA`

- Default offset used when submitting an identity registration without an
explicit timestamp.

- `DEPLOY_KEY`
- Private key used for deployments and contract interactions.

## Task Model

- Tasks use `#MISE depends=[...]` to express ordering.
- Tasks are implemented in bash or Python.
- Tasks are meant to be idempotent.

## Gnosis Note

Support for the gnosis keyper flow is not yet fully implemented in the
`mise test setup`.

- The fully exercised path is `DEPLOYMENT_TYPE=service`.
- Some higher-level tasks, especially around identity registration and
decryption testing, currently assume the service flow.
80 changes: 80 additions & 0 deletions mise-test-setup/compose.common.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
services:
db:
image: postgres:15
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_HOST_AUTH_METHOD=trust
volumes:
- ${DATA_DIR}/db:/var/lib/postgresql/data
healthcheck:
test: pg_isready -U postgres
interval: 1s
timeout: 1s
retries: 10

ethereum:
image: ghcr.io/foundry-rs/foundry:v1.5.0
volumes:
- ${DATA_DIR}/ethereum:/state
working_dir: /anvil
entrypoint: anvil
command:
- --host
- 0.0.0.0
- --chain-id
- ${ETHEREUM_CHAIN_ID}
- --block-time
- ${ETHEREUM_BLOCK_TIME}
- --state
- /state
healthcheck:
test: cast block-number --rpc-url http://127.0.0.1:8545 >/dev/null
interval: 1s
timeout: 1s
retries: 30

chain-seed:
image: rolling-shutter
entrypoint: /rolling-shutter
command:
- chain
- --config
- /chain/config/config.toml
volumes:
- ${DATA_DIR}/chain-seed:/chain
healthcheck:
test: curl -sf http://127.0.0.1:26657/status >/dev/null
interval: 1s
timeout: 1s
retries: 10

bootnode:
image: rolling-shutter
entrypoint: /rolling-shutter
command:
- p2pnode
- --config
- /config.toml
volumes:
- ${STATIC_DIR}/bootstrap.toml:/config.toml

contracts:
build:
context: https://github.com/shutter-network/contracts.git#docker
profiles:
- tools
environment:
- FOUNDRY_ETH_RPC_URL=http://ethereum:8545
volumes:
- ${DATA_DIR}/contracts/out:/app/out
- ${DATA_DIR}/contracts/broadcast:/app/broadcast

rolling-shutter:
image: rolling-shutter
profiles:
- tools
entrypoint:
- /rolling-shutter
volumes:
- ${DATA_DIR}:/data
Empty file.
45 changes: 45 additions & 0 deletions mise-test-setup/compose.keypers.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
x-chain-base: &chain_base
image: rolling-shutter
entrypoint:
- /rolling-shutter
command:
- chain
- --config
- /chain/config/config.toml
healthcheck:
test: curl -sf http://127.0.0.1:26657/status >/dev/null
interval: 1s
timeout: 1s
retries: 10
depends_on:
chain-seed:
condition: service_healthy

x-keyper-base: &keyper_base
image: rolling-shutter
entrypoint:
- /rolling-shutter
command:
- {{ keyper_command }}
- --config
- /config.toml

services:
{%- for i in range(num_keypers|int) %}
chain-{{ i }}:
<<: *chain_base
volumes:
- ../${DATA_DIR}/chain-{{ i }}/:/chain

keyper-{{ i }}:
<<: *keyper_base
depends_on:
bootnode:
condition: service_started
chain-{{ i }}:
condition: service_healthy
volumes:
- ../${DATA_DIR}/keyper-{{ i }}.toml:/config.toml
{%- if not loop.last %}
{% endif %}
{%- endfor %}
Empty file.
2 changes: 2 additions & 0 deletions mise-test-setup/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include:
- ./generated/compose.yml
4 changes: 4 additions & 0 deletions mise-test-setup/compose.yml.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include:
- ../compose.common.yml
- ./compose.keypers.yml
- ../compose.{{ deployment_type }}.yml
58 changes: 58 additions & 0 deletions mise-test-setup/mise-tasks/add-initial-keyper-set
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env -S uv run --script
#MISE description="Add the initial keyper set on-chain"
#MISE depends=["gen-keyper-configs"]

import json
import os
from pathlib import Path

import utils


deployment_type = utils.resolve_deployment_type(os.environ.get("DEPLOYMENT_TYPE", ""))
data_dir = Path(os.environ["DATA_DIR"])
run_path = (
data_dir
/ "contracts"
/ "broadcast"
/ utils.DEPLOYMENT_SCRIPTS[deployment_type]
/ os.environ["ETHEREUM_CHAIN_ID"]
/ "run-latest.json"
)
deployment_run = json.loads(run_path.read_text())
keyper_set_manager = utils.get_created_contract_address(
deployment_run, "KeyperSetManager"
)

count_result = utils.run(
[
"docker",
"compose",
"run",
"--rm",
"--entrypoint",
"cast",
"contracts",
"call",
"--rpc-url",
"http://ethereum:8545",
keyper_set_manager,
"getNumKeyperSets()(uint64)",
],
capture_output=True,
)
if int(count_result.stdout.strip()) > 1: # 0 is the guard keyper set
print("Initial keyper set already added; skipping")
raise SystemExit(0)

utils.run(
[
"mise",
"run",
"add-keyper-set",
"--indices",
os.environ["INITIAL_KEYPER_SET_INDICES"],
"--threshold",
os.environ["INITIAL_THRESHOLD"],
]
)
Loading