@layerzerolabs/common-utils-stellar-contracts 0.2.122
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +28 -0
- package/LICENSE +23 -0
- package/clippy.toml +7 -0
- package/package.json +43 -0
- package/rust-toolchain.toml +4 -0
- package/rustfmt.toml +15 -0
- package/src/auth.rs +48 -0
- package/src/buffer_reader.rs +194 -0
- package/src/buffer_writer.rs +138 -0
- package/src/bytes_ext.rs +18 -0
- package/src/errors.rs +82 -0
- package/src/lib.rs +20 -0
- package/src/multisig.rs +247 -0
- package/src/option_ext.rs +38 -0
- package/src/ownable.rs +227 -0
- package/src/rbac.rs +438 -0
- package/src/testing_utils.rs +180 -0
- package/src/tests/auth.rs +179 -0
- package/src/tests/buffer_reader.rs +969 -0
- package/src/tests/buffer_writer.rs +689 -0
- package/src/tests/bytes_ext.rs +160 -0
- package/src/tests/mod.rs +13 -0
- package/src/tests/multisig.rs +760 -0
- package/src/tests/option_ext.rs +18 -0
- package/src/tests/ownable.rs +822 -0
- package/src/tests/rbac.rs +559 -0
- package/src/tests/test_helper.rs +67 -0
- package/src/tests/testing_utils.rs +522 -0
- package/src/tests/ttl_configurable.rs +586 -0
- package/src/tests/ttl_extendable.rs +64 -0
- package/src/tests/upgradeable.rs +529 -0
- package/src/ttl_configurable.rs +169 -0
- package/src/ttl_extendable.rs +25 -0
- package/src/upgradeable.rs +103 -0
|
@@ -0,0 +1,822 @@
|
|
|
1
|
+
use crate::{
|
|
2
|
+
auth::Auth,
|
|
3
|
+
errors::OwnableError,
|
|
4
|
+
ownable::{
|
|
5
|
+
self, Ownable, OwnableInitializer, OwnableStorage, OwnershipRenounced, OwnershipTransferred,
|
|
6
|
+
OwnershipTransferring,
|
|
7
|
+
},
|
|
8
|
+
testing_utils::assert_eq_event,
|
|
9
|
+
tests::test_helper::mock_auth,
|
|
10
|
+
};
|
|
11
|
+
use soroban_sdk::{
|
|
12
|
+
contract, contractimpl,
|
|
13
|
+
testutils::{storage::Temporary as _, Address as _, Ledger as _, MockAuth, MockAuthInvoke},
|
|
14
|
+
Address, Env, IntoVal,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
#[contract]
|
|
18
|
+
pub struct Contract;
|
|
19
|
+
|
|
20
|
+
#[contractimpl]
|
|
21
|
+
impl Contract {
|
|
22
|
+
pub fn __constructor(env: &Env, owner: &Address) {
|
|
23
|
+
Self::init_owner(env, owner);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn enforce_and_return_owner(env: &Env) -> Address {
|
|
27
|
+
ownable::enforce_owner_auth::<Self>(env)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
pub fn remove_owner(env: &Env) {
|
|
31
|
+
OwnableStorage::remove_owner(env);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
pub fn reinit_owner(env: &Env, owner: &Address) {
|
|
35
|
+
Self::init_owner(env, owner);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Auth implementation for the test contract - uses stored owner as authorizer.
|
|
40
|
+
#[contractimpl]
|
|
41
|
+
impl Auth for Contract {
|
|
42
|
+
fn authorizer(env: &Env) -> Option<Address> {
|
|
43
|
+
<Self as Ownable>::owner(env)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#[contractimpl(contracttrait)]
|
|
48
|
+
impl Ownable for Contract {}
|
|
49
|
+
impl OwnableInitializer for Contract {}
|
|
50
|
+
|
|
51
|
+
// ============================================
|
|
52
|
+
// auth: Owner authentication tests
|
|
53
|
+
// ============================================
|
|
54
|
+
|
|
55
|
+
#[test]
|
|
56
|
+
fn auth_owner_can_call() {
|
|
57
|
+
let env = Env::default();
|
|
58
|
+
let owner = Address::generate(&env);
|
|
59
|
+
|
|
60
|
+
let contract = env.register(Contract, (&owner,));
|
|
61
|
+
let client = ContractClient::new(&env, &contract);
|
|
62
|
+
|
|
63
|
+
mock_auth(&env, &contract, &owner, "enforce_and_return_owner", ());
|
|
64
|
+
client.enforce_and_return_owner();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#[test]
|
|
68
|
+
#[should_panic(expected = "Error(Auth, InvalidAction)")]
|
|
69
|
+
fn auth_non_owner_cannot_call() {
|
|
70
|
+
let env = Env::default();
|
|
71
|
+
let owner = Address::generate(&env);
|
|
72
|
+
let contract = env.register(Contract, (&owner,));
|
|
73
|
+
let client = ContractClient::new(&env, &contract);
|
|
74
|
+
|
|
75
|
+
// Try to call without auth should fail
|
|
76
|
+
client.enforce_and_return_owner();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ============================================
|
|
80
|
+
// transfer: Single-step transfer ownership tests
|
|
81
|
+
// ============================================
|
|
82
|
+
|
|
83
|
+
#[test]
|
|
84
|
+
fn transfer_ownership_with_event() {
|
|
85
|
+
let env = Env::default();
|
|
86
|
+
let owner = Address::generate(&env);
|
|
87
|
+
let new_owner = Address::generate(&env);
|
|
88
|
+
|
|
89
|
+
let contract = env.register(Contract, (&owner,));
|
|
90
|
+
let client = ContractClient::new(&env, &contract);
|
|
91
|
+
|
|
92
|
+
assert_eq!(client.owner(), Some(owner.clone()));
|
|
93
|
+
|
|
94
|
+
mock_auth(&env, &contract, &owner, "transfer_ownership", (&new_owner,));
|
|
95
|
+
client.transfer_ownership(&new_owner);
|
|
96
|
+
|
|
97
|
+
assert_eq_event(&env, &contract, OwnershipTransferred { old_owner: owner, new_owner: new_owner.clone() });
|
|
98
|
+
assert_eq!(client.owner(), Some(new_owner));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
#[test]
|
|
102
|
+
fn transfer_ownership_to_same_owner() {
|
|
103
|
+
let env = Env::default();
|
|
104
|
+
let owner = Address::generate(&env);
|
|
105
|
+
|
|
106
|
+
let contract = env.register(Contract, (&owner,));
|
|
107
|
+
let client = ContractClient::new(&env, &contract);
|
|
108
|
+
|
|
109
|
+
assert_eq!(client.owner(), Some(owner.clone()));
|
|
110
|
+
|
|
111
|
+
mock_auth(&env, &contract, &owner, "transfer_ownership", (&owner,));
|
|
112
|
+
client.transfer_ownership(&owner);
|
|
113
|
+
|
|
114
|
+
// Should still emit event and update (even if same owner)
|
|
115
|
+
assert_eq_event(&env, &contract, OwnershipTransferred { old_owner: owner.clone(), new_owner: owner.clone() });
|
|
116
|
+
assert_eq!(client.owner(), Some(owner));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#[test]
|
|
120
|
+
fn transfer_after_renounce_fails() {
|
|
121
|
+
let env = Env::default();
|
|
122
|
+
let owner = Address::generate(&env);
|
|
123
|
+
let new_owner = Address::generate(&env);
|
|
124
|
+
|
|
125
|
+
let contract = env.register(Contract, (&owner,));
|
|
126
|
+
let client = ContractClient::new(&env, &contract);
|
|
127
|
+
|
|
128
|
+
// Renounce ownership
|
|
129
|
+
mock_auth(&env, &contract, &owner, "renounce_ownership", ());
|
|
130
|
+
client.renounce_ownership();
|
|
131
|
+
|
|
132
|
+
// Try to transfer after renounce - should fail with AuthorizerNotFound
|
|
133
|
+
mock_auth(&env, &contract, &owner, "transfer_ownership", (&new_owner,));
|
|
134
|
+
let result = client.try_transfer_ownership(&new_owner);
|
|
135
|
+
assert_eq!(result.err().unwrap().ok().unwrap(), OwnableError::OwnerNotSet.into());
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#[test]
|
|
139
|
+
#[should_panic(expected = "Error(Auth, InvalidAction)")]
|
|
140
|
+
fn transfer_ownership_non_owner_fails() {
|
|
141
|
+
let env = Env::default();
|
|
142
|
+
let owner = Address::generate(&env);
|
|
143
|
+
let non_owner = Address::generate(&env);
|
|
144
|
+
|
|
145
|
+
let contract = env.register(Contract, (&owner,));
|
|
146
|
+
let client = ContractClient::new(&env, &contract);
|
|
147
|
+
|
|
148
|
+
// Non-owner tries to transfer with their own auth - should fail
|
|
149
|
+
mock_auth(&env, &contract, &non_owner, "transfer_ownership", (&non_owner,));
|
|
150
|
+
client.transfer_ownership(&non_owner);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#[test]
|
|
154
|
+
#[should_panic(expected = "Error(Contract, #1036)")] // TransferInProgress
|
|
155
|
+
fn transfer_ownership_blocked_during_2step() {
|
|
156
|
+
let env = Env::default();
|
|
157
|
+
let owner = Address::generate(&env);
|
|
158
|
+
let new_owner = Address::generate(&env);
|
|
159
|
+
let another_owner = Address::generate(&env);
|
|
160
|
+
|
|
161
|
+
let contract = env.register(Contract, (&owner,));
|
|
162
|
+
let client = ContractClient::new(&env, &contract);
|
|
163
|
+
|
|
164
|
+
let ttl = 1000u32;
|
|
165
|
+
|
|
166
|
+
// Initiate 2-step transfer
|
|
167
|
+
env.mock_auths(&[MockAuth {
|
|
168
|
+
address: &owner,
|
|
169
|
+
invoke: &MockAuthInvoke {
|
|
170
|
+
contract: &contract,
|
|
171
|
+
args: (&new_owner, ttl).into_val(&env),
|
|
172
|
+
fn_name: "begin_ownership_transfer",
|
|
173
|
+
sub_invokes: &[],
|
|
174
|
+
},
|
|
175
|
+
}]);
|
|
176
|
+
client.begin_ownership_transfer(&new_owner, &ttl);
|
|
177
|
+
|
|
178
|
+
// Try single-step transfer - should fail
|
|
179
|
+
env.mock_auths(&[MockAuth {
|
|
180
|
+
address: &owner,
|
|
181
|
+
invoke: &MockAuthInvoke {
|
|
182
|
+
contract: &contract,
|
|
183
|
+
args: (&another_owner,).into_val(&env),
|
|
184
|
+
fn_name: "transfer_ownership",
|
|
185
|
+
sub_invokes: &[],
|
|
186
|
+
},
|
|
187
|
+
}]);
|
|
188
|
+
client.transfer_ownership(&another_owner);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ============================================
|
|
192
|
+
// transfer_2step: Two-step transfer ownership tests
|
|
193
|
+
// ============================================
|
|
194
|
+
|
|
195
|
+
#[test]
|
|
196
|
+
fn begin_ownership_transfer_initiate_and_accept() {
|
|
197
|
+
let env = Env::default();
|
|
198
|
+
let owner = Address::generate(&env);
|
|
199
|
+
let new_owner = Address::generate(&env);
|
|
200
|
+
|
|
201
|
+
let contract = env.register(Contract, (&owner,));
|
|
202
|
+
let client = ContractClient::new(&env, &contract);
|
|
203
|
+
|
|
204
|
+
let ttl = 1000u32;
|
|
205
|
+
|
|
206
|
+
assert_eq!(client.owner(), Some(owner.clone()));
|
|
207
|
+
assert_eq!(client.pending_owner(), None);
|
|
208
|
+
|
|
209
|
+
// Step 1: Initiate transfer
|
|
210
|
+
env.mock_auths(&[MockAuth {
|
|
211
|
+
address: &owner,
|
|
212
|
+
invoke: &MockAuthInvoke {
|
|
213
|
+
contract: &contract,
|
|
214
|
+
args: (&new_owner, ttl).into_val(&env),
|
|
215
|
+
fn_name: "begin_ownership_transfer",
|
|
216
|
+
sub_invokes: &[],
|
|
217
|
+
},
|
|
218
|
+
}]);
|
|
219
|
+
client.begin_ownership_transfer(&new_owner, &ttl);
|
|
220
|
+
|
|
221
|
+
assert_eq_event(
|
|
222
|
+
&env,
|
|
223
|
+
&contract,
|
|
224
|
+
OwnershipTransferring { old_owner: owner.clone(), new_owner: new_owner.clone(), ttl },
|
|
225
|
+
);
|
|
226
|
+
assert_eq!(client.owner(), Some(owner.clone())); // Still old owner
|
|
227
|
+
assert_eq!(client.pending_owner(), Some(new_owner.clone()));
|
|
228
|
+
|
|
229
|
+
// Step 2: Accept transfer
|
|
230
|
+
env.mock_auths(&[MockAuth {
|
|
231
|
+
address: &new_owner,
|
|
232
|
+
invoke: &MockAuthInvoke {
|
|
233
|
+
contract: &contract,
|
|
234
|
+
args: ().into_val(&env),
|
|
235
|
+
fn_name: "accept_ownership",
|
|
236
|
+
sub_invokes: &[],
|
|
237
|
+
},
|
|
238
|
+
}]);
|
|
239
|
+
client.accept_ownership();
|
|
240
|
+
|
|
241
|
+
assert_eq_event(&env, &contract, OwnershipTransferred { old_owner: owner, new_owner: new_owner.clone() });
|
|
242
|
+
assert_eq!(client.owner(), Some(new_owner)); // Now new owner
|
|
243
|
+
assert_eq!(client.pending_owner(), None);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
#[test]
|
|
247
|
+
fn pending_owner_expires_after_ttl() {
|
|
248
|
+
let env = Env::default();
|
|
249
|
+
env.ledger().set_sequence_number(1_000);
|
|
250
|
+
|
|
251
|
+
let owner = Address::generate(&env);
|
|
252
|
+
let new_owner = Address::generate(&env);
|
|
253
|
+
|
|
254
|
+
let contract = env.register(Contract, (&owner,));
|
|
255
|
+
let client = ContractClient::new(&env, &contract);
|
|
256
|
+
|
|
257
|
+
let ttl = 10u32;
|
|
258
|
+
|
|
259
|
+
// Initiate transfer
|
|
260
|
+
env.mock_auths(&[MockAuth {
|
|
261
|
+
address: &owner,
|
|
262
|
+
invoke: &MockAuthInvoke {
|
|
263
|
+
contract: &contract,
|
|
264
|
+
args: (&new_owner, ttl).into_val(&env),
|
|
265
|
+
fn_name: "begin_ownership_transfer",
|
|
266
|
+
sub_invokes: &[],
|
|
267
|
+
},
|
|
268
|
+
}]);
|
|
269
|
+
client.begin_ownership_transfer(&new_owner, &ttl);
|
|
270
|
+
assert_eq!(client.pending_owner(), Some(new_owner.clone()));
|
|
271
|
+
|
|
272
|
+
// Advance ledger beyond the actual storage TTL window; temporary pending owner entry should expire.
|
|
273
|
+
// Note: `extend_ttl` cannot reduce TTL, so the effective TTL can be >= the requested `ttl`.
|
|
274
|
+
let (ttl_before, seq_before) = env.as_contract(&contract, || {
|
|
275
|
+
(env.storage().temporary().get_ttl(&OwnableStorage::PendingOwner), env.ledger().sequence())
|
|
276
|
+
});
|
|
277
|
+
assert!(ttl_before >= ttl, "pending owner TTL must be at least requested ttl");
|
|
278
|
+
|
|
279
|
+
let live_until = seq_before + ttl_before;
|
|
280
|
+
env.ledger().set_sequence_number(live_until + 1);
|
|
281
|
+
|
|
282
|
+
assert_eq!(client.pending_owner(), None);
|
|
283
|
+
|
|
284
|
+
// Accept should now fail with NoPendingTransfer (expired).
|
|
285
|
+
let res = client.try_accept_ownership();
|
|
286
|
+
assert_eq!(res.err().unwrap().ok().unwrap(), OwnableError::NoPendingTransfer.into());
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#[test]
|
|
290
|
+
fn begin_ownership_transfer_cancel() {
|
|
291
|
+
let env = Env::default();
|
|
292
|
+
let owner = Address::generate(&env);
|
|
293
|
+
let new_owner = Address::generate(&env);
|
|
294
|
+
|
|
295
|
+
let contract = env.register(Contract, (&owner,));
|
|
296
|
+
let client = ContractClient::new(&env, &contract);
|
|
297
|
+
|
|
298
|
+
let ttl = 1000u32;
|
|
299
|
+
|
|
300
|
+
// Initiate transfer
|
|
301
|
+
env.mock_auths(&[MockAuth {
|
|
302
|
+
address: &owner,
|
|
303
|
+
invoke: &MockAuthInvoke {
|
|
304
|
+
contract: &contract,
|
|
305
|
+
args: (&new_owner, ttl).into_val(&env),
|
|
306
|
+
fn_name: "begin_ownership_transfer",
|
|
307
|
+
sub_invokes: &[],
|
|
308
|
+
},
|
|
309
|
+
}]);
|
|
310
|
+
client.begin_ownership_transfer(&new_owner, &ttl);
|
|
311
|
+
|
|
312
|
+
assert_eq!(client.pending_owner(), Some(new_owner.clone()));
|
|
313
|
+
|
|
314
|
+
// Cancel transfer (ttl = 0)
|
|
315
|
+
env.mock_auths(&[MockAuth {
|
|
316
|
+
address: &owner,
|
|
317
|
+
invoke: &MockAuthInvoke {
|
|
318
|
+
contract: &contract,
|
|
319
|
+
args: (&new_owner, 0u32).into_val(&env),
|
|
320
|
+
fn_name: "begin_ownership_transfer",
|
|
321
|
+
sub_invokes: &[],
|
|
322
|
+
},
|
|
323
|
+
}]);
|
|
324
|
+
client.begin_ownership_transfer(&new_owner, &0u32);
|
|
325
|
+
|
|
326
|
+
// No event emitted on cancel (matching OpenZeppelin behavior)
|
|
327
|
+
assert_eq!(client.owner(), Some(owner)); // Still old owner
|
|
328
|
+
assert_eq!(client.pending_owner(), None);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
#[test]
|
|
332
|
+
fn begin_ownership_transfer_with_max_ttl() {
|
|
333
|
+
let env = Env::default();
|
|
334
|
+
let owner = Address::generate(&env);
|
|
335
|
+
let new_owner = Address::generate(&env);
|
|
336
|
+
|
|
337
|
+
let contract = env.register(Contract, (&owner,));
|
|
338
|
+
let client = ContractClient::new(&env, &contract);
|
|
339
|
+
|
|
340
|
+
// Use max TTL
|
|
341
|
+
let max_ttl = env.storage().max_ttl();
|
|
342
|
+
// Initiate transfer with max TTL
|
|
343
|
+
env.mock_auths(&[MockAuth {
|
|
344
|
+
address: &owner,
|
|
345
|
+
invoke: &MockAuthInvoke {
|
|
346
|
+
contract: &contract,
|
|
347
|
+
args: (&new_owner, max_ttl).into_val(&env),
|
|
348
|
+
fn_name: "begin_ownership_transfer",
|
|
349
|
+
sub_invokes: &[],
|
|
350
|
+
},
|
|
351
|
+
}]);
|
|
352
|
+
client.begin_ownership_transfer(&new_owner, &max_ttl);
|
|
353
|
+
|
|
354
|
+
assert_eq!(client.pending_owner(), Some(new_owner.clone()));
|
|
355
|
+
|
|
356
|
+
// Get the actual TTL of the pending_owner entry
|
|
357
|
+
let actual_ttl = env.as_contract(&contract, || env.storage().temporary().get_ttl(&OwnableStorage::PendingOwner));
|
|
358
|
+
|
|
359
|
+
// The actual TTL should be at least the requested TTL
|
|
360
|
+
// To see the value, temporarily use: assert_eq!(actual_ttl, 0);
|
|
361
|
+
assert!(actual_ttl == max_ttl, "TTL should be max_ttl");
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
#[test]
|
|
365
|
+
#[should_panic(expected = "Error(Contract, #1033)")] // NoPendingTransfer
|
|
366
|
+
fn begin_ownership_transfer_cancel_no_pending_fails() {
|
|
367
|
+
let env = Env::default();
|
|
368
|
+
let owner = Address::generate(&env);
|
|
369
|
+
let new_owner = Address::generate(&env);
|
|
370
|
+
|
|
371
|
+
let contract = env.register(Contract, (&owner,));
|
|
372
|
+
let client = ContractClient::new(&env, &contract);
|
|
373
|
+
|
|
374
|
+
// Try to cancel when no transfer is pending
|
|
375
|
+
env.mock_auths(&[MockAuth {
|
|
376
|
+
address: &owner,
|
|
377
|
+
invoke: &MockAuthInvoke {
|
|
378
|
+
contract: &contract,
|
|
379
|
+
args: (&new_owner, 0u32).into_val(&env),
|
|
380
|
+
fn_name: "begin_ownership_transfer",
|
|
381
|
+
sub_invokes: &[],
|
|
382
|
+
},
|
|
383
|
+
}]);
|
|
384
|
+
client.begin_ownership_transfer(&new_owner, &0u32);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
#[test]
|
|
388
|
+
#[should_panic(expected = "Error(Contract, #1031)")] // InvalidPendingOwner
|
|
389
|
+
fn begin_ownership_transfer_cancel_wrong_address_fails() {
|
|
390
|
+
let env = Env::default();
|
|
391
|
+
let owner = Address::generate(&env);
|
|
392
|
+
let new_owner = Address::generate(&env);
|
|
393
|
+
let wrong_owner = Address::generate(&env);
|
|
394
|
+
|
|
395
|
+
let contract = env.register(Contract, (&owner,));
|
|
396
|
+
let client = ContractClient::new(&env, &contract);
|
|
397
|
+
|
|
398
|
+
let ttl = 1000u32;
|
|
399
|
+
|
|
400
|
+
// Initiate transfer
|
|
401
|
+
env.mock_auths(&[MockAuth {
|
|
402
|
+
address: &owner,
|
|
403
|
+
invoke: &MockAuthInvoke {
|
|
404
|
+
contract: &contract,
|
|
405
|
+
args: (&new_owner, ttl).into_val(&env),
|
|
406
|
+
fn_name: "begin_ownership_transfer",
|
|
407
|
+
sub_invokes: &[],
|
|
408
|
+
},
|
|
409
|
+
}]);
|
|
410
|
+
client.begin_ownership_transfer(&new_owner, &ttl);
|
|
411
|
+
|
|
412
|
+
// Try to cancel with wrong address
|
|
413
|
+
env.mock_auths(&[MockAuth {
|
|
414
|
+
address: &owner,
|
|
415
|
+
invoke: &MockAuthInvoke {
|
|
416
|
+
contract: &contract,
|
|
417
|
+
args: (&wrong_owner, 0u32).into_val(&env),
|
|
418
|
+
fn_name: "begin_ownership_transfer",
|
|
419
|
+
sub_invokes: &[],
|
|
420
|
+
},
|
|
421
|
+
}]);
|
|
422
|
+
client.begin_ownership_transfer(&wrong_owner, &0u32);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
#[test]
|
|
426
|
+
#[should_panic(expected = "Error(Contract, #1032)")] // InvalidTtl
|
|
427
|
+
fn begin_ownership_transfer_invalid_ttl_exceeds_max_fails() {
|
|
428
|
+
let env = Env::default();
|
|
429
|
+
let owner = Address::generate(&env);
|
|
430
|
+
let new_owner = Address::generate(&env);
|
|
431
|
+
|
|
432
|
+
let contract = env.register(Contract, (&owner,));
|
|
433
|
+
let client = ContractClient::new(&env, &contract);
|
|
434
|
+
|
|
435
|
+
// TTL that would exceed max_live_until_ledger
|
|
436
|
+
let ttl = env.storage().max_ttl() + 1;
|
|
437
|
+
|
|
438
|
+
env.mock_auths(&[MockAuth {
|
|
439
|
+
address: &owner,
|
|
440
|
+
invoke: &MockAuthInvoke {
|
|
441
|
+
contract: &contract,
|
|
442
|
+
args: (&new_owner, ttl).into_val(&env),
|
|
443
|
+
fn_name: "begin_ownership_transfer",
|
|
444
|
+
sub_invokes: &[],
|
|
445
|
+
},
|
|
446
|
+
}]);
|
|
447
|
+
|
|
448
|
+
client.begin_ownership_transfer(&new_owner, &ttl);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
#[test]
|
|
452
|
+
#[should_panic(expected = "Error(Contract, #1033)")] // NoPendingTransfer
|
|
453
|
+
fn accept_ownership_no_pending_fails() {
|
|
454
|
+
let env = Env::default();
|
|
455
|
+
let owner = Address::generate(&env);
|
|
456
|
+
let new_owner = Address::generate(&env);
|
|
457
|
+
|
|
458
|
+
let contract = env.register(Contract, (&owner,));
|
|
459
|
+
let client = ContractClient::new(&env, &contract);
|
|
460
|
+
|
|
461
|
+
// Try to accept when no transfer is pending
|
|
462
|
+
env.mock_auths(&[MockAuth {
|
|
463
|
+
address: &new_owner,
|
|
464
|
+
invoke: &MockAuthInvoke {
|
|
465
|
+
contract: &contract,
|
|
466
|
+
args: ().into_val(&env),
|
|
467
|
+
fn_name: "accept_ownership",
|
|
468
|
+
sub_invokes: &[],
|
|
469
|
+
},
|
|
470
|
+
}]);
|
|
471
|
+
client.accept_ownership();
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
#[test]
|
|
475
|
+
#[should_panic(expected = "Error(Auth, InvalidAction)")]
|
|
476
|
+
fn accept_ownership_wrong_address_fails() {
|
|
477
|
+
let env = Env::default();
|
|
478
|
+
let owner = Address::generate(&env);
|
|
479
|
+
let new_owner = Address::generate(&env);
|
|
480
|
+
let wrong_owner = Address::generate(&env);
|
|
481
|
+
|
|
482
|
+
let contract = env.register(Contract, (&owner,));
|
|
483
|
+
let client = ContractClient::new(&env, &contract);
|
|
484
|
+
|
|
485
|
+
let ttl = 1000u32;
|
|
486
|
+
|
|
487
|
+
// Initiate transfer
|
|
488
|
+
env.mock_auths(&[MockAuth {
|
|
489
|
+
address: &owner,
|
|
490
|
+
invoke: &MockAuthInvoke {
|
|
491
|
+
contract: &contract,
|
|
492
|
+
args: (&new_owner, ttl).into_val(&env),
|
|
493
|
+
fn_name: "begin_ownership_transfer",
|
|
494
|
+
sub_invokes: &[],
|
|
495
|
+
},
|
|
496
|
+
}]);
|
|
497
|
+
client.begin_ownership_transfer(&new_owner, &ttl);
|
|
498
|
+
|
|
499
|
+
// Try to accept with wrong address
|
|
500
|
+
env.mock_auths(&[MockAuth {
|
|
501
|
+
address: &wrong_owner,
|
|
502
|
+
invoke: &MockAuthInvoke {
|
|
503
|
+
contract: &contract,
|
|
504
|
+
args: ().into_val(&env),
|
|
505
|
+
fn_name: "accept_ownership",
|
|
506
|
+
sub_invokes: &[],
|
|
507
|
+
},
|
|
508
|
+
}]);
|
|
509
|
+
client.accept_ownership();
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
#[test]
|
|
513
|
+
fn begin_ownership_transfer_override_pending() {
|
|
514
|
+
let env = Env::default();
|
|
515
|
+
let owner = Address::generate(&env);
|
|
516
|
+
let new_owner1 = Address::generate(&env);
|
|
517
|
+
let new_owner2 = Address::generate(&env);
|
|
518
|
+
|
|
519
|
+
let contract = env.register(Contract, (&owner,));
|
|
520
|
+
let client = ContractClient::new(&env, &contract);
|
|
521
|
+
|
|
522
|
+
let ttl = 1000u32;
|
|
523
|
+
|
|
524
|
+
// Initiate first transfer
|
|
525
|
+
env.mock_auths(&[MockAuth {
|
|
526
|
+
address: &owner,
|
|
527
|
+
invoke: &MockAuthInvoke {
|
|
528
|
+
contract: &contract,
|
|
529
|
+
args: (&new_owner1, ttl).into_val(&env),
|
|
530
|
+
fn_name: "begin_ownership_transfer",
|
|
531
|
+
sub_invokes: &[],
|
|
532
|
+
},
|
|
533
|
+
}]);
|
|
534
|
+
client.begin_ownership_transfer(&new_owner1, &ttl);
|
|
535
|
+
|
|
536
|
+
assert_eq!(client.pending_owner(), Some(new_owner1.clone()));
|
|
537
|
+
|
|
538
|
+
// Override with second transfer (allowed - replaces pending)
|
|
539
|
+
env.mock_auths(&[MockAuth {
|
|
540
|
+
address: &owner,
|
|
541
|
+
invoke: &MockAuthInvoke {
|
|
542
|
+
contract: &contract,
|
|
543
|
+
args: (&new_owner2, ttl).into_val(&env),
|
|
544
|
+
fn_name: "begin_ownership_transfer",
|
|
545
|
+
sub_invokes: &[],
|
|
546
|
+
},
|
|
547
|
+
}]);
|
|
548
|
+
client.begin_ownership_transfer(&new_owner2, &ttl);
|
|
549
|
+
|
|
550
|
+
assert_eq!(client.pending_owner(), Some(new_owner2.clone()));
|
|
551
|
+
|
|
552
|
+
// new_owner2 can accept
|
|
553
|
+
env.mock_auths(&[MockAuth {
|
|
554
|
+
address: &new_owner2,
|
|
555
|
+
invoke: &MockAuthInvoke {
|
|
556
|
+
contract: &contract,
|
|
557
|
+
args: ().into_val(&env),
|
|
558
|
+
fn_name: "accept_ownership",
|
|
559
|
+
sub_invokes: &[],
|
|
560
|
+
},
|
|
561
|
+
}]);
|
|
562
|
+
client.accept_ownership();
|
|
563
|
+
|
|
564
|
+
assert_eq!(client.owner(), Some(new_owner2));
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// ============================================
|
|
568
|
+
// renounce: Renounce ownership tests
|
|
569
|
+
// ============================================
|
|
570
|
+
|
|
571
|
+
#[test]
|
|
572
|
+
fn renounce_ownership_with_event() {
|
|
573
|
+
let env = Env::default();
|
|
574
|
+
let owner = Address::generate(&env);
|
|
575
|
+
|
|
576
|
+
let contract = env.register(Contract, (&owner,));
|
|
577
|
+
let client = ContractClient::new(&env, &contract);
|
|
578
|
+
|
|
579
|
+
assert_eq!(client.owner(), Some(owner.clone()));
|
|
580
|
+
|
|
581
|
+
mock_auth(&env, &contract, &owner, "renounce_ownership", ());
|
|
582
|
+
client.renounce_ownership();
|
|
583
|
+
|
|
584
|
+
assert_eq_event(&env, &contract, OwnershipRenounced { old_owner: owner });
|
|
585
|
+
assert_eq!(client.owner(), None);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
#[test]
|
|
589
|
+
fn renounce_after_renounce_fails() {
|
|
590
|
+
let env = Env::default();
|
|
591
|
+
let owner = Address::generate(&env);
|
|
592
|
+
|
|
593
|
+
let contract = env.register(Contract, (&owner,));
|
|
594
|
+
let client = ContractClient::new(&env, &contract);
|
|
595
|
+
|
|
596
|
+
// Renounce ownership
|
|
597
|
+
mock_auth(&env, &contract, &owner, "renounce_ownership", ());
|
|
598
|
+
client.renounce_ownership();
|
|
599
|
+
|
|
600
|
+
// Try to renounce again - should fail with AuthorizerNotFound
|
|
601
|
+
mock_auth(&env, &contract, &owner, "renounce_ownership", ());
|
|
602
|
+
let result = client.try_renounce_ownership();
|
|
603
|
+
assert_eq!(result.err().unwrap().ok().unwrap(), OwnableError::OwnerNotSet.into());
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
#[test]
|
|
607
|
+
#[should_panic(expected = "Error(Auth, InvalidAction)")]
|
|
608
|
+
fn renounce_ownership_non_owner_fails() {
|
|
609
|
+
let env = Env::default();
|
|
610
|
+
let owner = Address::generate(&env);
|
|
611
|
+
let non_owner = Address::generate(&env);
|
|
612
|
+
|
|
613
|
+
let contract = env.register(Contract, (&owner,));
|
|
614
|
+
let client = ContractClient::new(&env, &contract);
|
|
615
|
+
|
|
616
|
+
// Non-owner tries to renounce with their own auth - should fail
|
|
617
|
+
mock_auth(&env, &contract, &non_owner, "renounce_ownership", ());
|
|
618
|
+
client.renounce_ownership();
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
#[test]
|
|
622
|
+
#[should_panic(expected = "Error(Contract, #1036)")] // TransferInProgress
|
|
623
|
+
fn renounce_blocked_during_2step_transfer() {
|
|
624
|
+
let env = Env::default();
|
|
625
|
+
let owner = Address::generate(&env);
|
|
626
|
+
let new_owner = Address::generate(&env);
|
|
627
|
+
|
|
628
|
+
let contract = env.register(Contract, (&owner,));
|
|
629
|
+
let client = ContractClient::new(&env, &contract);
|
|
630
|
+
|
|
631
|
+
let ttl = 1000u32;
|
|
632
|
+
|
|
633
|
+
// Initiate 2-step transfer
|
|
634
|
+
env.mock_auths(&[MockAuth {
|
|
635
|
+
address: &owner,
|
|
636
|
+
invoke: &MockAuthInvoke {
|
|
637
|
+
contract: &contract,
|
|
638
|
+
args: (&new_owner, ttl).into_val(&env),
|
|
639
|
+
fn_name: "begin_ownership_transfer",
|
|
640
|
+
sub_invokes: &[],
|
|
641
|
+
},
|
|
642
|
+
}]);
|
|
643
|
+
client.begin_ownership_transfer(&new_owner, &ttl);
|
|
644
|
+
|
|
645
|
+
// Try to renounce - should fail
|
|
646
|
+
env.mock_auths(&[MockAuth {
|
|
647
|
+
address: &owner,
|
|
648
|
+
invoke: &MockAuthInvoke {
|
|
649
|
+
contract: &contract,
|
|
650
|
+
args: ().into_val(&env),
|
|
651
|
+
fn_name: "renounce_ownership",
|
|
652
|
+
sub_invokes: &[],
|
|
653
|
+
},
|
|
654
|
+
}]);
|
|
655
|
+
client.renounce_ownership();
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// ============================================
|
|
659
|
+
// chain: Ownership chain tests
|
|
660
|
+
// ============================================
|
|
661
|
+
|
|
662
|
+
#[test]
|
|
663
|
+
fn chain_new_owner_can_transfer() {
|
|
664
|
+
let env = Env::default();
|
|
665
|
+
let owner = Address::generate(&env);
|
|
666
|
+
let new_owner = Address::generate(&env);
|
|
667
|
+
let third_owner = Address::generate(&env);
|
|
668
|
+
|
|
669
|
+
let contract = env.register(Contract, (&owner,));
|
|
670
|
+
let client = ContractClient::new(&env, &contract);
|
|
671
|
+
|
|
672
|
+
// Transfer to new_owner
|
|
673
|
+
mock_auth(&env, &contract, &owner, "transfer_ownership", (&new_owner,));
|
|
674
|
+
client.transfer_ownership(&new_owner);
|
|
675
|
+
|
|
676
|
+
// Verify old owner cannot call anymore
|
|
677
|
+
mock_auth(&env, &contract, &owner, "enforce_and_return_owner", ());
|
|
678
|
+
let result = client.try_enforce_and_return_owner();
|
|
679
|
+
assert!(result.is_err(), "old owner should not be able to call after transfer");
|
|
680
|
+
|
|
681
|
+
// new_owner can now transfer to third_owner
|
|
682
|
+
mock_auth(&env, &contract, &new_owner, "transfer_ownership", (&third_owner,));
|
|
683
|
+
client.transfer_ownership(&third_owner);
|
|
684
|
+
|
|
685
|
+
assert_eq!(client.owner(), Some(third_owner));
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
#[test]
|
|
689
|
+
fn chain_new_owner_can_transfer_2step() {
|
|
690
|
+
let env = Env::default();
|
|
691
|
+
let owner = Address::generate(&env);
|
|
692
|
+
let new_owner = Address::generate(&env);
|
|
693
|
+
let third_owner = Address::generate(&env);
|
|
694
|
+
|
|
695
|
+
let contract = env.register(Contract, (&owner,));
|
|
696
|
+
let client = ContractClient::new(&env, &contract);
|
|
697
|
+
|
|
698
|
+
let ttl = 1000u32;
|
|
699
|
+
|
|
700
|
+
// 2-step transfer to new_owner
|
|
701
|
+
env.mock_auths(&[MockAuth {
|
|
702
|
+
address: &owner,
|
|
703
|
+
invoke: &MockAuthInvoke {
|
|
704
|
+
contract: &contract,
|
|
705
|
+
args: (&new_owner, ttl).into_val(&env),
|
|
706
|
+
fn_name: "begin_ownership_transfer",
|
|
707
|
+
sub_invokes: &[],
|
|
708
|
+
},
|
|
709
|
+
}]);
|
|
710
|
+
client.begin_ownership_transfer(&new_owner, &ttl);
|
|
711
|
+
|
|
712
|
+
env.mock_auths(&[MockAuth {
|
|
713
|
+
address: &new_owner,
|
|
714
|
+
invoke: &MockAuthInvoke {
|
|
715
|
+
contract: &contract,
|
|
716
|
+
args: ().into_val(&env),
|
|
717
|
+
fn_name: "accept_ownership",
|
|
718
|
+
sub_invokes: &[],
|
|
719
|
+
},
|
|
720
|
+
}]);
|
|
721
|
+
client.accept_ownership();
|
|
722
|
+
|
|
723
|
+
// new_owner can now do another 2-step transfer
|
|
724
|
+
env.mock_auths(&[MockAuth {
|
|
725
|
+
address: &new_owner,
|
|
726
|
+
invoke: &MockAuthInvoke {
|
|
727
|
+
contract: &contract,
|
|
728
|
+
args: (&third_owner, ttl).into_val(&env),
|
|
729
|
+
fn_name: "begin_ownership_transfer",
|
|
730
|
+
sub_invokes: &[],
|
|
731
|
+
},
|
|
732
|
+
}]);
|
|
733
|
+
client.begin_ownership_transfer(&third_owner, &ttl);
|
|
734
|
+
|
|
735
|
+
env.mock_auths(&[MockAuth {
|
|
736
|
+
address: &third_owner,
|
|
737
|
+
invoke: &MockAuthInvoke {
|
|
738
|
+
contract: &contract,
|
|
739
|
+
args: ().into_val(&env),
|
|
740
|
+
fn_name: "accept_ownership",
|
|
741
|
+
sub_invokes: &[],
|
|
742
|
+
},
|
|
743
|
+
}]);
|
|
744
|
+
client.accept_ownership();
|
|
745
|
+
|
|
746
|
+
assert_eq!(client.owner(), Some(third_owner));
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// ============================================
|
|
750
|
+
// init: DefaultOwnable::init_owner tests
|
|
751
|
+
// ============================================
|
|
752
|
+
|
|
753
|
+
#[test]
|
|
754
|
+
fn reinit_owner_fails() {
|
|
755
|
+
let env = Env::default();
|
|
756
|
+
let owner = Address::generate(&env);
|
|
757
|
+
let new_owner = Address::generate(&env);
|
|
758
|
+
let contract = env.register(Contract, (&owner,));
|
|
759
|
+
let client = ContractClient::new(&env, &contract);
|
|
760
|
+
|
|
761
|
+
let result = client.try_reinit_owner(&new_owner);
|
|
762
|
+
assert_eq!(result.err().unwrap().ok().unwrap(), OwnableError::OwnerAlreadySet.into());
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// ============================================
|
|
766
|
+
// enforce: enforce_owner_auth tests
|
|
767
|
+
// ============================================
|
|
768
|
+
|
|
769
|
+
#[test]
|
|
770
|
+
fn enforce_owner_auth_returns_owner() {
|
|
771
|
+
let env = Env::default();
|
|
772
|
+
let owner = Address::generate(&env);
|
|
773
|
+
let contract = env.register(Contract, (&owner,));
|
|
774
|
+
let client = ContractClient::new(&env, &contract);
|
|
775
|
+
|
|
776
|
+
mock_auth(&env, &contract, &owner, "enforce_and_return_owner", ());
|
|
777
|
+
|
|
778
|
+
let returned_owner = client.enforce_and_return_owner();
|
|
779
|
+
assert_eq!(returned_owner, owner);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
#[test]
|
|
783
|
+
fn enforce_owner_auth_no_owner_set_fails() {
|
|
784
|
+
let env = Env::default();
|
|
785
|
+
let owner = Address::generate(&env);
|
|
786
|
+
let contract = env.register(Contract, (&owner,));
|
|
787
|
+
let client = ContractClient::new(&env, &contract);
|
|
788
|
+
|
|
789
|
+
client.remove_owner();
|
|
790
|
+
|
|
791
|
+
// Even with auth, enforce_owner_auth must fail because no owner is set in storage.
|
|
792
|
+
mock_auth(&env, &contract, &owner, "enforce_and_return_owner", ());
|
|
793
|
+
let result = client.try_enforce_and_return_owner();
|
|
794
|
+
assert_eq!(result.err().unwrap().ok().unwrap(), OwnableError::OwnerNotSet.into());
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
#[test]
|
|
798
|
+
fn require_owner_auth_no_owner_set_fails() {
|
|
799
|
+
let env = Env::default();
|
|
800
|
+
let owner = Address::generate(&env);
|
|
801
|
+
let contract = env.register(Contract, (&owner,));
|
|
802
|
+
let client = ContractClient::new(&env, &contract);
|
|
803
|
+
|
|
804
|
+
client.remove_owner();
|
|
805
|
+
|
|
806
|
+
let result = client.try_enforce_and_return_owner();
|
|
807
|
+
assert_eq!(result.err().unwrap().ok().unwrap(), OwnableError::OwnerNotSet.into());
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
#[test]
|
|
811
|
+
#[should_panic(expected = "Error(Auth, InvalidAction)")]
|
|
812
|
+
fn enforce_owner_auth_wrong_address_fails() {
|
|
813
|
+
let env = Env::default();
|
|
814
|
+
let owner = Address::generate(&env);
|
|
815
|
+
let wrong_address = Address::generate(&env);
|
|
816
|
+
let contract = env.register(Contract, (&owner,));
|
|
817
|
+
let client = ContractClient::new(&env, &contract);
|
|
818
|
+
|
|
819
|
+
mock_auth(&env, &contract, &wrong_address, "enforce_and_return_owner", ());
|
|
820
|
+
|
|
821
|
+
client.enforce_and_return_owner();
|
|
822
|
+
}
|