Skip to content

Commit 7a32e5c

Browse files
authored
Merge pull request #812 from CosmWasm/744-multitest-update-admin
[multi-test] Add update and clear admin support to WasmKeeper
2 parents 84ecb66 + 86bd569 commit 7a32e5c

File tree

2 files changed

+226
-1
lines changed

2 files changed

+226
-1
lines changed

packages/multi-test/src/app.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,6 +1828,68 @@ mod test {
18281828
assert_eq!(state.beneficiary, random);
18291829
}
18301830

1831+
#[test]
1832+
fn send_update_admin_works() {
1833+
// The plan:
1834+
// create a hackatom contract
1835+
// check admin set properly
1836+
// update admin succeeds if admin
1837+
// update admin fails if not (new) admin
1838+
// check admin set properly
1839+
let owner = Addr::unchecked("owner");
1840+
let owner2 = Addr::unchecked("owner2");
1841+
let beneficiary = Addr::unchecked("beneficiary");
1842+
1843+
let mut app = App::default();
1844+
1845+
// create a hackatom contract with some funds
1846+
let contract_id = app.store_code(hackatom::contract());
1847+
let contract = app
1848+
.instantiate_contract(
1849+
contract_id,
1850+
owner.clone(),
1851+
&hackatom::InstantiateMsg {
1852+
beneficiary: beneficiary.as_str().to_owned(),
1853+
},
1854+
&[],
1855+
"Hackatom",
1856+
Some(owner.to_string()),
1857+
)
1858+
.unwrap();
1859+
1860+
// check admin set properly
1861+
let info = app.contract_data(&contract).unwrap();
1862+
assert_eq!(info.admin, Some(owner.clone()));
1863+
1864+
// transfer adminship to owner2
1865+
app.execute(
1866+
owner.clone(),
1867+
CosmosMsg::Wasm(WasmMsg::UpdateAdmin {
1868+
contract_addr: contract.to_string(),
1869+
admin: owner2.to_string(),
1870+
}),
1871+
)
1872+
.unwrap();
1873+
1874+
// check admin set properly
1875+
let info = app.contract_data(&contract).unwrap();
1876+
assert_eq!(info.admin, Some(owner2.clone()));
1877+
1878+
// update admin fails if not owner2
1879+
app.execute(
1880+
owner.clone(),
1881+
CosmosMsg::Wasm(WasmMsg::UpdateAdmin {
1882+
contract_addr: contract.to_string(),
1883+
admin: owner.to_string(),
1884+
}),
1885+
)
1886+
.unwrap_err();
1887+
1888+
// check admin still the same
1889+
let info = app.contract_data(&contract).unwrap();
1890+
assert_eq!(info.admin, Some(owner2));
1891+
}
1892+
18311893
mod reply_data_overwrite {
18321894
use super::*;
18331895

packages/multi-test/src/wasm.rs

Lines changed: 164 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,34 @@ where
327327
}
328328
}
329329

330+
/// unified logic for UpdateAdmin and ClearAdmin messages
331+
fn update_admin(
332+
&self,
333+
api: &dyn Api,
334+
storage: &mut dyn Storage,
335+
sender: Addr,
336+
contract_addr: &str,
337+
new_admin: Option<String>,
338+
) -> AnyResult<AppResponse> {
339+
let contract_addr = api.addr_validate(contract_addr)?;
340+
let admin = new_admin.map(|a| api.addr_validate(&a)).transpose()?;
341+
342+
// check admin status
343+
let mut data = self.load_contract(storage, &contract_addr)?;
344+
if data.admin != Some(sender) {
345+
bail!("Only admin can update the contract admin: {:?}", data.admin);
346+
}
347+
// update admin field
348+
data.admin = admin;
349+
self.save_contract(storage, &contract_addr, &data)?;
350+
351+
// no custom event here
352+
Ok(AppResponse {
353+
data: None,
354+
events: vec![],
355+
})
356+
}
357+
330358
// this returns the contract address as well, so we can properly resend the data
331359
fn execute_wasm(
332360
&self,
@@ -474,6 +502,13 @@ where
474502
res.data = execute_response(res.data);
475503
Ok(res)
476504
}
505+
WasmMsg::UpdateAdmin {
506+
contract_addr,
507+
admin,
508+
} => self.update_admin(api, storage, sender, &contract_addr, Some(admin)),
509+
WasmMsg::ClearAdmin { contract_addr } => {
510+
self.update_admin(api, storage, sender, &contract_addr, None)
511+
}
477512
msg => bail!(Error::UnsupportedWasmMsg(msg)),
478513
}
479514
}
@@ -906,7 +941,8 @@ mod test {
906941
use crate::app::Router;
907942
use crate::bank::BankKeeper;
908943
use crate::module::FailingModule;
909-
use crate::test_helpers::contracts::{error, payout};
944+
use crate::test_helpers::contracts::{caller, error, payout};
945+
use crate::test_helpers::EmptyMsg;
910946
use crate::transactions::StorageTransaction;
911947

912948
use super::*;
@@ -1356,4 +1392,131 @@ mod test {
13561392
assert_payout(&keeper, &mut wasm_storage, &contract2, &payout2);
13571393
assert_payout(&keeper, &mut wasm_storage, &contract3, &payout3);
13581394
}
1395+
1396+
fn assert_admin(
1397+
storage: &dyn Storage,
1398+
keeper: &WasmKeeper<Empty, Empty>,
1399+
contract_addr: &impl ToString,
1400+
admin: Option<Addr>,
1401+
) {
1402+
let api = MockApi::default();
1403+
let querier: MockQuerier<Empty> = MockQuerier::new(&[]);
1404+
// query
1405+
let data = keeper
1406+
.query(
1407+
&api,
1408+
storage,
1409+
&querier,
1410+
&mock_env().block,
1411+
WasmQuery::ContractInfo {
1412+
contract_addr: contract_addr.to_string(),
1413+
},
1414+
)
1415+
.unwrap();
1416+
let res: ContractInfoResponse = from_slice(&data).unwrap();
1417+
assert_eq!(res.admin, admin.as_ref().map(Addr::to_string));
1418+
}
1419+
1420+
#[test]
1421+
fn update_clear_admin_works() {
1422+
let api = MockApi::default();
1423+
let mut keeper = WasmKeeper::new();
1424+
let block = mock_env().block;
1425+
let code_id = keeper.store_code(caller::contract());
1426+
1427+
let mut wasm_storage = MockStorage::new();
1428+
1429+
let admin: Addr = Addr::unchecked("admin");
1430+
let new_admin: Addr = Addr::unchecked("new_admin");
1431+
let normal_user: Addr = Addr::unchecked("normal_user");
1432+
1433+
let contract_addr = keeper
1434+
.register_contract(
1435+
&mut wasm_storage,
1436+
code_id,
1437+
Addr::unchecked("creator"),
1438+
admin.clone(),
1439+
"label".to_owned(),
1440+
1000,
1441+
)
1442+
.unwrap();
1443+
1444+
// init the contract
1445+
let info = mock_info("admin", &[]);
1446+
let init_msg = to_vec(&EmptyMsg {}).unwrap();
1447+
let res = keeper
1448+
.call_instantiate(
1449+
contract_addr.clone(),
1450+
&api,
1451+
&mut wasm_storage,
1452+
&mock_router(),
1453+
&block,
1454+
info,
1455+
init_msg,
1456+
)
1457+
.unwrap();
1458+
assert_eq!(0, res.messages.len());
1459+
1460+
assert_admin(&wasm_storage, &keeper, &contract_addr, Some(admin.clone()));
1461+
1462+
// non-admin should not be allowed to become admin on their own
1463+
keeper
1464+
.execute_wasm(
1465+
&api,
1466+
&mut wasm_storage,
1467+
&mock_router(),
1468+
&block,
1469+
normal_user.clone(),
1470+
WasmMsg::UpdateAdmin {
1471+
contract_addr: contract_addr.to_string(),
1472+
admin: normal_user.to_string(),
1473+
},
1474+
)
1475+
.unwrap_err();
1476+
1477+
// should still be admin
1478+
assert_admin(&wasm_storage, &keeper, &contract_addr, Some(admin.clone()));
1479+
1480+
// admin should be allowed to transfers adminship
1481+
let res = keeper
1482+
.execute_wasm(
1483+
&api,
1484+
&mut wasm_storage,
1485+
&mock_router(),
1486+
&block,
1487+
admin,
1488+
WasmMsg::UpdateAdmin {
1489+
contract_addr: contract_addr.to_string(),
1490+
admin: new_admin.to_string(),
1491+
},
1492+
)
1493+
.unwrap();
1494+
assert_eq!(res.events.len(), 0);
1495+
1496+
// new_admin should now be admin
1497+
assert_admin(
1498+
&wasm_storage,
1499+
&keeper,
1500+
&contract_addr,
1501+
Some(new_admin.clone()),
1502+
);
1503+
1504+
// new_admin should now be able to clear to admin
1505+
let res = keeper
1506+
.execute_wasm(
1507+
&api,
1508+
&mut wasm_storage,
1509+
&mock_router(),
1510+
&block,
1511+
new_admin,
1512+
WasmMsg::ClearAdmin {
1513+
contract_addr: contract_addr.to_string(),
1514+
},
1515+
)
1516+
.unwrap();
1517+
assert_eq!(res.events.len(), 0);
1518+
1519+
// should have no admin now
1520+
assert_admin(&wasm_storage, &keeper, &contract_addr, None);
1521+
}
13591522
}

0 commit comments

Comments
 (0)