Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dda00a0f16 | |||
| bbc6689a32 | |||
| 12345dbd2c | |||
| 63a1123ef3 | |||
| 3bfdf224d3 | |||
| 0ab0efd974 | |||
| 8b7ec0756e | |||
| 062f1c28f4 | |||
| 99fe1288f4 |
Generated
+515
-259
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["coconut-bandwidth", "mixnet", "vesting", "multisig/cw3-flex-multisig", "multisig/cw4-group", "coconut-test", "coconut-dkg"]
|
||||
members = ["coconut-bandwidth", "mixnet", "vesting", "multisig/cw3-flex-multisig", "multisig/cw4-group", "coconut-test", "coconut-dkg", "service-provider-directory"]
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[alias]
|
||||
wasm = "build --target wasm32-unknown-unknown --release"
|
||||
wasm-debug = "build --target wasm32-unknown-unknown"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
/target
|
||||
/Cargo.lock
|
||||
/notes.txt
|
||||
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "storage2"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = { version = "1.0.0-beta8", features = ["staking"] }
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
cw-storage-plus = "0.13.4"
|
||||
cw2 = { version = "0.16.0" }
|
||||
thiserror = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
cw-multi-test = "0.13.4"
|
||||
@@ -0,0 +1,428 @@
|
||||
use crate::error::ContractError;
|
||||
use crate::msg::{QueryMsg, InstantiateMsg, ExecuteMsg, ServicesListResp, ConfigResponse};
|
||||
use crate::state::{SERVICES, Service, CONFIG, Config};
|
||||
use cosmwasm_std::{
|
||||
to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult, Order
|
||||
};
|
||||
use cw2::set_contract_version;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
const CONTRACT_NAME: &str = "service-storage-poc";
|
||||
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub fn instantiate(
|
||||
deps: DepsMut,
|
||||
_env: Env,
|
||||
_info: MessageInfo,
|
||||
msg: InstantiateMsg,
|
||||
) -> StdResult<Response> {
|
||||
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
|
||||
let config = Config {
|
||||
updater_role: msg.updater_role,
|
||||
admin: msg.admin
|
||||
};
|
||||
|
||||
CONFIG.save(deps.storage, &config)?;
|
||||
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
pub fn execute(
|
||||
_deps: DepsMut,
|
||||
_env: Env,
|
||||
_info: MessageInfo,
|
||||
msg: ExecuteMsg
|
||||
) -> Result<Response, ContractError> {
|
||||
use ExecuteMsg::*;
|
||||
|
||||
match msg {
|
||||
Announce { client_address, standard_whitelist, owner } => exec::announce(_deps, _info, client_address, standard_whitelist, owner ),
|
||||
Delete { client_address } => exec::delete(_deps, _info, client_address),
|
||||
UpdateScore { client_address, new_score } => exec::update_score(_deps, _info, client_address, new_score)
|
||||
}
|
||||
}
|
||||
|
||||
mod exec {
|
||||
use super::*;
|
||||
|
||||
pub fn announce(
|
||||
deps: DepsMut,
|
||||
info: MessageInfo,
|
||||
client_address: String,
|
||||
standard_whitelist: bool,
|
||||
owner: Addr
|
||||
) -> Result<Response, ContractError> {
|
||||
|
||||
let new_service = Service {
|
||||
client_address: client_address.clone(),
|
||||
standard_whitelist,
|
||||
uptime_score: 0, // init @ 0 - no score on new service
|
||||
owner
|
||||
};
|
||||
|
||||
SERVICES.save(deps.storage, client_address.clone(), &new_service)?;
|
||||
|
||||
Ok(Response::new()
|
||||
.add_attribute("action", "service announced")
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO finish
|
||||
*/
|
||||
pub fn update_score(
|
||||
deps: DepsMut,
|
||||
info: MessageInfo,
|
||||
client_address: String,
|
||||
new_score: i8
|
||||
) -> Result<Response, ContractError> {
|
||||
|
||||
let to_update = SERVICES.load(deps.storage, client_address.clone())?;
|
||||
|
||||
// update score & save
|
||||
|
||||
Ok(Response::new()
|
||||
.add_attribute("action", "service updated")
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
pub fn delete(
|
||||
deps: DepsMut,
|
||||
info: MessageInfo,
|
||||
client_address: String
|
||||
) -> Result<Response, ContractError> {
|
||||
|
||||
let service_to_delete = SERVICES.load(deps.storage, client_address.clone())?;
|
||||
|
||||
if info.sender != service_to_delete.owner {
|
||||
return Err(ContractError::Unauthorized {
|
||||
sender: info.sender,
|
||||
});
|
||||
}
|
||||
|
||||
SERVICES.remove(deps.storage, client_address.clone());
|
||||
|
||||
Ok(Response::new()
|
||||
.add_attribute("action", "service deleted")
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
|
||||
use QueryMsg::*;
|
||||
|
||||
match msg {
|
||||
QueryAll {} => to_binary(&query::query_all(_deps, _env)?),
|
||||
QueryConfig {} => to_binary(&query::query_config(_deps, _env)?)
|
||||
}
|
||||
}
|
||||
|
||||
mod query {
|
||||
use crate::msg::ServicesInfo;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub fn query_all(
|
||||
deps: Deps,
|
||||
_env: Env,
|
||||
) -> StdResult<ServicesListResp> {
|
||||
let services = SERVICES
|
||||
.range(deps.storage, None, None, Order::Ascending)
|
||||
.map(|item| {
|
||||
item.map(|(owner, services)| ServicesInfo {
|
||||
owner,
|
||||
services
|
||||
})
|
||||
})
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
let resp = ServicesListResp{ services };
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
pub fn query_config(
|
||||
_deps: Deps,
|
||||
_env: Env,
|
||||
) -> StdResult<ConfigResponse> {
|
||||
let config = CONFIG.load(_deps.storage)?;
|
||||
let resp = ConfigResponse {
|
||||
updater_role: config.updater_role,
|
||||
admin: config.admin
|
||||
};
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cosmwasm_std::Addr;
|
||||
use cw_multi_test::{App, ContractWrapper, Executor};
|
||||
use crate::msg::ServicesInfo;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn set_config() {
|
||||
let mut app = App::default();
|
||||
let code = ContractWrapper::new(execute, instantiate, query);
|
||||
let code_id = app.store_code(Box::new(code));
|
||||
|
||||
let addr = app
|
||||
.instantiate_contract(
|
||||
code_id,
|
||||
Addr::unchecked("owner"),
|
||||
&InstantiateMsg { updater_role: Addr::unchecked("updater"), admin: Addr::unchecked("admin") },
|
||||
&[],
|
||||
"Contract",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let resp: ConfigResponse = app
|
||||
.wrap()
|
||||
.query_wasm_smart(addr, &QueryMsg::QueryConfig {})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
resp,
|
||||
ConfigResponse {
|
||||
updater_role: Addr::unchecked("updater"),
|
||||
admin: Addr::unchecked("admin")
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn announce_and_query_service() {
|
||||
let mut app = App::default();
|
||||
let code = ContractWrapper::new(execute, instantiate, query);
|
||||
let code_id = app.store_code(Box::new(code));
|
||||
let addr = app
|
||||
.instantiate_contract(
|
||||
code_id,
|
||||
Addr::unchecked("owner"),
|
||||
&InstantiateMsg{ updater_role: Addr::unchecked("updater"), admin: Addr::unchecked("admin") },
|
||||
&[],
|
||||
"Contract",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let resp = app
|
||||
.execute_contract(
|
||||
Addr::unchecked("owner"),
|
||||
addr.clone(),
|
||||
&ExecuteMsg::Announce {
|
||||
client_address: "nymAddress".to_owned(),
|
||||
standard_whitelist: true,
|
||||
owner: Addr::unchecked("owner")
|
||||
},
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let wasm = resp.events.iter().find(|ev| ev.ty == "wasm").unwrap();
|
||||
assert_eq!(
|
||||
wasm.attributes
|
||||
.iter()
|
||||
.find(|attr| attr.key == "action")
|
||||
.unwrap()
|
||||
.value,
|
||||
"service announced"
|
||||
);
|
||||
|
||||
let query: ServicesListResp = app.wrap()
|
||||
.query_wasm_smart(addr.clone(), &QueryMsg::QueryAll { })
|
||||
.unwrap();
|
||||
|
||||
let test_service: Service = Service {
|
||||
client_address: "nymAddress".to_string(),
|
||||
standard_whitelist: true,
|
||||
owner: Addr::unchecked("owner"),
|
||||
uptime_score: 0
|
||||
};
|
||||
|
||||
let expected = vec![
|
||||
ServicesInfo {
|
||||
owner: "nymAddress".to_string(),
|
||||
services: test_service,
|
||||
}
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
query,
|
||||
ServicesListResp {
|
||||
services: expected
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn delete_service() {
|
||||
let mut app = App::default();
|
||||
let code = ContractWrapper::new(execute, instantiate, query);
|
||||
let code_id = app.store_code(Box::new(code));
|
||||
let addr = app
|
||||
.instantiate_contract(
|
||||
code_id,
|
||||
Addr::unchecked("owner"),
|
||||
&InstantiateMsg{ updater_role: Addr::unchecked("updater"), admin: Addr::unchecked("admin") },
|
||||
&[],
|
||||
"Contract",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
app
|
||||
.execute_contract(
|
||||
Addr::unchecked("owner"),
|
||||
addr.clone(),
|
||||
&ExecuteMsg::Announce {
|
||||
client_address: "nymAddress".to_string(),
|
||||
standard_whitelist: true,
|
||||
owner: Addr::unchecked("owner")
|
||||
},
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let query: ServicesListResp = app.wrap()
|
||||
.query_wasm_smart(addr.clone(), &QueryMsg::QueryAll { })
|
||||
.unwrap();
|
||||
|
||||
let test_service: Service = Service {
|
||||
client_address: "nymAddress".to_string(),
|
||||
standard_whitelist: true,
|
||||
owner: Addr::unchecked("owner"),
|
||||
uptime_score: 0
|
||||
};
|
||||
|
||||
let expected = vec![
|
||||
ServicesInfo {
|
||||
owner: "nymAddress".to_string(),
|
||||
services: test_service,
|
||||
}
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
query,
|
||||
ServicesListResp {
|
||||
services: expected
|
||||
}
|
||||
);
|
||||
|
||||
let delete_resp = app
|
||||
.execute_contract(
|
||||
Addr::unchecked("owner"),
|
||||
addr.clone(),
|
||||
&ExecuteMsg::Delete {
|
||||
client_address: "nymAddress".to_string()
|
||||
},
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let wasm = delete_resp.events.iter().find(|ev| ev.ty == "wasm").unwrap();
|
||||
assert_eq!(
|
||||
wasm.attributes
|
||||
.iter()
|
||||
.find(|attr| attr.key == "action")
|
||||
.unwrap()
|
||||
.value,
|
||||
"service deleted"
|
||||
);
|
||||
|
||||
let query: ServicesListResp = app.wrap()
|
||||
.query_wasm_smart(addr.clone(), &QueryMsg::QueryAll { })
|
||||
.unwrap();
|
||||
|
||||
let expected = vec![];
|
||||
|
||||
assert_eq!(
|
||||
query,
|
||||
ServicesListResp {
|
||||
services: expected
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_owner_can_delete_service() {
|
||||
let mut app = App::default();
|
||||
let code = ContractWrapper::new(execute, instantiate, query);
|
||||
let code_id = app.store_code(Box::new(code));
|
||||
let addr = app
|
||||
.instantiate_contract(
|
||||
code_id,
|
||||
Addr::unchecked("owner"),
|
||||
&InstantiateMsg{ updater_role: Addr::unchecked("updater"), admin: Addr::unchecked("admin") },
|
||||
&[],
|
||||
"Contract",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
app
|
||||
.execute_contract(
|
||||
Addr::unchecked("owner"),
|
||||
addr.clone(),
|
||||
&ExecuteMsg::Announce {
|
||||
client_address: "nymAddress".to_string(),
|
||||
standard_whitelist: true,
|
||||
owner: Addr::unchecked("owner")
|
||||
},
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let query: ServicesListResp = app.wrap()
|
||||
.query_wasm_smart(addr.clone(), &QueryMsg::QueryAll { })
|
||||
.unwrap();
|
||||
|
||||
let test_service: Service = Service {
|
||||
client_address: "nymAddress".to_string(),
|
||||
standard_whitelist: true,
|
||||
owner: Addr::unchecked("owner"),
|
||||
uptime_score: 0
|
||||
};
|
||||
|
||||
let expected = vec![
|
||||
ServicesInfo {
|
||||
owner: "nymAddress".to_string(),
|
||||
services: test_service,
|
||||
}
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
query,
|
||||
ServicesListResp {
|
||||
services: expected
|
||||
}
|
||||
);
|
||||
|
||||
let delete_resp = app
|
||||
.execute_contract(
|
||||
Addr::unchecked("not_owner"),
|
||||
addr.clone(),
|
||||
&ExecuteMsg::Delete {
|
||||
client_address: "nymAddress".to_string()
|
||||
},
|
||||
&[],
|
||||
)
|
||||
.unwrap_err(); // we're **expecting** an error hence this will panic if delete_resp = Ok value
|
||||
|
||||
assert_eq!(
|
||||
ContractError::Unauthorized {
|
||||
sender: Addr::unchecked("not_owner")
|
||||
},
|
||||
delete_resp.downcast().unwrap()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
use cosmwasm_std::{Addr, StdError};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum ContractError {
|
||||
#[error("{0}")]
|
||||
StdError(#[from] StdError),
|
||||
#[error("{sender} is not owner of service")]
|
||||
Unauthorized { sender: Addr },
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
use cosmwasm_std::{
|
||||
entry_point, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
|
||||
};
|
||||
use error::{ContractError};
|
||||
use crate::msg::ExecuteMsg;
|
||||
|
||||
mod contract;
|
||||
mod msg;
|
||||
mod state;
|
||||
mod error;
|
||||
|
||||
#[entry_point]
|
||||
pub fn instantiate(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
msg: msg::InstantiateMsg,
|
||||
) -> StdResult<Response> {
|
||||
contract::instantiate(deps, env, info, msg)
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn execute(
|
||||
deps: DepsMut,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
msg: ExecuteMsg
|
||||
) -> Result<Response, ContractError> {
|
||||
contract::execute(deps, env, info, msg)
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn query(deps: Deps, env: Env, msg: msg::QueryMsg)
|
||||
-> StdResult<Binary>
|
||||
{
|
||||
contract::query(deps, env, msg)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use cosmwasm_std::Addr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::state::Service;
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
pub enum QueryMsg {
|
||||
QueryAll {},
|
||||
QueryConfig {},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
pub struct GreetResp {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
pub struct InstantiateMsg {
|
||||
pub updater_role: Addr,
|
||||
pub admin: Addr
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
pub enum ExecuteMsg {
|
||||
Announce { client_address: String, standard_whitelist: bool, owner: Addr },
|
||||
Delete { client_address: String },
|
||||
UpdateScore { client_address: String, new_score: i8 }
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
pub struct ServicesInfo {
|
||||
pub owner: String,
|
||||
pub services: Service,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
pub struct ServicesListResp {
|
||||
pub services: Vec<ServicesInfo>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
|
||||
pub struct ConfigResponse {
|
||||
pub updater_role: Addr,
|
||||
pub admin: Addr
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
use cosmwasm_std::Addr;
|
||||
use cw_storage_plus::{Item, Map};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Service {
|
||||
pub client_address: String,
|
||||
pub standard_whitelist: bool,
|
||||
pub uptime_score: u8,
|
||||
pub owner: Addr
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Config {
|
||||
pub updater_role: Addr,
|
||||
pub admin: Addr
|
||||
}
|
||||
|
||||
pub const CONFIG: Item<Config> = Item::new("config");
|
||||
pub const SERVICES: Map<String, Service> = Map::new("services");
|
||||
Reference in New Issue
Block a user