Repository navigation
Expand file tree
/
Copy pathprogram-escrow-manifest.json
More file actions
349 lines (349 loc) · 14.6 KB
/
Copy pathprogram-escrow-manifest.json
File metadata and controls
349 lines (349 loc) · 14.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
{
"contract_name": "ProgramEscrowContract",
"version": {
"current": "1.1.0",
"schema": 1
},
"purpose": "Handles hackathon and program prize pools with batch payouts, dependency management, and circuit breaker protection.",
"entrypoints": {
"public": [
{
"name": "init_program",
"description": "Initialize a new program escrow instance.",
"parameters": [
{ "name": "program_id", "type": "String" },
{ "name": "authorized_payout_key", "type": "Address" },
{ "name": "token_address", "type": "Address" },
{ "name": "creator", "type": "Address" },
{ "name": "initial_liquidity", "type": "Option<i128>" },
{ "name": "reference_hash", "type": "Option<Bytes>" }
],
"authorization": "creator"
},
{
"name": "lock_program_funds",
"description": "Lock funds into the program escrow. Blocked when lock_paused is true.",
"parameters": [
{ "name": "amount", "type": "i128" }
],
"authorization": "none"
},
{
"name": "single_payout",
"description": "Execute a single payout to one winner. Blocked when circuit breaker is OPEN or release_paused is true.",
"parameters": [
{ "name": "recipient", "type": "Address" },
{ "name": "amount", "type": "i128" },
{ "name": "idempotency_key", "type": "Option<String>" }
],
"authorization": "authorized_payout_key"
},
{
"name": "batch_payout",
"description": "Execute batch payouts to multiple winners atomically (all-or-nothing). Blocked when circuit breaker is OPEN or release_paused is true.",
"parameters": [
{ "name": "recipients", "type": "Vec<Address>" },
{ "name": "amounts", "type": "Vec<i128>" },
{ "name": "idempotency_key", "type": "Option<String>" }
],
"authorization": "authorized_payout_key"
},
{
"name": "get_program_info",
"description": "Return current program state.",
"parameters": [],
"authorization": "none"
},
{
"name": "get_remaining_balance",
"description": "Return current remaining balance.",
"parameters": [],
"authorization": "none"
},
{
"name": "get_circuit_breaker_status",
"description": "Return a full snapshot of the circuit breaker state including is_open, failure_count, success_count, and configured thresholds.",
"parameters": [],
"authorization": "none"
},
{
"name": "get_cb_schema_version",
"description": "Return the upgrade-safe circuit-breaker storage schema version. Returns 0 on legacy deployments.",
"parameters": [],
"authorization": "none"
},
{
"name": "get_circuit_error_log",
"description": "Return the full circuit breaker error log (last N entries).",
"parameters": [],
"authorization": "none"
}
],
"admin": [
{
"name": "initialize_contract",
"description": "Initialize the contract with an admin address. Sets up role management schema for upgrade safety.",
"parameters": [
{ "name": "admin", "type": "Address" }
],
"authorization": "none (first call only)"
},
{
"name": "propose_admin",
"description": "Propose a new admin with deterministic behavior and explicit errors. Two-step rotation (step 1).",
"parameters": [
{ "name": "proposed_admin", "type": "Address" }
],
"authorization": "current admin",
"errors": [
{ "code": 1200, "message": "Admin rotation already in progress" },
{ "code": 1207, "message": "Invalid role proposal" },
{ "code": 1208, "message": "Role rotation not allowed" }
]
},
{
"name": "accept_admin",
"description": "Accept the proposed admin role. Two-step rotation (step 2).",
"parameters": [],
"authorization": "proposed admin",
"errors": [
{ "code": 1201, "message": "No admin rotation in progress" },
{ "code": 1202, "message": "Invalid admin rotation state" }
]
},
{
"name": "cancel_admin_rotation",
"description": "Cancel a pending admin rotation.",
"parameters": [],
"authorization": "current admin",
"errors": [
{ "code": 1201, "message": "No admin rotation in progress" }
]
},
{
"name": "set_circuit_admin",
"description": "Set or rotate the circuit breaker admin address.",
"parameters": [
{ "name": "new_admin", "type": "Address" },
{ "name": "caller", "type": "Option<Address>" }
],
"authorization": "existing circuit admin or contract admin"
},
{
"name": "emergency_open_circuit",
"description": "Immediately transition the circuit breaker to OPEN, blocking all payouts. Use when a security incident is detected.",
"parameters": [
{ "name": "admin", "type": "Address" }
],
"authorization": "circuit admin"
},
{
"name": "reset_circuit_breaker",
"description": "Reset the circuit breaker to CLOSED state, allowing payouts to resume.",
"parameters": [
{ "name": "caller", "type": "Address" }
],
"authorization": "circuit admin"
},
{
"name": "configure_circuit_breaker",
"description": "Configure circuit breaker thresholds: failure_threshold, success_threshold, and max_error_log entries.",
"parameters": [
{ "name": "caller", "type": "Address" },
{ "name": "failure_threshold", "type": "u32" },
{ "name": "success_threshold", "type": "u32" },
{ "name": "max_error_log", "type": "u32" }
],
"authorization": "circuit admin"
},
{
"name": "set_paused",
"description": "Set pause flags for lock, release, and refund operations independently.",
"parameters": [
{ "name": "lock", "type": "Option<bool>" },
{ "name": "release", "type": "Option<bool>" },
{ "name": "refund", "type": "Option<bool>" },
{ "name": "reason", "type": "Option<String>" }
],
"authorization": "admin"
},
{
"name": "propose_controller",
"description": "Propose a new controller (authorized_payout_key) for a program with deterministic behavior. Two-step rotation (step 1).",
"parameters": [
{ "name": "program_id", "type": "String" },
{ "name": "caller", "type": "Address" },
{ "name": "proposed_controller", "type": "Address" }
],
"authorization": "current controller or admin",
"errors": [
{ "code": 1203, "message": "Controller rotation already in progress" },
{ "code": 1207, "message": "Invalid role proposal" },
{ "code": 1208, "message": "Role rotation not allowed" }
]
},
{
"name": "accept_controller",
"description": "Accept the proposed controller role for a program. Two-step rotation (step 2).",
"parameters": [
{ "name": "program_id", "type": "String" }
],
"authorization": "proposed controller",
"errors": [
{ "code": 1204, "message": "No controller rotation in progress" },
{ "code": 1205, "message": "Invalid controller rotation state" }
]
},
{
"name": "cancel_controller_rotation",
"description": "Cancel a pending controller rotation for a program.",
"parameters": [
{ "name": "program_id", "type": "String" },
{ "name": "caller", "type": "Address" }
],
"authorization": "current controller or admin",
"errors": [
{ "code": 1204, "message": "No controller rotation in progress" }
]
},
{
"name": "set_program_spend_threshold",
"description": "Set per-program spend threshold. Any single payout or batch total exceeding this is rejected.",
"parameters": [
{ "name": "program_id", "type": "String" },
{ "name": "threshold_amount", "type": "i128" }
],
"authorization": "admin"
}
]
},
"behaviors": {
"role_separation": {
"description": "Deterministic role separation and controller rotation with two-step process, explicit errors, and upgrade-safe storage.",
"features": {
"two_step_rotation": "Both admin and controller roles use two-step rotation (propose + accept) for security",
"deterministic_behavior": "All role operations have deterministic outcomes with explicit error codes",
"upgrade_safe_storage": "Role management uses schema versioning for upgrade compatibility",
"replay_protection": "Nonces and timestamps prevent replay attacks",
"emergency_controls": "Role rotations can be blocked during emergencies or disputes",
"transition_periods": "Configurable maximum transition periods for role changes"
},
"error_codes": {
"1200": "Admin rotation already in progress",
"1201": "No admin rotation in progress",
"1202": "Invalid admin rotation state",
"1203": "Controller rotation already in progress",
"1204": "No controller rotation in progress",
"1205": "Invalid controller rotation state",
"1206": "Role transition period expired",
"1207": "Invalid role proposal",
"1208": "Role rotation not allowed"
}
},
"circuit_breaker": {
"description": "Three-state circuit breaker (Closed, Open, HalfOpen) guards all payout operations with deterministic, explicit errors and upgrade-safe storage.",
"states": ["Closed", "Open", "HalfOpen"],
"state_machine": {
"Closed": {
"initial_state": true,
"description": "Normal operation. Requests pass through after basic checks.",
"transitions": {
"to_Open": "Failure count >= failure_threshold",
"to_HalfOpen": "N/A from Closed"
}
},
"Open": {
"description": "Circuit open (protection active). All payout requests rejected immediately with ERR_CIRCUIT_OPEN without balance/threshold checks.",
"transitions": {
"to_HalfOpen": "admin calls reset_circuit_breaker() — moves Open -> HalfOpen for recovery attempt",
"to_Closed": "N/A from Open directly"
}
},
"HalfOpen": {
"description": "Admin has initiated recovery. Operations allowed but monitored. First failure re-opens; success_threshold consecutive successes close circuit.",
"transitions": {
"to_Open": "Any failure while in HalfOpen",
"to_Closed": "success_count >= success_threshold"
}
}
},
"deterministic_enforcement_order": [
"1. Reentrancy guard (acquire before any state read)",
"2. Idempotency key check (early exit if already processed)",
"3. Contract initialized check",
"4. Pause state check (lock_paused/release_paused/refund_paused)",
"5. Dispute guard check",
"6. CIRCUIT BREAKER CHECK (check_and_allow_with_thresholds before all business logic)",
"7. Authorization check",
"8. Input validation (amounts, recipient count, empty batch)",
"9. Spend threshold check",
"10. Balance check (sufficient funds)",
"11. Token transfer execution"
],
"explicit_errors": {
"ERR_CIRCUIT_OPEN": {
"code": 1001,
"message": "Circuit breaker is OPEN",
"when": "check_and_allow_with_thresholds returns ERR_CIRCUIT_OPEN",
"deterministic": true
},
"ERR_THRESHOLD_BREACHED": {
"code": 1000,
"message": "Operation rejected by circuit breaker",
"when": "Threshold monitoring detects breach; circuit auto-opens",
"deterministic": true
}
},
"upgrade_safety": {
"marker": "CircuitBreakerSchemaVersion (u32 in instance storage)",
"default_value": 0,
"behavior_on_legacy": "Returns 0 on deployments where marker was never written",
"behavior_on_new": "Set to 1 on init_program()",
"migration_path": "Version 0 -> 1 supported transparently; future versions trigger controlled failure"
}
},
"atomicity": "batch_payout uses all-or-nothing semantics. If any validation fails, no transfers occur and balance is unchanged.",
"idempotency": "Optional idempotency key on single_payout and batch_payout ensures deterministic behavior on retries.",
"reentrancy_protection": "All payout paths acquire a reentrancy guard before any state read."
},
"storage_keys": {
"CircuitBreakerSchemaVersion": "Upgrade-safe schema version marker for circuit breaker storage. Written on init.",
"ReentrancyGuard": "u32 flag: 1=NOT_ENTERED, 2=ENTERED.",
"PauseFlags": "PauseFlags struct with lock_paused, release_paused, refund_paused.",
"Admin": "Contract admin address.",
"ProgramData": "Legacy single-program storage key."
},
"security_notes": [
"Circuit breaker check runs before balance and threshold checks for deterministic, stable rejection messages.",
"emergency_open_circuit halts all payouts immediately without waiting for failure threshold.",
"Only the circuit admin can open or reset the circuit breaker.",
"Token economics (fee-on-transfer tokens) are out of scope per external_calls.rs.",
"All payout rejections emit audit events before panicking so the rejection is always visible on-chain."
],
"testing": {
"coverage_target": "95%",
"test_files": [
"contracts/program-escrow/src/test.rs"
],
"circuit_breaker_test_cases": [
"CB-01: Closed circuit allows payouts",
"CB-02: Open circuit blocks single_payout",
"CB-03: Open circuit blocks batch_payout",
"CB-04: Open circuit — no partial transfer",
"CB-05: Reset restores payouts",
"CB-06: Status reflects OPEN state",
"CB-07: Status reflects CLOSED after reset",
"CB-08: Only circuit admin can emergency open",
"CB-09: Only circuit admin can reset",
"CB-10: Schema version readable after init",
"CB-11: Open circuit — batch no partial transfer",
"CB-12: Circuit checked before balance",
"CB-13: Deterministic panic message",
"CB-14: Multiple payouts succeed after reset",
"CB-15: Error log readable",
"CB-16: Configure thresholds persists",
"CB-17: Open circuit blocks batch_release",
"CB-18: Emergency open emits audit event"
]
}
}