|
| 1 | +//! Demonstrates that register and register_at switch to recording auth for |
| 2 | +//! native contract constructors. |
| 3 | +//! |
| 4 | +//! A native contract whose constructor calls `require_auth` must succeed when |
| 5 | +//! registered with `register` or `register_at`, the same as the Wasm paths. |
| 6 | +//! Without switching to recording auth the constructor runs under the default |
| 7 | +//! enforcing auth and `require_auth` fails. |
| 8 | +
|
| 9 | +use crate as soroban_sdk; |
| 10 | + |
| 11 | +use soroban_sdk::{contract, contractimpl, testutils::Address as _, Address, Env}; |
| 12 | + |
| 13 | +#[contract] |
| 14 | +pub struct Contract; |
| 15 | + |
| 16 | +#[contractimpl] |
| 17 | +impl Contract { |
| 18 | + pub fn __constructor(env: Env, admin: Address) { |
| 19 | + admin.require_auth(); |
| 20 | + env.storage().instance().set(&"admin", &admin); |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | +#[contract] |
| 25 | +pub struct Probe; |
| 26 | + |
| 27 | +#[contractimpl] |
| 28 | +impl Probe { |
| 29 | + pub fn need_auth(_env: Env, address: Address) { |
| 30 | + address.require_auth(); |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +// Auth is intentionally not mocked in these tests. |
| 35 | + |
| 36 | +#[test] |
| 37 | +fn register() { |
| 38 | + let e = Env::default(); |
| 39 | + |
| 40 | + let admin = Address::generate(&e); |
| 41 | + e.register(Contract, (&admin,)); |
| 42 | +} |
| 43 | + |
| 44 | +#[test] |
| 45 | +fn register_at() { |
| 46 | + let e = Env::default(); |
| 47 | + |
| 48 | + let admin = Address::generate(&e); |
| 49 | + let contract_id = Address::generate(&e); |
| 50 | + e.register_at(&contract_id, Contract, (&admin,)); |
| 51 | +} |
| 52 | + |
| 53 | +// If registration fails while the constructor is running, the previous auth |
| 54 | +// manager must be restored rather than left in recording mode. Otherwise later |
| 55 | +// require_auth calls would be silently auto-authorized. |
| 56 | +#[test] |
| 57 | +fn register_restores_auth_before_panics() { |
| 58 | + let e = Env::default(); |
| 59 | + |
| 60 | + // A native contract with a function that requires auth, used to probe |
| 61 | + // whether the environment is enforcing authorization. |
| 62 | + let probe = e.register(Probe, ()); |
| 63 | + let probe_client = ProbeClient::new(&e, &probe); |
| 64 | + let user = Address::generate(&e); |
| 65 | + |
| 66 | + // Before the failed registration an unmocked require_auth is enforced. |
| 67 | + let pre = probe_client.try_need_auth(&user); |
| 68 | + assert!(pre.is_err()); |
| 69 | + |
| 70 | + // Registering the contract with missing constructor arguments fails while |
| 71 | + // constructing. Catch the failure inside a contract frame so it does not |
| 72 | + // abort the test. |
| 73 | + let another = e.register(Probe, ()); |
| 74 | + let register_result = e.try_as_contract::<_, soroban_sdk::Error>(&another, || { |
| 75 | + e.register(Contract, ()); |
| 76 | + }); |
| 77 | + assert!(register_result.is_err()); |
| 78 | + |
| 79 | + // The previous auth manager must have been restored: require_auth is still |
| 80 | + // enforced after the failed registration. |
| 81 | + let post = probe_client.try_need_auth(&user); |
| 82 | + assert!(post.is_err()); |
| 83 | + assert_eq!(pre, post); |
| 84 | +} |
0 commit comments