Usingn IndexedMap for mixnodes
This commit is contained in:
@@ -15,8 +15,10 @@ pub(crate) fn try_add_gateway(
|
||||
let sender_bytes = info.sender.as_bytes();
|
||||
|
||||
// if the client has an active bonded mixnode, don't allow gateway bonding
|
||||
if mixnodes_storage::mixnodes_owners_read(deps.storage)
|
||||
.may_load(sender_bytes)?
|
||||
if mixnodes_storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.storage, info.sender.to_string())?
|
||||
.is_some()
|
||||
{
|
||||
return Err(ContractError::AlreadyOwnsMixnode);
|
||||
|
||||
@@ -22,7 +22,7 @@ pub fn query_mixnodes_paged(
|
||||
|
||||
let start = start_after.map(Bound::exclusive);
|
||||
|
||||
let nodes = storage::MIXNODES
|
||||
let nodes = storage::mixnodes()
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| res.map(|item| item.1))
|
||||
@@ -43,8 +43,10 @@ pub fn query_mixnodes_paged(
|
||||
}
|
||||
|
||||
pub fn query_owns_mixnode(deps: Deps, address: Addr) -> StdResult<MixOwnershipResponse> {
|
||||
let has_node = storage::mixnodes_owners_read(deps.storage)
|
||||
.may_load(address.as_bytes())?
|
||||
let has_node = storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.storage, address.to_string())?
|
||||
.is_some();
|
||||
Ok(MixOwnershipResponse { address, has_node })
|
||||
}
|
||||
|
||||
@@ -28,15 +28,19 @@ pub(crate) fn try_add_mixnode(
|
||||
}
|
||||
|
||||
// if the client has an active bonded mixnode, regardless of its identity, don't allow bonding
|
||||
if storage::mixnodes_owners_read(deps.storage)
|
||||
.may_load(sender_bytes)?
|
||||
if storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.storage, info.sender.to_string())?
|
||||
.is_some()
|
||||
{
|
||||
return Err(ContractError::AlreadyOwnsMixnode);
|
||||
}
|
||||
|
||||
// check if somebody else has already bonded a mixnode with this identity
|
||||
if let Some(existing_bond) = storage::MIXNODES.may_load(deps.storage, &mix_node.identity_key)? {
|
||||
if let Some(existing_bond) =
|
||||
storage::mixnodes().may_load(deps.storage, mix_node.identity_key.as_bytes())?
|
||||
{
|
||||
if existing_bond.owner != info.sender {
|
||||
return Err(ContractError::DuplicateMixnode {
|
||||
owner: existing_bond.owner,
|
||||
@@ -62,13 +66,11 @@ pub(crate) fn try_add_mixnode(
|
||||
None,
|
||||
);
|
||||
|
||||
let identity = stored_bond.identity();
|
||||
|
||||
// technically we don't have to set the total_delegation bucket, but it makes things easier
|
||||
// in different places that we can guarantee that if node exists, so does the data behind the total delegation
|
||||
storage::MIXNODES.save(deps.storage, identity, &stored_bond)?;
|
||||
storage::mixnodes_owners(deps.storage).save(sender_bytes, identity)?;
|
||||
storage::total_delegation(deps.storage).save(identity.as_bytes(), &Uint128::zero())?;
|
||||
let identity = stored_bond.identity().as_bytes();
|
||||
storage::mixnodes().save(deps.storage, identity, &stored_bond)?;
|
||||
storage::total_delegation(deps.storage).save(identity, &Uint128::zero())?;
|
||||
mixnet_params_storage::increment_layer_count(deps.storage, stored_bond.layer)?;
|
||||
|
||||
Ok(Response::new())
|
||||
@@ -78,17 +80,16 @@ pub(crate) fn try_remove_mixnode(
|
||||
deps: DepsMut,
|
||||
info: MessageInfo,
|
||||
) -> Result<Response, ContractError> {
|
||||
let sender_bytes = info.sender.as_bytes();
|
||||
|
||||
// try to find the identity of the sender's node
|
||||
let mix_identity = match storage::mixnodes_owners_read(deps.storage).may_load(sender_bytes)? {
|
||||
Some(identity) => identity,
|
||||
// try to find the node of the sender
|
||||
let (entry_key, mixnode_bond) = match storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.storage, info.sender.to_string())?
|
||||
{
|
||||
Some(node) => (node.0, node.1),
|
||||
None => return Err(ContractError::NoAssociatedMixNodeBond { owner: info.sender }),
|
||||
};
|
||||
|
||||
// get the bond, since we found associated identity, the node MUST exist
|
||||
let mixnode_bond = storage::MIXNODES.load(deps.storage, &mix_identity)?;
|
||||
|
||||
// send bonded funds back to the bond owner
|
||||
let messages = vec![BankMsg::Send {
|
||||
to_address: info.sender.as_str().to_owned(),
|
||||
@@ -96,10 +97,9 @@ pub(crate) fn try_remove_mixnode(
|
||||
}
|
||||
.into()];
|
||||
|
||||
// remove the bond from the list of bonded mixnodes
|
||||
storage::MIXNODES.remove(deps.storage, &mix_identity);
|
||||
// remove the node ownership
|
||||
storage::mixnodes_owners(deps.storage).remove(sender_bytes);
|
||||
// remove the bond
|
||||
storage::mixnodes().remove(deps.storage, &entry_key)?;
|
||||
|
||||
// decrement layer count
|
||||
mixnet_params_storage::decrement_layer_count(deps.storage, mixnode_bond.layer)?;
|
||||
|
||||
@@ -303,8 +303,10 @@ pub mod tests {
|
||||
};
|
||||
|
||||
// before the execution the node had no associated owner
|
||||
assert!(storage::mixnodes_owners_read(deps.as_ref().storage)
|
||||
.may_load("myAwesomeMixnode".as_bytes())
|
||||
assert!(storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.as_ref().storage, "mix-owner".to_string())
|
||||
.unwrap()
|
||||
.is_none());
|
||||
|
||||
@@ -314,9 +316,14 @@ pub mod tests {
|
||||
|
||||
assert_eq!(
|
||||
"myAwesomeMixnode",
|
||||
storage::mixnodes_owners_read(deps.as_ref().storage)
|
||||
.load("mix-owner".as_bytes())
|
||||
storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.as_ref().storage, "mix-owner".to_string())
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.1
|
||||
.identity()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -513,9 +520,14 @@ pub mod tests {
|
||||
execute(deps.as_mut(), mock_env(), info, msg).unwrap();
|
||||
assert_eq!(
|
||||
"myAwesomeMixnode",
|
||||
storage::mixnodes_owners_read(deps.as_ref().storage)
|
||||
.load("mix-owner".as_bytes())
|
||||
storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.as_ref().storage, "mix-owner".to_string())
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.1
|
||||
.identity()
|
||||
);
|
||||
|
||||
let info = mock_info("mix-owner", &[]);
|
||||
@@ -523,8 +535,10 @@ pub mod tests {
|
||||
|
||||
assert!(execute(deps.as_mut(), mock_env(), info, msg).is_ok());
|
||||
|
||||
assert!(storage::mixnodes_owners_read(deps.as_ref().storage)
|
||||
.may_load("mix-owner".as_bytes())
|
||||
assert!(storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.as_ref().storage, "mix-owner".to_string())
|
||||
.unwrap()
|
||||
.is_none());
|
||||
|
||||
@@ -540,9 +554,14 @@ pub mod tests {
|
||||
assert!(execute(deps.as_mut(), mock_env(), info, msg).is_ok());
|
||||
assert_eq!(
|
||||
"myAwesomeMixnode",
|
||||
storage::mixnodes_owners_read(deps.as_ref().storage)
|
||||
.load("mix-owner".as_bytes())
|
||||
storage::mixnodes()
|
||||
.idx
|
||||
.owner
|
||||
.item(deps.as_ref().storage, "mix-owner".to_string())
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.1
|
||||
.identity()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,10 @@ pub(crate) fn try_delegate_to_mixnode(
|
||||
validate_delegation_stake(&info.funds)?;
|
||||
|
||||
// check if the target node actually exists
|
||||
if storage::MIXNODES.load(deps.storage, &mix_identity).is_err() {
|
||||
if storage::mixnodes()
|
||||
.load(deps.storage, mix_identity.as_bytes())
|
||||
.is_err()
|
||||
{
|
||||
return Err(ContractError::MixNodeBondNotFound {
|
||||
identity: mix_identity,
|
||||
});
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use config::defaults::DENOM;
|
||||
use cosmwasm_std::{StdResult, Storage, Uint128};
|
||||
use cosmwasm_storage::{bucket, bucket_read, Bucket, ReadonlyBucket};
|
||||
use cw_storage_plus::Map;
|
||||
use cw_storage_plus::{Index, IndexList, IndexedMap, UniqueIndex};
|
||||
use mixnet_contract::{
|
||||
Addr, Coin, IdentityKey, IdentityKeyRef, Layer, MixNode, MixNodeBond, RawDelegationData,
|
||||
RewardingStatus,
|
||||
@@ -15,7 +15,7 @@ use std::fmt::{Display, Formatter};
|
||||
|
||||
// storage prefixes
|
||||
// const PREFIX_MIXNODES: &[u8] = b"mn";
|
||||
const PREFIX_MIXNODES_OWNERS: &[u8] = b"mo";
|
||||
// const PREFIX_MIXNODES_OWNERS: &[u8] = b"mo";
|
||||
const PREFIX_MIX_DELEGATION: &[u8] = b"md";
|
||||
const PREFIX_REVERSE_MIX_DELEGATION: &[u8] = b"dm";
|
||||
pub const PREFIX_REWARDED_MIXNODES: &[u8] = b"rm";
|
||||
@@ -29,9 +29,32 @@ pub(crate) const BOND_PAGE_DEFAULT_LIMIT: u32 = 50;
|
||||
|
||||
const PREFIX_TOTAL_DELEGATION: &[u8] = b"td";
|
||||
|
||||
pub(crate) const MIXNODES: Map<IdentityKeyRef, StoredMixnodeBond> = Map::new("mn");
|
||||
pub(crate) struct MixnodeBondIndex<'a> {
|
||||
pub(crate) identity: UniqueIndex<'a, IdentityKey, StoredMixnodeBond>,
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
// somehow PrimaryKey is not implemented for Addr but is for String?
|
||||
// maybe it's an omission in this version and is fixed in the cosmwasm 1.0 compatible release?
|
||||
pub(crate) owner: UniqueIndex<'a, String, StoredMixnodeBond>,
|
||||
}
|
||||
|
||||
// IndexList is just boilerplate code for fetching a struct's indexes
|
||||
impl<'a> IndexList<StoredMixnodeBond> for MixnodeBondIndex<'a> {
|
||||
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<StoredMixnodeBond>> + '_> {
|
||||
let v: Vec<&dyn Index<StoredMixnodeBond>> = vec![&self.identity, &self.owner];
|
||||
Box::new(v.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
// mixnodes() is the storage access function.
|
||||
pub(crate) fn mixnodes<'a>() -> IndexedMap<'a, &'a [u8], StoredMixnodeBond, MixnodeBondIndex<'a>> {
|
||||
let indexes = MixnodeBondIndex {
|
||||
identity: UniqueIndex::new(|d| d.mix_node.identity_key.clone(), "mni"),
|
||||
owner: UniqueIndex::new(|d| d.owner.to_string(), "mno"),
|
||||
};
|
||||
IndexedMap::new("mn", indexes)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
pub(crate) struct StoredMixnodeBond {
|
||||
pub bond_amount: Coin,
|
||||
pub owner: Addr,
|
||||
@@ -96,15 +119,6 @@ impl Display for StoredMixnodeBond {
|
||||
|
||||
// Mixnode-related stuff
|
||||
|
||||
// owner address -> node identity
|
||||
pub fn mixnodes_owners(storage: &mut dyn Storage) -> Bucket<IdentityKey> {
|
||||
bucket(storage, PREFIX_MIXNODES_OWNERS)
|
||||
}
|
||||
|
||||
pub fn mixnodes_owners_read(storage: &dyn Storage) -> ReadonlyBucket<IdentityKey> {
|
||||
bucket_read(storage, PREFIX_MIXNODES_OWNERS)
|
||||
}
|
||||
|
||||
pub fn total_delegation(storage: &mut dyn Storage) -> Bucket<Uint128> {
|
||||
bucket(storage, PREFIX_TOTAL_DELEGATION)
|
||||
}
|
||||
@@ -146,7 +160,7 @@ pub(crate) fn read_mixnode_bond(
|
||||
storage: &dyn Storage,
|
||||
mix_identity: IdentityKeyRef,
|
||||
) -> StdResult<Option<MixNodeBond>> {
|
||||
let stored_bond = MIXNODES.may_load(storage, mix_identity)?;
|
||||
let stored_bond = mixnodes().may_load(storage, mix_identity.as_bytes())?;
|
||||
match stored_bond {
|
||||
None => Ok(None),
|
||||
Some(stored_bond) => {
|
||||
@@ -218,13 +232,13 @@ mod tests {
|
||||
#[test]
|
||||
fn mixnode_single_read_retrieval() {
|
||||
let mut storage = MockStorage::new();
|
||||
let bond1 = test_helpers::stored_mixnode_bond_fixture();
|
||||
let bond2 = test_helpers::stored_mixnode_bond_fixture();
|
||||
MIXNODES.save(&mut storage, "bond1", &bond1).unwrap();
|
||||
MIXNODES.save(&mut storage, "bond2", &bond2).unwrap();
|
||||
let bond1 = test_helpers::stored_mixnode_bond_fixture("owner1");
|
||||
let bond2 = test_helpers::stored_mixnode_bond_fixture("owner2");
|
||||
mixnodes().save(&mut storage, b"bond1", &bond1).unwrap();
|
||||
mixnodes().save(&mut storage, b"bond2", &bond2).unwrap();
|
||||
|
||||
let res1 = MIXNODES.load(&storage, "bond1").unwrap();
|
||||
let res2 = MIXNODES.load(&storage, "bond2").unwrap();
|
||||
let res1 = mixnodes().load(&storage, b"bond1").unwrap();
|
||||
let res2 = mixnodes().load(&storage, b"bond2").unwrap();
|
||||
assert_eq!(bond1, res1);
|
||||
assert_eq!(bond2, res2);
|
||||
}
|
||||
|
||||
@@ -341,9 +341,9 @@ pub(crate) fn try_reward_mixnode_v2(
|
||||
Ok(current_total.unwrap() + delegation_rewarding_result.total_rewarded)
|
||||
},
|
||||
)?;
|
||||
mixnodes_storage::MIXNODES.update::<_, ContractError>(
|
||||
mixnodes_storage::mixnodes().update::<_, ContractError>(
|
||||
deps.storage,
|
||||
&mix_identity,
|
||||
mix_identity.as_bytes(),
|
||||
|current_bond| {
|
||||
// unwrap is fine because we just read the entry...
|
||||
let mut unwrapped = current_bond.unwrap();
|
||||
@@ -1032,8 +1032,12 @@ pub mod tests {
|
||||
profit_margin_percent: Some(10),
|
||||
};
|
||||
|
||||
mixnodes_storage::MIXNODES
|
||||
.save(deps.as_mut().storage, &node_identity, &mixnode_bond)
|
||||
mixnodes_storage::mixnodes()
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
node_identity.as_bytes(),
|
||||
&mixnode_bond,
|
||||
)
|
||||
.unwrap();
|
||||
mixnodes_storage::total_delegation(deps.as_mut().storage)
|
||||
.save(node_identity.as_bytes(), &Uint128::new(initial_delegation))
|
||||
|
||||
@@ -139,13 +139,16 @@ pub mod test_helpers {
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn stored_mixnode_bond_fixture() -> mixnodes_storage::StoredMixnodeBond {
|
||||
pub(crate) fn stored_mixnode_bond_fixture(owner: &str) -> mixnodes_storage::StoredMixnodeBond {
|
||||
StoredMixnodeBond::new(
|
||||
coin(50, DENOM),
|
||||
Addr::unchecked("foo"),
|
||||
Addr::unchecked(owner),
|
||||
Layer::One,
|
||||
12_345,
|
||||
mix_node_fixture(),
|
||||
MixNode {
|
||||
identity_key: format!("id-{}", owner),
|
||||
..mix_node_fixture()
|
||||
},
|
||||
None,
|
||||
)
|
||||
}
|
||||
@@ -229,7 +232,7 @@ pub mod test_helpers {
|
||||
storage: &dyn Storage,
|
||||
identity: IdentityKeyRef,
|
||||
) -> StdResult<cosmwasm_std::Uint128> {
|
||||
let node = mixnodes_storage::MIXNODES.load(storage, identity)?;
|
||||
let node = mixnodes_storage::mixnodes().load(storage, identity.as_bytes())?;
|
||||
Ok(node.bond_amount.amount)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user