Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ the bottom of the screen redraws.
⏺ chain.validators(status=jailed)
⎿ no validator is jailed

⏺ No — none of primium-1's validators is jailed.
⏺ No — none of mychain-1's validators is jailed.
```

| Key / prefix | Does |
Expand Down
38 changes: 19 additions & 19 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ Every step is read-only unless it says otherwise.
## 0. Prerequisites

- Go 1.25+
- A running local network (this guide uses the `primium-evm` docker-compose
setup: containers `primium-validator0` … `primium-validator3`)
- A running local network (this guide uses an evmd docker-compose
setup: containers `validator0` … `validator3`)
- Optional, for the agent: a free LLM key (see [§3](#3-pick-a-free-llm))

Check the nodes are up and producing blocks:
Expand Down Expand Up @@ -46,34 +46,34 @@ cometcli init # detects docker containers, port bindings, and mounted hom
### Option B: one command per node

Each container maps its ports to different host ports. These match the
`primium-evm` compose file; check yours with `docker port primium-validator0`.
evmd compose file; check yours with `docker port validator0`.

| profile | container | comet | gRPC | REST | EVM | home (host mount) |
|---|---|---|---|---|---|---|
| primium-val0 | primium-validator0 | 26657 | 9090 | 1317 | 8545 | `~/.primium_1` |
| primium-val1 | primium-validator1 | 26667 | 9100 | 1327 | 8555 | `~/.primium_2` |
| primium-val2 | primium-validator2 | 26677 | 9110 | 1337 | 8565 | `~/.primium_3` |
| primium-val3 | primium-validator3 | 26687 | 9120 | 1347 | 8575 | `~/.primium_4` |
| val0 | validator0 | 26657 | 9090 | 1317 | 8545 | `~/.node_1` |
| val1 | validator1 | 26667 | 9100 | 1327 | 8555 | `~/.node_2` |
| val2 | validator2 | 26677 | 9110 | 1337 | 8565 | `~/.node_3` |
| val3 | validator3 | 26687 | 9120 | 1347 | 8575 | `~/.node_4` |

```bash
for i in 0 1 2 3; do
o=$((i*10))
cometcli profile add primium-val$i \
--chain-id primium-1 --evm-chain-id 123457 --bech32-prefix cosmos --fee-denom adex \
--role validator --binary evmd --home "$HOME/.primium_$((i+1))" \
cometcli profile add val$i \
--chain-id mychain-1 --evm-chain-id 123457 --bech32-prefix cosmos --fee-denom adex \
--role validator --binary evmd --home "$HOME/.node_$((i+1))" \
--comet tcp://127.0.0.1:$((26657+o)) --grpc 127.0.0.1:$((9090+o)) \
--lcd http://127.0.0.1:$((1317+o)) --evm http://127.0.0.1:$((8545+o)) \
--transport local --service docker --unit primium-validator$i
--transport local --service docker --unit validator$i
done
cometcli profile use primium-val0
cometcli profile use val0
```

To see signing and jail info without an ops key, add each validator's
operator address. You can list them with
`curl -s localhost:1317/cosmos/staking/v1beta1/validators`.

```bash
cometcli profile set primium-val0 --valoper cosmosvaloper1...
cometcli profile set val0 --valoper cosmosvaloper1...
```

### Smoke-test the tool layer
Expand All @@ -87,7 +87,7 @@ cometcli profile set primium-val0 --valoper cosmosvaloper1...
| `cometcli fleet status` | one row per profile, same height |
| `cometcli doctor` | checklist (see note below) |
| `cometcli sec exposure` | port audit from `docker port` |
| `cometcli --profile primium-val2 node peers` | any profile, without switching |
| `cometcli --profile val2 node peers` | any profile, without switching |

> **Expected findings on a dev network.** `doctor` and `sec exposure`
> report **6 critical exposures** per validator, because the compose file
Expand Down Expand Up @@ -119,7 +119,7 @@ Free-tier limits change; check the provider's console for current numbers.
```bash
export GROQ_API_KEY=gsk_...
for i in 0 1 2 3; do
cometcli profile set primium-val$i --agent-provider groq --agent-model openai/gpt-oss-120b
cometcli profile set val$i --agent-provider groq --agent-model openai/gpt-oss-120b
done
```

Expand All @@ -128,7 +128,7 @@ done
```bash
brew install ollama && ollama serve & # or the macOS app
ollama pull qwen3:8b # ~5 GB; tool-capable. qwen3:32b if you have 32 GB+ RAM
cometcli profile set primium-val0 --agent-provider ollama --agent-model qwen3:8b
cometcli profile set val0 --agent-provider ollama --agent-model qwen3:8b
```

The default base URL is `http://localhost:11434`. Small models make more
Expand All @@ -138,7 +138,7 @@ tool-calling mistakes, so expect rougher answers than Groq.

```bash
export COMETCLI_LLM_API_KEY=sk-or-... # from https://openrouter.ai/keys
cometcli profile set primium-val0 --agent-provider openai-compat \
cometcli profile set val0 --agent-provider openai-compat \
--agent-base-url https://openrouter.ai/api \
--agent-model meta-llama/llama-3.3-70b-instruct:free
```
Expand All @@ -161,7 +161,7 @@ stored in `config.yaml`.

```bash
cometcli ask --mode readonly "is my validator healthy? cite height and missed blocks"
cometcli ask --mode readonly --profile primium-val2 "compare peers and signing across the fleet"
cometcli ask --mode readonly --profile val2 "compare peers and signing across the fleet"
cometcli ask --mode readonly "why might the EVM height drift from the comet height?"
```

Expand Down Expand Up @@ -251,7 +251,7 @@ keyring. `keys` commands use the active profile's keyring backend, so set it
first:

```bash
cometcli profile set primium-val0 --signer ops --signer-backend test
cometcli profile set val0 --signer ops --signer-backend test
cometcli keys add --name ops # new key; fund it from a dev account
# or: cometcli keys add --name ops --recover # import a funded dev mnemonic
```
Expand Down
2 changes: 1 addition & 1 deletion docs/COMMANDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ Two signers; when both can sign, you pick one first, then approve the tx (the ap
signer:
key: ops # cometcli keyring (optional)
mode: "" # "" = ask when both work | local | container
container: primium-validator0 # default: service.unit for docker services
container: validator0 # default: service.unit for docker services
container_key: val0 # key name in the container's keyring
container_keyring: "" # test | file — detected when empty
container_home: /data/node0/evmd
Expand Down
6 changes: 3 additions & 3 deletions docs/EXTENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ Standing instructions loaded into every session's system prompt:
A line `@path/to/file.md` imports another file (key material is never imported).

```markdown
# primium node-setup
- Validators run in docker; containers are primium-validator0..3.
# node-setup
- Validators run in docker; containers are validator0..3.
- Never restart validator0 without asking — it's the genesis proposer.
- Upgrades follow upgrade/README.md.
@docs/ports.md
Expand All @@ -86,7 +86,7 @@ allowed-tools: bash(./upgrade/vote-upgrade.sh:*), read
---
Vote $2 on proposal $1.

Current proposals: !`docker exec primium-validator0 evmd q gov proposals -o json | jq -r '.proposals[] | "\(.id) \(.status)"'`
Current proposals: !`docker exec validator0 evmd q gov proposals -o json | jq -r '.proposals[] | "\(.id) \(.status)"'`
Network config: @run-genesis/network-config.env

Read upgrade/vote-upgrade.sh first, confirm the proposal is in its voting period,
Expand Down
4 changes: 2 additions & 2 deletions internal/agent/phase2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,13 @@ func TestGeneralModeAndScopeSwitch(t *testing.T) {
nodeTool := stubTool{name: "node.status", tier: toolkit.TierObserve, run: func(*toolkit.Context, toolkit.Args) (*toolkit.Result, error) { return &toolkit.Result{}, nil }}
a, _ := newShellAgent(t, prov, nodeTool)
prof := a.Ctx.Profile
prof.Role, prof.ChainID = "validator", "primium-1"
prof.Role, prof.ChainID = "validator", "mychain-1"
a.Ctx.Cfg = &config.Config{Profiles: map[string]*config.Profile{"testp": prof}}
a.SwitchScope(nil) // start in general mode
a.carry = ""
a.Run(context.Background(), "one")
r := prov.reqs[0]
if !strings.Contains(r.System, "working in the operator's terminal") || !strings.Contains(r.System, "testp (validator, primium-1)") {
if !strings.Contains(r.System, "working in the operator's terminal") || !strings.Contains(r.System, "testp (validator, mychain-1)") {
t.Fatalf("general prompt = %q", r.System)
}
for _, td := range r.Tools {
Expand Down
4 changes: 2 additions & 2 deletions internal/agent/phase4_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@ func TestCustomCommandExpandsAndGrantsTurnRules(t *testing.T) {
a, dir := newShellAgent(t, prov)
withExt(a, dir, nil)
a.ext.Settings.Trusted = true
os.WriteFile(filepath.Join(dir, "network-config.env"), []byte("CHAIN_ID=primium-1\n"), 0o644)
os.WriteFile(filepath.Join(dir, "network-config.env"), []byte("CHAIN_ID=mychain-1\n"), 0o644)
a.ext.Commands["vote"] = &settings.Command{Name: "vote", Scope: "project", AllowedTools: []string{"bash(./upgrade/vote-upgrade.sh:*)"},
Body: "Vote $1 on proposal $2.\nHost: !`uname -s`\nRefused: !`touch x`\nConfig: @network-config.env"}
res, err := RunCommand(a, a.Ctx, a.Reg, "/vote yes 7")
if err != nil {
t.Fatal(err)
}
for _, want := range []string{"Vote yes on proposal 7.", "Host: " + uname(t), "not run: only read-only", "CHAIN_ID=primium-1"} {
for _, want := range []string{"Vote yes on proposal 7.", "Host: " + uname(t), "not run: only read-only", "CHAIN_ID=mychain-1"} {
if !strings.Contains(res.Prompt, want) {
t.Errorf("prompt missing %q:\n%s", want, res.Prompt)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/agent/usenode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ func TestModelSwitchesToNodeItself(t *testing.T) {
}}
a := newTestAgent(t, prov, validators)
cfg := &config.Config{Profiles: map[string]*config.Profile{
"val01": {ChainID: "primium-1", Role: "validator"},
"val02": {ChainID: "primium-1", Role: "validator"},
"val01": {ChainID: "mychain-1", Role: "validator"},
"val02": {ChainID: "mychain-1", Role: "validator"},
}}
a.Ctx.Profile, a.Ctx.Cfg = nil, cfg // general mode
if a.Node() {
Expand Down
2 changes: 1 addition & 1 deletion internal/keys/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func TestSignVerify(t *testing.T) {
}

// eth_secp256k1 must sign keccak256(msg) — the chain rejects sha256 digests
// (verified live: signature-verification failures on primium-1).
// (verified live: signature-verification failures on mychain-1).
func TestSignEthSecp256k1_UsesKeccak(t *testing.T) {
priv, err := Derive(testMnemonic, AlgoEthSecp256k1, 60, 0, 0)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions internal/scenario/jail_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ func (s *scripted) Chat(_ context.Context, r *agent.Request) (*agent.Response, e
}

func TestJailRecoveryEndToEnd(t *testing.T) {
n := fakenode.Start(t, "primium-1")
n := fakenode.Start(t, "mychain-1")
chain := fakenode.StartComet(t)
p := n.Profile(t, "eth_secp256k1")
fakenode.WithComet(p, chain)
p.Service = config.Service{Type: "docker", Unit: "primium-validator0"}
p.Service = config.Service{Type: "docker", Unit: "validator0"}
p.Home = t.TempDir()
fakenode.InstallFakeDocker(t, "test", "")
fakenode.SetNodeLogs(t, jailLogs, "status=running restarts=2 oom_killed=false")
Expand Down
4 changes: 2 additions & 2 deletions internal/testutil/fakenode/comet.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func (c *Comet) result(method string, p map[string]any) (any, error) {
power = c.Power
}
return &coretypes.ResultStatus{
NodeInfo: p2p.DefaultNodeInfo{Network: "primium-1", Version: "0.39.3", Moniker: "validator-01"},
NodeInfo: p2p.DefaultNodeInfo{Network: "mychain-1", Version: "0.39.3", Moniker: "validator-01"},
SyncInfo: coretypes.SyncInfo{LatestBlockHeight: c.Height, LatestBlockTime: c.timeAt(c.Height), CatchingUp: c.CatchingUp},
ValidatorInfo: coretypes.ValidatorInfo{Address: addr, PubKey: cmted25519.PubKey(ConsPub()), VotingPower: power},
}, nil
Expand All @@ -164,7 +164,7 @@ func (c *Comet) result(method string, p map[string]any) (any, error) {
}
sigs = append(sigs, cs)
}
hdr := &types.Header{ChainID: "primium-1", Height: h, Time: c.timeAt(h)}
hdr := &types.Header{ChainID: "mychain-1", Height: h, Time: c.timeAt(h)}
return coretypes.NewResultCommit(hdr, &types.Commit{Height: h, Signatures: sigs}, true), nil
case "validators":
vals := []*types.Validator{types.NewValidator(cmted25519.GenPrivKeyFromSecret([]byte("other")).PubKey(), 100)}
Expand Down
2 changes: 1 addition & 1 deletion internal/tools/common/broadcast_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
func setup(t *testing.T, algo string) (*fakenode.Node, *toolkit.Context, *[]string) {
t.Helper()
tx.ConfirmTimeout, tx.ConfirmPoll = 300*time.Millisecond, 20*time.Millisecond
n := fakenode.Start(t, "primium-1")
n := fakenode.Start(t, "mychain-1")
p := n.Profile(t, algo)
var asked []string
c := &toolkit.Context{Context: context.Background(), Profile: p, AutoApproveBelow: toolkit.TierOnChain,
Expand Down
8 changes: 4 additions & 4 deletions internal/tools/common/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ func TestMain(m *testing.M) {
func containerSetup(t *testing.T, keyring string) (*fakenode.Node, *toolkit.Context, *[]string) {
t.Helper()
tx.ConfirmTimeout, tx.ConfirmPoll = 300*time.Millisecond, 20*time.Millisecond
n := fakenode.Start(t, "primium-1")
n := fakenode.Start(t, "mychain-1")
p := n.Profile(t, "eth_secp256k1")
p.Signer = config.Signer{Backend: "file", ContainerKey: "val"}
p.Service = config.Service{Type: "docker", Unit: "primium-validator0"}
p.Service = config.Service{Type: "docker", Unit: "validator0"}
fakenode.InstallFakeDocker(t, keyring, "correct horse")
var asked []string
c := &toolkit.Context{Context: context.Background(), Profile: p,
Expand All @@ -57,7 +57,7 @@ func TestContainerSigningTestKeyring(t *testing.T) {
if sims != 0 || bs[0].Fee.GasLimit != tx.DefaultGas || !strings.Contains((*asked)[0], "not simulated") {
t.Fatalf("sims=%d gas=%d", sims, bs[0].Fee.GasLimit)
}
if !strings.Contains((*asked)[0], "container primium-validator0 (key val, test keyring)") {
if !strings.Contains((*asked)[0], "container validator0 (key val, test keyring)") {
t.Fatalf("approval doesn't name the container signer: %s", (*asked)[0])
}
if !strings.Contains((*asked)[0], fakenode.TestAddress()) || res.Data["code"] != uint32(0) {
Expand Down Expand Up @@ -116,7 +116,7 @@ func TestSignerChoiceWhenBothAvailable(t *testing.T) {
if _, err := BroadcastMsgs(c, send("cosmos1dest"), "", nil, tx.Options{}); err != nil {
t.Fatal(err)
}
if len(offered) != 2 || !strings.Contains(offered[0], "cometcli keyring") || !strings.Contains(offered[1], "node container primium-validator0") {
if len(offered) != 2 || !strings.Contains(offered[0], "cometcli keyring") || !strings.Contains(offered[1], "node container validator0") {
t.Fatalf("options = %v", offered)
}
if _, _, sims := n.Snapshot(); sims != 0 {
Expand Down
4 changes: 2 additions & 2 deletions internal/tools/common/realdocker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ package common
// Validates container signing against a real evmd. Run with a throwaway
// container (no node process) holding test keys:
//
// docker run -d --rm --name cometcli-signtest --entrypoint sleep primium-evm:v0.7.2 900
// docker run -d --rm --name cometcli-signtest --entrypoint sleep evmd:latest 900
// docker exec cometcli-signtest evmd keys add t --keyring-backend test --home /tmp/h
// printf 'testpass123\ntestpass123\n' | docker exec -i cometcli-signtest evmd keys add tf --keyring-backend file --home /tmp/h
// go test -tags realdocker -run RealEvmd ./internal/tools/common/
Expand All @@ -30,7 +30,7 @@ func TestRealEvmdContainerSigning(t *testing.T) {
for _, tc := range []struct{ key, keyring string }{{"t", "test"}, {"tf", "file"}} {
t.Run(tc.keyring, func(t *testing.T) {
tx.ConfirmTimeout, tx.ConfirmPoll = time.Second, 20*time.Millisecond
n := fakenode.Start(t, "primium-1")
n := fakenode.Start(t, "mychain-1")
p := n.Profile(t, "eth_secp256k1")
p.Binary = "evmd"
p.Signer = config.Signer{Container: "cometcli-signtest", ContainerKey: tc.key, ContainerKeyring: tc.keyring, ContainerHome: "/tmp/h"}
Expand Down
12 changes: 6 additions & 6 deletions internal/tools/shell/classify_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ func TestClassify(t *testing.T) {
{"journalctl -u evmd --since '10 min ago' --no-pager", read},
{"systemctl status evmd", read},
{"docker ps --format '{{.Names}}'", read},
{"docker logs --tail 100 primium-validator0 2>&1 | grep -i panic", read},
{"docker logs --tail 100 validator0 2>&1 | grep -i panic", read},
{"docker compose -f docker-compose.yml ps", read},
{"docker exec primium-validator0 evmd status", read},
{"docker exec -i primium-validator0 evmd q staking validators -o json | jq '.validators | length'", read},
{"docker exec validator0 evmd status", read},
{"docker exec -i validator0 evmd q staking validators -o json | jq '.validators | length'", read},
{"curl -s localhost:26657/status | jq .result.sync_info", read},
{`curl -s -X POST -H 'content-type: application/json' --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' localhost:8545`, read},
{"evmd q bank balances cosmos1abc", read},
Expand All @@ -65,8 +65,8 @@ func TestClassify(t *testing.T) {
{"echo hi >> /etc/hosts", local},
{"sed -i 's/a/b/' config.toml", local},
{"docker compose up -d", local},
{"docker restart primium-validator0", local},
{"docker run --rm primium-evm:v0.7.2 evmd version", local},
{"docker restart validator0", local},
{"docker run --rm evmd:latest evmd version", local},
{"rm -rf ./tmp", local},
{"sudo apt install jq", local},
{"curl -o /tmp/x https://example.com", local},
Expand All @@ -84,7 +84,7 @@ func TestClassify(t *testing.T) {

// transactions — always ask
{"evmd tx staking unjail --from ops --yes", chain},
{"docker exec primium-validator0 evmd tx gov vote 3 yes --from val", chain},
{"docker exec validator0 evmd tx gov vote 3 yes --from val", chain},
{"docker run --rm img evmd tx bank send a b 1adex", chain},
{"cast send 0xabc 'f()' --private-key $K", chain},
{`curl -X POST --data '{"method":"eth_sendRawTransaction","params":["0x"]}' localhost:8545`, chain},
Expand Down
2 changes: 1 addition & 1 deletion internal/tools/triage/triage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func writeHome(t *testing.T, configToml, appToml, pvs string) string {

// setup builds a jailed, catching-up validator on the fake chain.
func setup(t *testing.T, binary, configToml, appToml string) (*toolkit.Context, *fakenode.Comet) {
n := fakenode.Start(t, "primium-1")
n := fakenode.Start(t, "mychain-1")
chain := fakenode.StartComet(t)
p := n.Profile(t, "eth_secp256k1")
fakenode.WithComet(p, chain)
Expand Down
2 changes: 1 addition & 1 deletion internal/tools/txtool/tx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestParseCoin(t *testing.T) {

func TestSendBuildsMsgSend(t *testing.T) {
tx.ConfirmTimeout, tx.ConfirmPoll = 300*time.Millisecond, 20*time.Millisecond
n := fakenode.Start(t, "primium-1")
n := fakenode.Start(t, "mychain-1")
c := &toolkit.Context{Context: context.Background(), Profile: n.Profile(t, "eth_secp256k1"),
Approver: func(*toolkit.Context, string, toolkit.Tier, map[string]any) (bool, error) { return true, nil }}
defer c.Close()
Expand Down
4 changes: 2 additions & 2 deletions internal/tools/val/incident_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ INF validator jailed module=x/staking validator=cosmosvalcons1someoneelse000

func TestJailCheckExplainsAndBlocks(t *testing.T) {
n, c, valoper, chain := setupChain(t)
c.Profile.Service = config.Service{Type: "docker", Unit: "primium-validator0"}
c.Profile.Service = config.Service{Type: "docker", Unit: "validator0"}
c.Profile.Home = t.TempDir()
c.SetHost(&host.Local{})
fakenode.InstallFakeDocker(t, "test", "")
Expand Down Expand Up @@ -142,7 +142,7 @@ func TestUnjailBlockedOnWrongConsensusKey(t *testing.T) {
func TestGatherFallsBackWhenNodeCantAnswer(t *testing.T) {
n, c, valoper, _ := setupChain(t)
jail(n, time.Now().Add(time.Minute))
loading := fakenode.Start(t, "primium-1") // up, but knows nothing yet
loading := fakenode.Start(t, "mychain-1") // up, but knows nothing yet
p := *c.Profile
p.Endpoints.GRPC, p.Endpoints.FallbackGRPC = loading.Addr, n.Addr
c2 := &toolkit.Context{Context: context.Background(), Profile: &p}
Expand Down
Loading
Loading