Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent contracts from being re-initialized #307

Merged
15 changes: 13 additions & 2 deletions upgradeable_contract/new_contract/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
#![no_std]

use soroban_sdk::{contract, contractimpl, contracttype, Address, BytesN, Env};
use soroban_sdk::{contract, contracterror, contractimpl, contracttype, Address, BytesN, Env};

#[contracttype]
#[derive(Clone)]
enum DataKey {
Admin,
}

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Error {
AlreadyInitialized = 1,
matiassequeira marked this conversation as resolved.
Show resolved Hide resolved
}

#[contract]
pub struct UpgradeableContract;

#[contractimpl]
impl UpgradeableContract {
pub fn init(e: Env, admin: Address) {
pub fn init(e: Env, admin: Address) -> Result<(), Error> {
if e.storage().instance().has(&DataKey::Admin) {
return Err(Error::AlreadyInitialized);
}
e.storage().instance().set(&DataKey::Admin, &admin);
Ok(())
}

pub fn version() -> u32 {
Expand Down
15 changes: 13 additions & 2 deletions upgradeable_contract/old_contract/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
#![no_std]

use soroban_sdk::{contract, contractimpl, contracttype, Address, BytesN, Env};
use soroban_sdk::{contract, contracterror, contractimpl, contracttype, Address, BytesN, Env};

#[contracttype]
#[derive(Clone)]
enum DataKey {
Admin,
}

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
#[repr(u32)]
pub enum Error {
AlreadyInitialized = 1,
}

#[contract]
pub struct UpgradeableContract;

#[contractimpl]
impl UpgradeableContract {
pub fn init(e: Env, admin: Address) {
pub fn init(e: Env, admin: Address) -> Result<(), Error> {
if e.storage().instance().has(&DataKey::Admin) {
return Err(Error::AlreadyInitialized);
}
e.storage().instance().set(&DataKey::Admin, &admin);
Ok(())
}

pub fn version() -> u32 {
Expand Down
22 changes: 22 additions & 0 deletions upgradeable_contract/old_contract/src/test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![cfg(test)]

use crate::Error;
use soroban_sdk::{testutils::Address as _, Address, BytesN, Env};

mod old_contract {
Expand Down Expand Up @@ -44,3 +45,24 @@ fn test() {
let client = new_contract::Client::new(&env, &contract_id);
assert_eq!(1010101, client.new_v2_fn());
}

#[test]
fn test_cannot_re_init() {
let env = Env::default();
env.mock_all_auths();

// Note that we use register_contract_wasm instead of register_contract
// because the old contracts WASM is expected to exist in storage.
let contract_id = env.register_contract_wasm(None, old_contract::WASM);
let client = old_contract::Client::new(&env, &contract_id);
let admin = Address::generate(&env);
client.init(&admin);

// `try_init` is expected to return an error. Since client is generated from Wasm,
// this is a generic SDK error.
let err: soroban_sdk::Error = client.try_init(&admin).err().unwrap().unwrap();
// Convert the SDK error to the contract error.
let contract_err: Error = err.try_into().unwrap();
// Make sure contract error has the expected value.
assert_eq!(contract_err, Error::AlreadyInitialized);
}
Loading