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
56 changes: 48 additions & 8 deletions stellar/PAUSE.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,67 @@
# Stellar Contract Pause Posture

## Pattern
Admin-only pause via `DataKey::Paused` in contract storage.
Upgrade authority (set at init) is the only address that can pause/unpause.
All state-mutating functions guard with `require_not_paused!`.
Admin-only pause via `DataKey::Paused` in contract instance storage.
Admin address is set at `init` time and is the only address that can pause/unpause.
All state-mutating functions guard with `require_not_paused()` which returns
the contract-specific `Paused` error.

`Paused` / `Unpaused` events are emitted (topic: `"paused"` / `"unpaused"`,
data: `(caller,)`).

## Per-Contract Decision

| Contract | Pausable? | Reason |
|--------------------|-----------|--------|
| stealth-announcer | No | Stateless event emitter — no storage, nothing to pause |
| stealth-registry | Yes | Stores stealth meta-addresses; pause prevents new registrations during incident |
| stealth-registry | No | Not implemented; non-custodial metadata writes, registrations are not guarded |
| stealth-sender | Yes | Moves tokens; pause prevents sends during incident |
| wraith-names | Yes | Name registry with ownership; pause prevents registrations/releases |
| wraith-names | Yes | Name registry with ownership; pause prevents registrations, updates, releases, and TTL extensions |

## Guarded Surface

### stealth-sender

Guarded by `require_not_paused`:
- `send` — token transfer + announcement
- `batch_send` — batch token transfers + announcements

NOT guarded (users must be able to exit during an incident):
- `withdraw_many` — batch asset exits

### wraith-names

Guarded by `require_not_paused`:
- `register` / `register_on_behalf`
- `update` / `update_on_behalf`
- `release` / `release_on_behalf`
- `extend_name_ttl`

NOT guarded (read-only lookups remain available):
- `resolve` — name → meta-address lookup
- `name_of` — reverse lookup (meta-address → name)

## Usage
```rust
// Admin initialises the pause capability
client.init(&admin); // wraith-names only; stealth-sender admin set in init()

// Pause
client.pause();
client.pause(&admin);

// Unpause
client.unpause();
// Unpause
client.unpause(&admin);

// Check
client.is_paused(); // returns bool
```

## Events

```rust
// Pause
env.events().publish(("paused",), (caller,));

// Unpause
env.events().publish(("unpaused",), (caller,));
```
3 changes: 2 additions & 1 deletion stellar/bench-crossover/src/crossover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ fn measure_individual(n: u32) -> Measured {
let sender_contract_id = env.register(StealthSenderContract, ());
let announcer_id = env.register(StealthAnnouncerContract, ());
let client = StealthSenderContractClient::new(&env, &sender_contract_id);
client.init(&announcer_id, &None, &None, &0);
let admin = Address::generate(&env);
client.init(&announcer_id, &None, &None, &0, &admin);
let (token, sender) = funded_token(&env);

env.cost_estimate().budget().reset_unlimited();
Expand Down
6 changes: 4 additions & 2 deletions stellar/bench/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ fn main() {
let sender_contract_id = env.register(StealthSenderContract, ());
let announcer_id = env.register(StealthAnnouncerContract, ());
let client = StealthSenderContractClient::new(env, &sender_contract_id);
client.init(&announcer_id, &None, &None, &0);
let admin = Address::generate(env);
client.init(&announcer_id, &None, &None, &0, &admin);
let (token, sender) = funded_token(env, asset == "xlm");
client.send(
&sender,
Expand All @@ -103,7 +104,8 @@ fn main() {
let sender_contract_id = env.register(StealthSenderContract, ());
let announcer_id = env.register(StealthAnnouncerContract, ());
let client = StealthSenderContractClient::new(env, &sender_contract_id);
client.init(&announcer_id, &None, &None, &0);
let admin = Address::generate(env);
client.init(&announcer_id, &None, &None, &0, &admin);
let (token, sender) = funded_token(env, true);
let mut addresses: SorobanVec<Address> = vec![env];
let mut keys: SorobanVec<BytesN<32>> = vec![env];
Expand Down
9 changes: 6 additions & 3 deletions stellar/integration-tests/tests/chaos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ fn sender_send_eth_through_chaos() {
let announcer_id = env.register(mock_announcer::MockAnnouncer, ());
let sender_id = env.register(StealthSenderContract, ());
let client = StealthSenderContractClient::new(&env, &sender_id);
client.init(&announcer_id, &None, &None, &0);
let admin = Address::generate(&env);
client.init(&announcer_id, &None, &None, &0, &admin);
let (token, sender_addr) = funded_token(&env);
let stealth = Address::generate(&env);
let epk = bytes32(&env, &[0xab; 32]);
Expand All @@ -209,7 +210,8 @@ fn sender_batch_send_through_chaos() {
let announcer_id = env.register(mock_announcer::MockAnnouncer, ());
let sender_id = env.register(StealthSenderContract, ());
let client = StealthSenderContractClient::new(&env, &sender_id);
client.init(&announcer_id, &None, &None, &0);
let admin = Address::generate(&env);
client.init(&announcer_id, &None, &None, &0, &admin);
let (token, sender_addr) = funded_token(&env);
let stealth1 = Address::generate(&env);
let stealth2 = Address::generate(&env);
Expand Down Expand Up @@ -382,7 +384,8 @@ fn sender_announcer_lifecycle_through_chaos() {
let announcer_id = env.register(StealthAnnouncerContract, ());
let sender_id = env.register(StealthSenderContract, ());
let client = StealthSenderContractClient::new(&env, &sender_id);
client.init(&announcer_id, &None, &None, &0);
let admin = Address::generate(&env);
client.init(&announcer_id, &None, &None, &0, &admin);
let (token, sender_addr) = funded_token(&env);
let stealth = Address::generate(&env);
let epk = bytes32(&env, &[0xff; 32]);
Expand Down
Loading
Loading