diff --git a/contracts/split/src/lib.rs b/contracts/split/src/lib.rs index 3a9c2b1..7e332de 100644 --- a/contracts/split/src/lib.rs +++ b/contracts/split/src/lib.rs @@ -100,6 +100,13 @@ fn admin_key() -> Symbol { fn admins_key() -> Symbol { symbol_short!("admins") } + +/// Issue #477: One-shot initialiser guard — instance storage. +/// Set to `true` at the end of `initialize()`; checked at the top to prevent +/// re-initialisation (front-run protection). +fn initialised_key() -> Symbol { + symbol_short!("init_flg") +} fn paused_key() -> Symbol { symbol_short!("paused") } @@ -2385,10 +2392,11 @@ impl SplitContract { rate_limit: u32, rate_window: u64, ) { - assert!( - !env.storage().instance().has(&admin_key()), - "already initialized" - ); + // Issue #477: one-shot initialiser guard using a dedicated key so the + // guard cannot be bypassed by front-running the admin write. + if env.storage().instance().get::<_, bool>(&initialised_key()).unwrap_or(false) { + panic!("AlreadyInitialised"); + } assert!(creation_fee >= 0, "creation_fee must be non-negative"); assert!( platform_fee_bps <= 10_000, @@ -2431,6 +2439,9 @@ impl SplitContract { env.storage() .instance() .set(&storage_quota_key(), &DEFAULT_INVOICE_STORAGE_QUOTA); + // Issue #477: set the initialisation flag atomically at the end so the + // contract is marked fully initialised only after all state is written. + env.storage().instance().set(&initialised_key(), &true); } /// Add a new admin with a given role. Requires SuperAdmin auth. diff --git a/contracts/split/src/storage_snapshot.rs b/contracts/split/src/storage_snapshot.rs index 176d896..eeabf3e 100644 --- a/contracts/split/src/storage_snapshot.rs +++ b/contracts/split/src/storage_snapshot.rs @@ -125,6 +125,8 @@ fn storage_key_snapshot() { "upgrade_proposal_key", hex_xdr(&env, upgrade_proposal_key()), )); + // Issue #477: one-shot initialiser guard — instance storage + keys.push(("initialised_key", hex_xdr(&env, initialised_key()))); // ----------------------------------------------------------------------- // Persistent-tier keys (per-entity) diff --git a/tests/snapshots/storage_keys.json b/tests/snapshots/storage_keys.json index acac5b8..5514243 100644 --- a/tests/snapshots/storage_keys.json +++ b/tests/snapshots/storage_keys.json @@ -50,6 +50,7 @@ "governance_contract_key": "0000000f00000007676f765f63747200", "group_key": "0000001000000001000000020000000f0000000367727000000000050000000000000001", "group_treasury_key": "0000001000000001000000020000000f000000066772705f74720000000000050000000000000001", + "initialised_key": "0000000f00000008696e69745f666c67", "invoice_compact_key": "0000001000000001000000020000000f00000007696e765f63707400000000050000000000000001", "invoice_count_key": "0000001000000001000000020000000f00000009696e765f636f756e740000000000001200000000000000000000000000000000000000000000000000000000000000000000000000000000", "invoice_ext2_key": "0000001000000001000000020000000f00000007696e765f65783200000000050000000000000001",