Skip to content

Commit

Permalink
Merge pull request #812 from CosmWasm/744-multitest-update-admin
Browse files Browse the repository at this point in the history
[multi-test] Add update and clear admin support to WasmKeeper
  • Loading branch information
uint committed Sep 28, 2022
2 parents 84ecb66 + 86bd569 commit 7a32e5c
Show file tree
Hide file tree
Showing 2 changed files with 226 additions and 1 deletion.
62 changes: 62 additions & 0 deletions packages/multi-test/src/app.rs
Expand Up @@ -1828,6 +1828,68 @@ mod test {
assert_eq!(state.beneficiary, random);
}

#[test]
fn send_update_admin_works() {
// The plan:
// create a hackatom contract
// check admin set properly
// update admin succeeds if admin
// update admin fails if not (new) admin
// check admin set properly
let owner = Addr::unchecked("owner");
let owner2 = Addr::unchecked("owner2");
let beneficiary = Addr::unchecked("beneficiary");

let mut app = App::default();

// create a hackatom contract with some funds
let contract_id = app.store_code(hackatom::contract());
let contract = app
.instantiate_contract(
contract_id,
owner.clone(),
&hackatom::InstantiateMsg {
beneficiary: beneficiary.as_str().to_owned(),
},
&[],
"Hackatom",
Some(owner.to_string()),
)
.unwrap();

// check admin set properly
let info = app.contract_data(&contract).unwrap();
assert_eq!(info.admin, Some(owner.clone()));

// transfer adminship to owner2
app.execute(
owner.clone(),
CosmosMsg::Wasm(WasmMsg::UpdateAdmin {
contract_addr: contract.to_string(),
admin: owner2.to_string(),
}),
)
.unwrap();

// check admin set properly
let info = app.contract_data(&contract).unwrap();
assert_eq!(info.admin, Some(owner2.clone()));

// update admin fails if not owner2
app.execute(
owner.clone(),
CosmosMsg::Wasm(WasmMsg::UpdateAdmin {
contract_addr: contract.to_string(),
admin: owner.to_string(),
}),
)
.unwrap_err();

// check admin still the same
let info = app.contract_data(&contract).unwrap();
assert_eq!(info.admin, Some(owner2));
}

mod reply_data_overwrite {
use super::*;

Expand Down
165 changes: 164 additions & 1 deletion packages/multi-test/src/wasm.rs
Expand Up @@ -327,6 +327,34 @@ where
}
}

/// unified logic for UpdateAdmin and ClearAdmin messages
fn update_admin(
&self,
api: &dyn Api,
storage: &mut dyn Storage,
sender: Addr,
contract_addr: &str,
new_admin: Option<String>,
) -> AnyResult<AppResponse> {
let contract_addr = api.addr_validate(contract_addr)?;
let admin = new_admin.map(|a| api.addr_validate(&a)).transpose()?;

// check admin status
let mut data = self.load_contract(storage, &contract_addr)?;
if data.admin != Some(sender) {
bail!("Only admin can update the contract admin: {:?}", data.admin);
}
// update admin field
data.admin = admin;
self.save_contract(storage, &contract_addr, &data)?;

// no custom event here
Ok(AppResponse {
data: None,
events: vec![],
})
}

// this returns the contract address as well, so we can properly resend the data
fn execute_wasm(
&self,
Expand Down Expand Up @@ -474,6 +502,13 @@ where
res.data = execute_response(res.data);
Ok(res)
}
WasmMsg::UpdateAdmin {
contract_addr,
admin,
} => self.update_admin(api, storage, sender, &contract_addr, Some(admin)),
WasmMsg::ClearAdmin { contract_addr } => {
self.update_admin(api, storage, sender, &contract_addr, None)
}
msg => bail!(Error::UnsupportedWasmMsg(msg)),
}
}
Expand Down Expand Up @@ -906,7 +941,8 @@ mod test {
use crate::app::Router;
use crate::bank::BankKeeper;
use crate::module::FailingModule;
use crate::test_helpers::contracts::{error, payout};
use crate::test_helpers::contracts::{caller, error, payout};
use crate::test_helpers::EmptyMsg;
use crate::transactions::StorageTransaction;

use super::*;
Expand Down Expand Up @@ -1356,4 +1392,131 @@ mod test {
assert_payout(&keeper, &mut wasm_storage, &contract2, &payout2);
assert_payout(&keeper, &mut wasm_storage, &contract3, &payout3);
}

fn assert_admin(
storage: &dyn Storage,
keeper: &WasmKeeper<Empty, Empty>,
contract_addr: &impl ToString,
admin: Option<Addr>,
) {
let api = MockApi::default();
let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
// query
let data = keeper
.query(
&api,
storage,
&querier,
&mock_env().block,
WasmQuery::ContractInfo {
contract_addr: contract_addr.to_string(),
},
)
.unwrap();
let res: ContractInfoResponse = from_slice(&data).unwrap();
assert_eq!(res.admin, admin.as_ref().map(Addr::to_string));
}

#[test]
fn update_clear_admin_works() {
let api = MockApi::default();
let mut keeper = WasmKeeper::new();
let block = mock_env().block;
let code_id = keeper.store_code(caller::contract());

let mut wasm_storage = MockStorage::new();

let admin: Addr = Addr::unchecked("admin");
let new_admin: Addr = Addr::unchecked("new_admin");
let normal_user: Addr = Addr::unchecked("normal_user");

let contract_addr = keeper
.register_contract(
&mut wasm_storage,
code_id,
Addr::unchecked("creator"),
admin.clone(),
"label".to_owned(),
1000,
)
.unwrap();

// init the contract
let info = mock_info("admin", &[]);
let init_msg = to_vec(&EmptyMsg {}).unwrap();
let res = keeper
.call_instantiate(
contract_addr.clone(),
&api,
&mut wasm_storage,
&mock_router(),
&block,
info,
init_msg,
)
.unwrap();
assert_eq!(0, res.messages.len());

assert_admin(&wasm_storage, &keeper, &contract_addr, Some(admin.clone()));

// non-admin should not be allowed to become admin on their own
keeper
.execute_wasm(
&api,
&mut wasm_storage,
&mock_router(),
&block,
normal_user.clone(),
WasmMsg::UpdateAdmin {
contract_addr: contract_addr.to_string(),
admin: normal_user.to_string(),
},
)
.unwrap_err();

// should still be admin
assert_admin(&wasm_storage, &keeper, &contract_addr, Some(admin.clone()));

// admin should be allowed to transfers adminship
let res = keeper
.execute_wasm(
&api,
&mut wasm_storage,
&mock_router(),
&block,
admin,
WasmMsg::UpdateAdmin {
contract_addr: contract_addr.to_string(),
admin: new_admin.to_string(),
},
)
.unwrap();
assert_eq!(res.events.len(), 0);

// new_admin should now be admin
assert_admin(
&wasm_storage,
&keeper,
&contract_addr,
Some(new_admin.clone()),
);

// new_admin should now be able to clear to admin
let res = keeper
.execute_wasm(
&api,
&mut wasm_storage,
&mock_router(),
&block,
new_admin,
WasmMsg::ClearAdmin {
contract_addr: contract_addr.to_string(),
},
)
.unwrap();
assert_eq!(res.events.len(), 0);

// should have no admin now
assert_admin(&wasm_storage, &keeper, &contract_addr, None);
}
}

0 comments on commit 7a32e5c

Please sign in to comment.