Skip to content

Commit

Permalink
refactor(service-registry): remove use of cw_multi_test::App from u…
Browse files Browse the repository at this point in the history
…nit tests and reorganize tests (#360)

* move some tests to unit tests

* move more tests to unit tests

* move unbonding test to unit test

* add claim stake integration test

* cleanup
  • Loading branch information
eguajardo authored Apr 22, 2024
1 parent 3cd5e43 commit 75ec41a
Show file tree
Hide file tree
Showing 11 changed files with 2,242 additions and 2,248 deletions.
2,071 changes: 1,822 additions & 249 deletions contracts/service-registry/src/contract.rs

Large diffs are not rendered by default.

216 changes: 216 additions & 0 deletions contracts/service-registry/src/contract/execute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
use crate::state;
use crate::state::{AuthorizationState, WORKERS, WORKERS_PER_CHAIN};
use connection_router_api::ChainName;

use super::*;

pub fn require_governance(deps: &DepsMut, info: MessageInfo) -> Result<(), ContractError> {
let config = CONFIG.load(deps.storage)?;
if config.governance != info.sender {
return Err(ContractError::Unauthorized);
}
Ok(())
}

#[allow(clippy::too_many_arguments)]
pub fn register_service(
deps: DepsMut,
service_name: String,
service_contract: Addr,
min_num_workers: u16,
max_num_workers: Option<u16>,
min_worker_bond: Uint128,
bond_denom: String,
unbonding_period_days: u16,
description: String,
) -> Result<Response, ContractError> {
let key = &service_name.clone();

SERVICES.update(
deps.storage,
key,
|service| -> Result<Service, ContractError> {
match service {
None => Ok(Service {
name: service_name,
service_contract,
min_num_workers,
max_num_workers,
min_worker_bond,
bond_denom,
unbonding_period_days,
description,
}),
_ => Err(ContractError::ServiceAlreadyExists),
}
},
)?;

// Response with attributes? event?
Ok(Response::new())
}

pub fn update_worker_authorization_status(
deps: DepsMut,
workers: Vec<Addr>,
service_name: String,
auth_state: AuthorizationState,
) -> Result<Response, ContractError> {
SERVICES
.may_load(deps.storage, &service_name)?
.ok_or(ContractError::ServiceNotFound)?;

for worker in workers {
WORKERS.update(
deps.storage,
(&service_name, &worker.clone()),
|sw| -> Result<Worker, ContractError> {
match sw {
Some(mut worker) => {
worker.authorization_state = auth_state.clone();
Ok(worker)
}
None => Ok(Worker {
address: worker,
bonding_state: BondingState::Unbonded,
authorization_state: auth_state.clone(),
service_name: service_name.clone(),
}),
}
},
)?;
}

Ok(Response::new())
}

pub fn bond_worker(
deps: DepsMut,
info: MessageInfo,
service_name: String,
) -> Result<Response, ContractError> {
let service = SERVICES
.may_load(deps.storage, &service_name)?
.ok_or(ContractError::ServiceNotFound)?;

let bond = if !info.funds.is_empty() {
info.funds
.iter()
.find(|coin| coin.denom == service.bond_denom)
.ok_or(ContractError::WrongDenom)?
.amount
} else {
Uint128::zero() // sender can rebond currently unbonding funds by just sending no new funds
};

WORKERS.update(
deps.storage,
(&service_name.clone(), &info.sender.clone()),
|sw| -> Result<Worker, ContractError> {
match sw {
Some(worker) => Ok(worker.add_bond(bond)?),
None => Ok(Worker {
address: info.sender,
bonding_state: BondingState::Bonded { amount: bond },
authorization_state: AuthorizationState::NotAuthorized,
service_name,
}),
}
},
)?;

Ok(Response::new())
}

pub fn register_chains_support(
deps: DepsMut,
info: MessageInfo,
service_name: String,
chains: Vec<ChainName>,
) -> Result<Response, ContractError> {
SERVICES
.may_load(deps.storage, &service_name)?
.ok_or(ContractError::ServiceNotFound)?;

WORKERS
.may_load(deps.storage, (&service_name, &info.sender))?
.ok_or(ContractError::WorkerNotFound)?;

let _res =
state::register_chains_support(deps.storage, service_name.clone(), chains, info.sender);

Ok(Response::new())
}

pub fn deregister_chains_support(
deps: DepsMut,
info: MessageInfo,
service_name: String,
chains: Vec<ChainName>,
) -> Result<Response, ContractError> {
SERVICES
.may_load(deps.storage, &service_name)?
.ok_or(ContractError::ServiceNotFound)?;

WORKERS
.may_load(deps.storage, (&service_name, &info.sender))?
.ok_or(ContractError::WorkerNotFound)?;

for chain in chains {
WORKERS_PER_CHAIN.remove(deps.storage, (&service_name, &chain, &info.sender));
}

Ok(Response::new())
}

pub fn unbond_worker(
deps: DepsMut,
env: Env,
info: MessageInfo,
service_name: String,
) -> Result<Response, ContractError> {
SERVICES
.may_load(deps.storage, &service_name)?
.ok_or(ContractError::ServiceNotFound)?;

let worker = WORKERS
.may_load(deps.storage, (&service_name, &info.sender))?
.ok_or(ContractError::WorkerNotFound)?;

let can_unbond = true; // TODO: actually query the service to determine this value

let worker = worker.unbond(can_unbond, env.block.time)?;

WORKERS.save(deps.storage, (&service_name, &info.sender), &worker)?;

Ok(Response::new())
}

pub fn claim_stake(
deps: DepsMut,
env: Env,
info: MessageInfo,
service_name: String,
) -> Result<Response, ContractError> {
let service = SERVICES
.may_load(deps.storage, &service_name)?
.ok_or(ContractError::ServiceNotFound)?;

let worker = WORKERS
.may_load(deps.storage, (&service_name, &info.sender))?
.ok_or(ContractError::WorkerNotFound)?;

let (worker, released_bond) =
worker.claim_stake(env.block.time, service.unbonding_period_days as u64)?;

WORKERS.save(deps.storage, (&service_name, &info.sender), &worker)?;

Ok(Response::new().add_message(BankMsg::Send {
to_address: info.sender.into(),
amount: [Coin {
denom: service.bond_denom,
amount: released_bond,
}]
.to_vec(), // TODO: isolate coins
}))
}
57 changes: 57 additions & 0 deletions contracts/service-registry/src/contract/query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use connection_router_api::ChainName;

use crate::state::{WeightedWorker, WORKERS, WORKERS_PER_CHAIN, WORKER_WEIGHT};

use super::*;

pub fn get_active_workers(
deps: Deps,
service_name: String,
chain_name: ChainName,
) -> Result<Vec<WeightedWorker>, ContractError> {
let service = SERVICES
.may_load(deps.storage, &service_name)?
.ok_or(ContractError::ServiceNotFound)?;

let workers: Vec<_> = WORKERS_PER_CHAIN
.prefix((&service_name, &chain_name))
.range(deps.storage, None, None, Order::Ascending)
.map(|res| res.and_then(|(addr, _)| WORKERS.load(deps.storage, (&service_name, &addr))))
.collect::<Result<Vec<Worker>, _>>()?
.into_iter()
.filter(|worker| match worker.bonding_state {
BondingState::Bonded { amount } => amount >= service.min_worker_bond,
_ => false,
})
.filter(|worker| worker.authorization_state == AuthorizationState::Authorized)
.map(|worker| WeightedWorker {
worker_info: worker,
weight: WORKER_WEIGHT, // all workers have an identical const weight for now
})
.collect();

if workers.len() < service.min_num_workers.into() {
Err(ContractError::NotEnoughWorkers)
} else {
Ok(workers)
}
}

pub fn get_worker(
deps: Deps,
service_name: String,
worker: String,
) -> Result<Worker, ContractError> {
WORKERS
.may_load(
deps.storage,
(&service_name, &deps.api.addr_validate(&worker)?),
)?
.ok_or(ContractError::WorkerNotFound)
}

pub fn get_service(deps: Deps, service_name: String) -> Result<Service, ContractError> {
SERVICES
.may_load(deps.storage, &service_name)?
.ok_or(ContractError::ServiceNotFound)
}
49 changes: 0 additions & 49 deletions contracts/service-registry/tests/test_utils/mod.rs

This file was deleted.

Loading

0 comments on commit 75ec41a

Please sign in to comment.