Merge pull request #4603 from nymtech/jon/purge-name-service-and-sp-dir-contracts

Purge name service and service provider directory contracts
This commit is contained in:
Jon Häggblad
2024-06-11 11:50:13 +02:00
committed by GitHub
143 changed files with 11 additions and 13074 deletions
@@ -58,8 +58,6 @@ jobs:
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_service_provider_directory.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_name_service.wasm $OUTPUT_DIR
- name: Deploy branch to CI www
continue-on-error: true
Generated
-33
View File
@@ -3898,11 +3898,9 @@ dependencies = [
"nym-inclusion-probability",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
"nym-name-service-common",
"nym-node-requests",
"nym-node-tester-utils",
"nym-pemstore",
"nym-service-provider-directory-common",
"nym-sphinx",
"nym-task",
"nym-topology",
@@ -4081,10 +4079,8 @@ dependencies = [
"nym-id",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
"nym-name-service-common",
"nym-network-defaults",
"nym-pemstore",
"nym-service-provider-directory-common",
"nym-sphinx",
"nym-types",
"nym-validator-client",
@@ -4907,20 +4903,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "nym-name-service-common"
version = "0.1.0"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
"cw-controllers",
"cw-utils",
"cw2",
"nym-contracts-common",
"serde",
"thiserror",
]
[[package]]
name = "nym-network-defaults"
version = "0.1.0"
@@ -5250,19 +5232,6 @@ dependencies = [
"url",
]
[[package]]
name = "nym-service-provider-directory-common"
version = "0.1.0"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
"cw-controllers",
"cw-utils",
"cw2",
"nym-contracts-common",
"thiserror",
]
[[package]]
name = "nym-service-providers-common"
version = "0.1.0"
@@ -5686,9 +5655,7 @@ dependencies = [
"nym-http-api-client",
"nym-mixnet-contract-common",
"nym-multisig-contract-common",
"nym-name-service-common",
"nym-network-defaults",
"nym-service-provider-directory-common",
"nym-vesting-contract-common",
"prost 0.12.4",
"reqwest 0.12.4",
-2
View File
@@ -39,8 +39,6 @@ members = [
"common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/multisig-contract",
"common/cosmwasm-smart-contracts/name-service",
"common/cosmwasm-smart-contracts/service-provider-directory",
"common/cosmwasm-smart-contracts/vesting-contract",
"common/country-group",
"common/credential-storage",
+2 -2
View File
@@ -134,7 +134,7 @@ clippy: sdk-wasm-lint
# Build contracts ready for deploy
# -----------------------------------------------------------------------------
CONTRACTS=vesting_contract mixnet_contract nym_service_provider_directory nym_name_service
CONTRACTS=vesting_contract mixnet_contract
CONTRACTS_WASM=$(addsuffix .wasm, $(CONTRACTS))
CONTRACTS_OUT_DIR=contracts/target/wasm32-unknown-unknown/release
@@ -186,4 +186,4 @@ deb-gateway: build-nym-gateway
deb-cli: build-nym-cli
cargo deb -p nym-cli
deb: deb-mixnode deb-gateway deb-cli
deb: deb-mixnode deb-gateway deb-cli
@@ -19,9 +19,7 @@ nym-mixnet-contract-common = { path = "../../cosmwasm-smart-contracts/mixnet-con
nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-contract" }
nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
nym-name-service-common = { path = "../../cosmwasm-smart-contracts/name-service" }
nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
nym-service-provider-directory-common = { path = "../../cosmwasm-smart-contracts/service-provider-directory" }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
nym-http-api-client = { path = "../../../common/http-api-client"}
@@ -86,14 +84,6 @@ name = "offline_signing"
# (traits would need to be moved around and refactored themselves)
required-features = ["http-client"]
[[example]]
name = "query_service_provider_directory"
required-features = ["http-client"]
[[example]]
name = "query_name_service"
required-features = ["http-client"]
[features]
default = ["http-client"]
http-client = ["cosmrs/rpc"]
@@ -1,37 +0,0 @@
use std::str::FromStr;
use cosmrs::AccountId;
use nym_name_service_common::Address;
use nym_network_defaults::{setup_env, NymNetworkDetails};
use nym_validator_client::nyxd::contract_traits::{
NameServiceQueryClient, PagedNameServiceQueryClient,
};
#[tokio::main]
async fn main() {
setup_env(Some("../../../envs/qa.env"));
let network_details = NymNetworkDetails::new_from_env();
let config =
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
let client = nym_validator_client::Client::new_query(config).unwrap();
let config = client.nyxd.get_name_service_config().await.unwrap();
println!("config: {config:?}");
let names_paged = client.nyxd.get_names_paged(None, None).await.unwrap();
println!("names (paged): {names_paged:#?}");
let names = client.nyxd.get_all_names().await.unwrap();
println!("names: {names:#?}");
let owner = AccountId::from_str("n1hmf957kc7arcd39rl7xq8l0a4zyg7kxnv7su87").unwrap();
let names_by_owner = client.nyxd.get_names_by_owner(owner).await.unwrap();
println!("names (by owner): {names_by_owner:#?}");
let nym_address = Address::new("client_id.client_key@gateway_id").unwrap();
let names_by_address = client.nyxd.get_names_by_address(nym_address).await.unwrap();
println!("names (by address): {names_by_address:#?}");
let service_info = client.nyxd.get_name_entry(1).await;
println!("service info: {service_info:#?}");
}
@@ -1,45 +0,0 @@
use std::str::FromStr;
use cosmrs::AccountId;
use nym_network_defaults::{setup_env, NymNetworkDetails};
use nym_service_provider_directory_common::NymAddress;
use nym_validator_client::nyxd::contract_traits::{
PagedSpDirectoryQueryClient, SpDirectoryQueryClient,
};
#[tokio::main]
async fn main() {
setup_env(Some("../../../envs/qa.env"));
let network_details = NymNetworkDetails::new_from_env();
let config =
nym_validator_client::Config::try_from_nym_network_details(&network_details).unwrap();
let client = nym_validator_client::Client::new_query(config).unwrap();
let config = client.nyxd.get_service_config().await.unwrap();
println!("config: {config:?}");
let services_paged = client.nyxd.get_services_paged(None, None).await.unwrap();
println!("services (paged): {services_paged:#?}");
let services = client.nyxd.get_all_services().await.unwrap();
println!("services: {services:#?}");
let announcer = AccountId::from_str("n1hmf957kc7arcd39rl7xq8l0a4zyg7kxnv7su87").unwrap();
let services_by_announcer = client
.nyxd
.get_services_by_announcer(announcer)
.await
.unwrap();
println!("services (by announcer): {services_by_announcer:#?}");
let nym_address = NymAddress::new("foo.bar@gateway");
let services_by_nym_address = client
.nyxd
.get_services_by_nym_address(nym_address)
.await
.unwrap();
assert_eq!(services_by_announcer, services_by_nym_address);
let service_info = client.nyxd.get_service_info(1).await;
println!("service info: {service_info:#?}");
}
@@ -25,8 +25,6 @@ pub use nym_coconut_dkg_common::types::EpochId;
use nym_http_api_client::{ApiClient, NO_PARAMS};
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
use nym_name_service_common::response::NamesListResponse;
use nym_service_provider_directory_common::response::ServicesListResponse;
pub mod error;
pub mod routes;
@@ -492,19 +490,6 @@ pub trait NymApiClientExt: ApiClient {
)
.await
}
async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
log::trace!("Getting service providers");
self.get_json(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
.await
}
//async fn get_registered_names(&self) -> Result<Vec<NameEntry>, NymAPIError> {
async fn get_registered_names(&self) -> Result<NamesListResponse, NymAPIError> {
log::trace!("Getting registered names");
self.get_json(&[routes::API_VERSION, routes::REGISTERED_NAMES], NO_PARAMS)
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -40,4 +40,3 @@ pub const STAKE_SATURATION: &str = "stake-saturation";
pub const INCLUSION_CHANCE: &str = "inclusion-probability";
pub const SERVICE_PROVIDERS: &str = "services";
pub const REGISTERED_NAMES: &str = "names";
@@ -14,8 +14,6 @@ pub mod ephemera_query_client;
pub mod group_query_client;
pub mod mixnet_query_client;
pub mod multisig_query_client;
pub mod name_service_query_client;
pub mod sp_directory_query_client;
pub mod vesting_query_client;
// signing clients
@@ -25,8 +23,6 @@ pub mod ephemera_signing_client;
pub mod group_signing_client;
pub mod mixnet_signing_client;
pub mod multisig_signing_client;
pub mod name_service_signing_client;
pub mod sp_directory_signing_client;
pub mod vesting_signing_client;
// re-export query traits
@@ -38,8 +34,6 @@ pub use ephemera_query_client::{EphemeraQueryClient, PagedEphemeraQueryClient};
pub use group_query_client::{GroupQueryClient, PagedGroupQueryClient};
pub use mixnet_query_client::{MixnetQueryClient, PagedMixnetQueryClient};
pub use multisig_query_client::{MultisigQueryClient, PagedMultisigQueryClient};
pub use name_service_query_client::{NameServiceQueryClient, PagedNameServiceQueryClient};
pub use sp_directory_query_client::{PagedSpDirectoryQueryClient, SpDirectoryQueryClient};
pub use vesting_query_client::{PagedVestingQueryClient, VestingQueryClient};
// re-export signing traits
@@ -49,8 +43,6 @@ pub use ephemera_signing_client::EphemeraSigningClient;
pub use group_signing_client::GroupSigningClient;
pub use mixnet_signing_client::MixnetSigningClient;
pub use multisig_signing_client::MultisigSigningClient;
pub use name_service_signing_client::NameServiceSigningClient;
pub use sp_directory_signing_client::SpDirectorySigningClient;
pub use vesting_signing_client::VestingSigningClient;
// helper for providing blanket implementation for query clients
@@ -67,10 +59,6 @@ pub trait NymContractsProvider {
// ephemera-related
fn ephemera_contract_address(&self) -> Option<&AccountId>;
// SPs
fn name_service_contract_address(&self) -> Option<&AccountId>;
fn service_provider_contract_address(&self) -> Option<&AccountId>;
}
#[derive(Debug, Clone)]
@@ -84,9 +72,6 @@ pub struct TypedNymContracts {
pub coconut_dkg_contract_address: Option<AccountId>,
pub ephemera_contract_address: Option<AccountId>,
pub service_provider_directory_contract_address: Option<AccountId>,
pub name_service_contract_address: Option<AccountId>,
}
impl TryFrom<NymContracts> for TypedNymContracts {
@@ -122,14 +107,6 @@ impl TryFrom<NymContracts> for TypedNymContracts {
.ephemera_contract_address
.map(|addr| addr.parse())
.transpose()?,
service_provider_directory_contract_address: value
.service_provider_directory_contract_address
.map(|addr| addr.parse())
.transpose()?,
name_service_contract_address: value
.name_service_contract_address
.map(|addr| addr.parse())
.transpose()?,
})
}
}
@@ -1,144 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::collect_paged;
use crate::nyxd::contract_traits::NymContractsProvider;
use crate::nyxd::{error::NyxdError, CosmWasmClient};
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
use nym_name_service_common::{
msg::QueryMsg as NameQueryMsg,
response::{ConfigResponse, NamesListResponse, PagedNamesListResponse},
Address, NameId, NymName, RegisteredName,
};
use serde::Deserialize;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait NameServiceQueryClient {
async fn query_name_service_contract<T>(&self, query: NameQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn get_name_service_config(&self) -> Result<ConfigResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::Config {})
.await
}
async fn get_name_entry(&self, name_id: NameId) -> Result<RegisteredName, NyxdError> {
self.query_name_service_contract(NameQueryMsg::NameId { name_id })
.await
}
async fn get_names_paged(
&self,
start_after: Option<NameId>,
limit: Option<u32>,
) -> Result<PagedNamesListResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::All { limit, start_after })
.await
}
async fn get_name_signing_nonce(&self, address: &AccountId) -> Result<Nonce, NyxdError> {
self.query_name_service_contract(NameQueryMsg::SigningNonce {
address: address.to_string(),
})
.await
}
async fn get_names_by_owner(&self, owner: AccountId) -> Result<NamesListResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::ByOwner {
owner: owner.to_string(),
})
.await
}
async fn get_names_by_nym_name(&self, name: NymName) -> Result<NamesListResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::ByName { name })
.await
}
async fn get_names_by_address(&self, address: Address) -> Result<NamesListResponse, NyxdError> {
self.query_name_service_contract(NameQueryMsg::ByAddress { address })
.await
}
async fn get_name_service_contract_version(
&self,
) -> Result<ContractBuildInformation, NyxdError> {
self.query_name_service_contract(NameQueryMsg::GetContractVersion {})
.await
}
async fn get_name_service_contract_cw2_version(
&self,
) -> Result<cw2::ContractVersion, NyxdError> {
self.query_name_service_contract(NameQueryMsg::GetCW2ContractVersion {})
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait PagedNameServiceQueryClient: NameServiceQueryClient {
async fn get_all_names(&self) -> Result<Vec<RegisteredName>, NyxdError> {
collect_paged!(self, get_names_paged, names)
}
}
#[async_trait]
impl<T> PagedNameServiceQueryClient for T where T: NameServiceQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> NameServiceQueryClient for C
where
C: CosmWasmClient + NymContractsProvider + Send + Sync,
{
async fn query_name_service_contract<T>(&self, query: NameQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>,
{
let name_service_contract_address = &self
.name_service_contract_address()
.ok_or_else(|| NyxdError::unavailable_contract_address("name service contract"))?;
self.query_contract_smart(name_service_contract_address, &query)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nyxd::contract_traits::tests::IgnoreValue;
// it's enough that this compiles and clippy is happy about it
#[allow(dead_code)]
fn all_query_variants_are_covered<C: NameServiceQueryClient + Send + Sync>(
client: C,
msg: NameQueryMsg,
) {
match msg {
NameQueryMsg::NameId { name_id } => client.get_name_entry(name_id).ignore(),
NameQueryMsg::ByOwner { owner } => {
client.get_names_by_owner(owner.parse().unwrap()).ignore()
}
NameQueryMsg::ByName { name } => client.get_names_by_nym_name(name).ignore(),
NameQueryMsg::ByAddress { address } => client.get_names_by_address(address).ignore(),
NameQueryMsg::All { limit, start_after } => {
client.get_names_paged(limit, start_after).ignore()
}
NameQueryMsg::SigningNonce { address } => client
.get_name_signing_nonce(&address.parse().unwrap())
.ignore(),
NameQueryMsg::Config {} => client.get_name_service_config().ignore(),
NameQueryMsg::GetContractVersion {} => {
client.get_name_service_contract_version().ignore()
}
NameQueryMsg::GetCW2ContractVersion {} => {
client.get_name_service_contract_cw2_version().ignore()
}
};
}
}
@@ -1,138 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use nym_contracts_common::signing::MessageSignature;
use nym_name_service_common::{msg::ExecuteMsg as NameExecuteMsg, NameDetails, NameId, NymName};
use crate::nyxd::contract_traits::NymContractsProvider;
use crate::nyxd::{
coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, SigningCosmWasmClient,
};
use crate::signing::signer::OfflineSigner;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait NameServiceSigningClient {
async fn execute_name_service_contract(
&self,
fee: Option<Fee>,
msg: NameExecuteMsg,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn register_name(
&self,
name: NameDetails,
owner_signature: MessageSignature,
deposit: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_name_service_contract(
fee,
NameExecuteMsg::Register {
name,
owner_signature,
},
vec![deposit],
)
.await
}
async fn delete_name_by_id(
&self,
name_id: NameId,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_name_service_contract(fee, NameExecuteMsg::DeleteId { name_id }, vec![])
.await
}
async fn delete_service_provider_by_name(
&self,
name: NymName,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_name_service_contract(fee, NameExecuteMsg::DeleteName { name }, vec![])
.await
}
async fn update_deposit_required(
&self,
deposit_required: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_name_service_contract(
fee,
NameExecuteMsg::UpdateDepositRequired {
deposit_required: deposit_required.into(),
},
vec![],
)
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> NameServiceSigningClient for C
where
C: SigningCosmWasmClient + NymContractsProvider + Sync,
NyxdError: From<<Self as OfflineSigner>::Error>,
{
async fn execute_name_service_contract(
&self,
fee: Option<Fee>,
msg: NameExecuteMsg,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError> {
let name_service_contract_address = &self
.name_service_contract_address()
.ok_or_else(|| NyxdError::unavailable_contract_address("name service contract"))?;
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
let memo = msg.default_memo();
let signer_address = &self.signer_addresses()?[0];
self.execute(
signer_address,
name_service_contract_address,
&msg,
fee,
memo,
funds,
)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue};
// it's enough that this compiles and clippy is happy about it
#[allow(dead_code)]
fn all_execute_variants_are_covered<C: NameServiceSigningClient + Send + Sync>(
client: C,
msg: NameExecuteMsg,
) {
match msg {
NameExecuteMsg::Register {
name,
owner_signature,
} => client
.register_name(name, owner_signature, mock_coin(), None)
.ignore(),
NameExecuteMsg::DeleteId { name_id } => {
client.delete_name_by_id(name_id, None).ignore()
}
NameExecuteMsg::DeleteName { name } => {
client.delete_service_provider_by_name(name, None).ignore()
}
NameExecuteMsg::UpdateDepositRequired { deposit_required } => client
.update_deposit_required(deposit_required.into(), None)
.ignore(),
};
}
}
@@ -1,142 +0,0 @@
use crate::collect_paged;
use async_trait::async_trait;
use cosmrs::AccountId;
use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
use nym_service_provider_directory_common::{
msg::QueryMsg as SpQueryMsg,
response::{
ConfigResponse, PagedServicesListResponse, ServiceInfoResponse, ServicesListResponse,
},
NymAddress, Service, ServiceId,
};
use serde::Deserialize;
use crate::nyxd::contract_traits::NymContractsProvider;
use crate::nyxd::{error::NyxdError, CosmWasmClient};
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait SpDirectoryQueryClient {
async fn query_service_provider_contract<T>(&self, query: SpQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>;
async fn get_service_config(&self) -> Result<ConfigResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::Config {})
.await
}
async fn get_service_info(
&self,
service_id: ServiceId,
) -> Result<ServiceInfoResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::ServiceId { service_id })
.await
}
async fn get_services_paged(
&self,
start_after: Option<ServiceId>,
limit: Option<u32>,
) -> Result<PagedServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::All { limit, start_after })
.await
}
async fn get_services_by_announcer(
&self,
announcer: AccountId,
) -> Result<ServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::ByAnnouncer {
announcer: announcer.to_string(),
})
.await
}
async fn get_services_by_nym_address(
&self,
nym_address: NymAddress,
) -> Result<ServicesListResponse, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::ByNymAddress { nym_address })
.await
}
async fn get_sp_contract_version(&self) -> Result<ContractBuildInformation, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::GetContractVersion {})
.await
}
async fn get_sp_contract_cw2_version(&self) -> Result<cw2::ContractVersion, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::GetCW2ContractVersion {})
.await
}
async fn get_service_signing_nonce(&self, address: &AccountId) -> Result<Nonce, NyxdError> {
self.query_service_provider_contract(SpQueryMsg::SigningNonce {
address: address.to_string(),
})
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait PagedSpDirectoryQueryClient: SpDirectoryQueryClient {
async fn get_all_services(&self) -> Result<Vec<Service>, NyxdError> {
collect_paged!(self, get_services_paged, services)
}
}
#[async_trait]
impl<T> PagedSpDirectoryQueryClient for T where T: SpDirectoryQueryClient {}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> SpDirectoryQueryClient for C
where
C: CosmWasmClient + NymContractsProvider + Send + Sync,
{
async fn query_service_provider_contract<T>(&self, query: SpQueryMsg) -> Result<T, NyxdError>
where
for<'a> T: Deserialize<'a>,
{
let sp_directory_contract_address =
&self.service_provider_contract_address().ok_or_else(|| {
NyxdError::unavailable_contract_address("service provider directory contract")
})?;
self.query_contract_smart(sp_directory_contract_address, &query)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nyxd::contract_traits::tests::IgnoreValue;
// it's enough that this compiles and clippy is happy about it
#[allow(dead_code)]
fn all_query_variants_are_covered<C: SpDirectoryQueryClient + Send + Sync>(
client: C,
msg: SpQueryMsg,
) {
match msg {
SpQueryMsg::ServiceId { service_id } => client.get_service_info(service_id).ignore(),
SpQueryMsg::ByAnnouncer { announcer } => client
.get_services_by_announcer(announcer.parse().unwrap())
.ignore(),
SpQueryMsg::ByNymAddress { nym_address } => {
client.get_services_by_nym_address(nym_address).ignore()
}
SpQueryMsg::All { limit, start_after } => {
client.get_services_paged(start_after, limit).ignore()
}
SpQueryMsg::SigningNonce { address } => client
.get_service_signing_nonce(&address.parse().unwrap())
.ignore(),
SpQueryMsg::Config {} => client.get_service_config().ignore(),
SpQueryMsg::GetContractVersion {} => client.get_sp_contract_version().ignore(),
SpQueryMsg::GetCW2ContractVersion {} => client.get_sp_contract_cw2_version().ignore(),
};
}
}
@@ -1,148 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::nyxd::contract_traits::NymContractsProvider;
use crate::nyxd::{
coin::Coin, cosmwasm_client::types::ExecuteResult, error::NyxdError, Fee, SigningCosmWasmClient,
};
use crate::signing::signer::OfflineSigner;
use async_trait::async_trait;
use nym_contracts_common::signing::MessageSignature;
use nym_service_provider_directory_common::{
msg::ExecuteMsg as SpExecuteMsg, NymAddress, ServiceDetails, ServiceId,
};
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait SpDirectorySigningClient {
async fn execute_service_provider_directory_contract(
&self,
fee: Option<Fee>,
msg: SpExecuteMsg,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn announce_service_provider(
&self,
service: ServiceDetails,
owner_signature: MessageSignature,
deposit: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_service_provider_directory_contract(
fee,
SpExecuteMsg::Announce {
service,
owner_signature,
},
vec![deposit],
)
.await
}
async fn delete_service_provider_by_id(
&self,
service_id: ServiceId,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_service_provider_directory_contract(
fee,
SpExecuteMsg::DeleteId { service_id },
vec![],
)
.await
}
async fn delete_service_provider_by_nym_address(
&self,
nym_address: NymAddress,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_service_provider_directory_contract(
fee,
SpExecuteMsg::DeleteNymAddress { nym_address },
vec![],
)
.await
}
async fn update_deposit_required(
&self,
deposit_required: Coin,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
self.execute_service_provider_directory_contract(
fee,
SpExecuteMsg::UpdateDepositRequired {
deposit_required: deposit_required.into(),
},
vec![],
)
.await
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl<C> SpDirectorySigningClient for C
where
C: SigningCosmWasmClient + NymContractsProvider + Sync,
NyxdError: From<<Self as OfflineSigner>::Error>,
{
async fn execute_service_provider_directory_contract(
&self,
fee: Option<Fee>,
msg: SpExecuteMsg,
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError> {
let sp_directory_contract_address =
&self.service_provider_contract_address().ok_or_else(|| {
NyxdError::unavailable_contract_address("service provider directory contract")
})?;
let fee = fee.unwrap_or(Fee::Auto(Some(self.simulated_gas_multiplier())));
let memo = msg.default_memo();
let signer_address = &self.signer_addresses()?[0];
self.execute(
signer_address,
sp_directory_contract_address,
&msg,
fee,
memo,
funds,
)
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue};
// it's enough that this compiles and clippy is happy about it
#[allow(dead_code)]
fn all_execute_variants_are_covered<C: SpDirectorySigningClient + Send + Sync>(
client: C,
msg: SpExecuteMsg,
) {
match msg {
SpExecuteMsg::Announce {
service,
owner_signature,
} => client
.announce_service_provider(service, owner_signature, mock_coin(), None)
.ignore(),
SpExecuteMsg::DeleteId { service_id } => client
.delete_service_provider_by_id(service_id, None)
.ignore(),
SpExecuteMsg::DeleteNymAddress { nym_address } => client
.delete_service_provider_by_nym_address(nym_address, None)
.ignore(),
SpExecuteMsg::UpdateDepositRequired { deposit_required } => client
.update_deposit_required(deposit_required.into(), None)
.ignore(),
};
}
}
@@ -246,12 +246,6 @@ impl<C, S> NyxdClient<C, S> {
self.config.contracts.multisig_contract_address = Some(address);
}
pub fn set_service_provider_contract_address(&mut self, address: AccountId) {
self.config
.contracts
.service_provider_directory_contract_address = Some(address);
}
pub fn set_simulated_gas_multiplier(&mut self, multiplier: f32) {
self.config.simulated_gas_multiplier = multiplier;
}
@@ -288,17 +282,6 @@ impl<C, S> NymContractsProvider for NyxdClient<C, S> {
fn ephemera_contract_address(&self) -> Option<&AccountId> {
self.config.contracts.ephemera_contract_address.as_ref()
}
fn name_service_contract_address(&self) -> Option<&AccountId> {
self.config.contracts.name_service_contract_address.as_ref()
}
fn service_provider_contract_address(&self) -> Option<&AccountId> {
self.config
.contracts
.service_provider_directory_contract_address
.as_ref()
}
}
// queries
-2
View File
@@ -46,8 +46,6 @@ nym-vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-cont
nym-coconut-bandwidth-contract-common = { path = "../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-coconut-dkg-common = { path = "../cosmwasm-smart-contracts/coconut-dkg" }
nym-multisig-contract-common = { path = "../cosmwasm-smart-contracts/multisig-contract" }
nym-service-provider-directory-common = { path = "../cosmwasm-smart-contracts/service-provider-directory" }
nym-name-service-common = { path = "../cosmwasm-smart-contracts/name-service" }
nym-sphinx = { path = "../../common/nymsphinx" }
nym-client-core = { path = "../../common/client-core" }
nym-config = { path = "../../common/config" }
@@ -6,8 +6,6 @@ use clap::{Args, Subcommand};
pub mod gateway;
pub mod identity_key;
pub mod mixnode;
pub mod name;
pub mod service;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
@@ -23,10 +21,6 @@ pub enum MixnetOperatorsCommands {
Mixnode(mixnode::MixnetOperatorsMixnode),
/// Manage your gateway
Gateway(gateway::MixnetOperatorsGateway),
/// Manage your service
ServiceProvider(service::MixnetOperatorsService),
/// Manage your registered name
Name(name::MixnetOperatorsName),
/// Sign messages using your private identity key
IdentityKey(identity_key::MixnetOperatorsIdentityKey),
}
@@ -1,25 +0,0 @@
use clap::Parser;
use log::{error, info};
use nym_name_service_common::NameId;
use nym_validator_client::nyxd::{contract_traits::NameServiceSigningClient, error::NyxdError};
use tap::TapFallible;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub id: NameId,
}
pub async fn delete(args: Args, client: SigningClient) -> Result<(), NyxdError> {
info!("Deleting registered name alias with id {}", args.id);
let res = client
.delete_name_by_id(args.id, None)
.await
.tap_err(|err| error!("Failed to delete name: {err:#?}"))?;
info!("Deleted: {res:?}");
Ok(())
}
@@ -1,22 +0,0 @@
use clap::{Args, Subcommand};
pub mod delete;
pub mod register;
pub mod register_sign_payload;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsName {
#[clap(subcommand)]
pub command: MixnetOperatorsNameCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsNameCommands {
/// Register a name alias for a nym address
Register(register::Args),
/// Delete name alias for a nym address
Delete(delete::Args),
/// Create base58-encoded payload required for producing valiid register signature.
CreateNameRegisterPayload(register_sign_payload::Args),
}
@@ -1,53 +0,0 @@
use clap::Parser;
use log::{error, info};
use nym_contracts_common::signing::MessageSignature;
use nym_name_service_common::{Address, Coin, NameDetails, NymName};
use nym_sphinx::addressing::clients::Recipient;
use nym_validator_client::nyxd::{contract_traits::NameServiceSigningClient, error::NyxdError};
use tap::TapFallible;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
/// Name alias
#[clap(long)]
pub name: NymName,
/// Nym address that the alias is pointing to
#[clap(long)]
pub nym_address: Recipient,
#[clap(long)]
pub signature: MessageSignature,
/// Deposit to be made to the service provider directory, in curent DENOMINATION (e.g. 'unym')
#[clap(long)]
pub deposit: u128,
}
pub async fn register(args: Args, client: SigningClient) -> Result<(), NyxdError> {
info!(
"Registering name alias '{}' for nym address '{}'",
args.name, args.nym_address
);
let address = Address::new(&args.nym_address.to_string()).expect("invalid address");
let identity_key = address.client_id().to_string();
let name = NameDetails {
name: args.name,
address,
identity_key,
};
let denom = client.current_chain_details().mix_denom.base.as_str();
let deposit = Coin::new(args.deposit, denom);
let res = client
.register_name(name, args.signature, deposit.into(), None)
.await
.tap_err(|err| error!("Failed to register name: {err:#?}"))?;
info!("Registered name: {res:?}");
Ok(())
}
@@ -1,65 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
context::SigningClient,
utils::{account_id_to_cw_addr, DataWrapper},
};
use clap::Parser;
use cosmwasm_std::Coin;
use nym_bin_common::output_format::OutputFormat;
use nym_name_service_common::{
signing_types::construct_name_register_sign_payload, Address, NymName,
};
use nym_sphinx::addressing::clients::Recipient;
use nym_validator_client::nyxd::{contract_traits::NameServiceQueryClient, error::NyxdError};
#[derive(Debug, Parser)]
pub struct Args {
// The name to register.
#[arg(long)]
pub name: NymName,
/// The nym address the name should point to.
#[arg(long)]
pub nym_address: Recipient,
#[arg(long)]
pub amount: u128,
#[arg(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
pub async fn create_payload(args: Args, client: SigningClient) -> Result<(), NyxdError> {
let address = Address::new(&args.nym_address.to_string()).expect("invalid address");
let identity_key = address.client_id().to_string();
let name = nym_name_service_common::NameDetails {
name: args.name,
address,
identity_key,
};
let denom = client.current_chain_details().mix_denom.base.as_str();
let deposit = Coin::new(args.amount, denom);
let nonce = match client.get_name_signing_nonce(&client.address()).await {
Ok(nonce) => nonce,
Err(err) => {
eprint!(
"failed to query for the signing nonce of {}: {err}",
client.address()
);
return Err(err);
}
};
let address = account_id_to_cw_addr(&client.address());
let payload = construct_name_register_sign_payload(nonce, address, deposit, name);
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
println!("{}", args.output.format(&wrapper));
Ok(())
}
@@ -1,45 +0,0 @@
use clap::Parser;
use log::info;
use nym_contracts_common::signing::MessageSignature;
use nym_service_provider_directory_common::{Coin, NymAddress, ServiceDetails, ServiceType};
use nym_validator_client::nyxd::contract_traits::SpDirectorySigningClient;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub nym_address: String,
#[clap(long)]
pub signature: MessageSignature,
/// Deposit to be made to the service provider directory, in curent DENOMINATION (e.g. 'unym')
#[clap(long)]
pub deposit: u128,
#[clap(long)]
pub identity_key: String,
}
pub async fn announce(args: Args, client: SigningClient) {
info!("Annoucing service provider");
let nym_address = NymAddress::Address(args.nym_address);
let service_type = ServiceType::NetworkRequester;
let service = ServiceDetails {
nym_address,
service_type,
identity_key: args.identity_key,
};
let denom = client.current_chain_details().mix_denom.base.as_str();
let deposit = Coin::new(args.deposit, denom);
let res = client
.announce_service_provider(service, args.signature, deposit.into(), None)
.await
.expect("Failed to announce service provider");
info!("Announced service provider: {res:?}");
}
@@ -1,61 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{
context::SigningClient,
utils::{account_id_to_cw_addr, DataWrapper},
};
use clap::Parser;
use cosmwasm_std::Coin;
use nym_bin_common::output_format::OutputFormat;
use nym_service_provider_directory_common::{
signing_types::construct_service_provider_announce_sign_payload, NymAddress,
ServiceType::NetworkRequester,
};
use nym_sphinx::addressing::clients::Recipient;
use nym_validator_client::nyxd::contract_traits::SpDirectoryQueryClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub nym_address: Recipient,
#[clap(long)]
pub amount: u128,
#[clap(long)]
pub identity_key: String,
#[clap(short, long, default_value_t = OutputFormat::default())]
output: OutputFormat,
}
pub async fn create_payload(args: Args, client: SigningClient) {
let service = nym_service_provider_directory_common::ServiceDetails {
nym_address: NymAddress::new(&args.nym_address.to_string()),
service_type: NetworkRequester,
identity_key: args.identity_key,
};
let denom = client.current_chain_details().mix_denom.base.as_str();
let deposit = Coin::new(args.amount, denom);
let nonce = match client.get_service_signing_nonce(&client.address()).await {
Ok(nonce) => nonce,
Err(err) => {
eprint!(
"failed to query for the signing nonce of {}: {err}",
client.address()
);
return;
}
};
let address = account_id_to_cw_addr(&client.address());
let payload =
construct_service_provider_announce_sign_payload(nonce, address, deposit, service);
let wrapper = DataWrapper::new(payload.to_base58_string().unwrap());
println!("{}", args.output.format(&wrapper))
}
@@ -1,23 +0,0 @@
use clap::Parser;
use log::info;
use nym_service_provider_directory_common::ServiceId;
use nym_validator_client::nyxd::contract_traits::SpDirectorySigningClient;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub id: ServiceId,
}
pub async fn delete(args: Args, client: SigningClient) {
info!("Deleting service provider with id {}", args.id);
let res = client
.delete_service_provider_by_id(args.id, None)
.await
.expect("Failed to delete service provider");
info!("Deleted: {res:?}");
}
@@ -1,23 +0,0 @@
use clap::{Args, Subcommand};
pub mod announce;
pub mod announce_sign_payload;
pub mod delete;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsService {
#[clap(subcommand)]
pub command: MixnetOperatorsServiceCommands,
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsServiceCommands {
/// Announce service provider to the world
Announce(announce::Args),
/// Delete entry for service provider from the directory
Delete(delete::Args),
/// Create base58-encoded payload required for producing valid announce signature.
CreateServiceAnnounceSignPayload(announce_sign_payload::Args),
}
@@ -5,8 +5,6 @@ use clap::{Args, Subcommand};
pub mod query_all_gateways;
pub mod query_all_mixnodes;
pub mod query_all_names;
pub mod query_all_service_providers;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
@@ -21,8 +19,4 @@ pub enum MixnetQueryCommands {
Mixnodes(query_all_mixnodes::Args),
/// Query gateways
Gateways(query_all_gateways::Args),
/// Query announced service-providers
ServiceProviders(query_all_service_providers::Args),
/// Query registed names
Names(query_all_names::Args),
}
@@ -1,53 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClientWithNyxd;
use crate::utils::show_error;
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
use nym_validator_client::nym_api::error::NymAPIError;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "Optionally, the registered name to display")]
pub name: Option<String>,
}
pub async fn query(args: Args, client: &QueryClientWithNyxd) {
log::trace!("Querying all registered names");
match client.nym_api.get_registered_names().await {
Ok(res) => {
if let Some(name) = args.name {
let name = res.names.iter().find(|name_entry| {
name_entry.name.name.to_string().eq_ignore_ascii_case(&name)
});
println!(
"{}",
::serde_json::to_string_pretty(&name).expect("json formatting error")
);
} else {
let mut table = Table::new();
table.set_header(vec!["Name Id", "Owner", "Nym Address", "Name"]);
for name_entry in res.names {
table.add_row(vec![
name_entry.id.to_string(),
name_entry.owner.to_string(),
name_entry.name.address.to_string(),
name_entry.name.name.to_string(),
]);
}
println!("The registered names in the directory are:");
println!("{table}");
}
}
Err(NymAPIError::NotFound) => {
println!("nym-api reports no name endpoint available");
}
Err(e) => show_error(e),
}
}
@@ -1,55 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::QueryClientWithNyxd;
use crate::utils::show_error;
use clap::Parser;
use comfy_table::Table;
use nym_validator_client::client::NymApiClientExt;
use nym_validator_client::nym_api::error::NymAPIError;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "Optionally, the service provider to display")]
pub nym_address: Option<String>,
}
pub async fn query(args: Args, client: &QueryClientWithNyxd) {
match client.nym_api.get_service_providers().await {
Ok(res) => {
if let Some(nym_address) = args.nym_address {
let service = res.services.iter().find(|service| {
service
.service
.nym_address
.to_string()
.eq_ignore_ascii_case(&nym_address)
});
println!(
"{}",
::serde_json::to_string_pretty(&service).expect("json formatting error")
);
} else {
let mut table = Table::new();
table.set_header(vec!["Service Id", "Announcer", "Type", "Nym Address"]);
for service in res.services {
table.add_row(vec![
service.service_id.to_string(),
service.announcer.to_string(),
service.service.service_type.to_string(),
service.service.nym_address.to_string(),
]);
}
println!("The service providers in the directory are:");
println!("{table}");
}
}
Err(NymAPIError::NotFound) => {
println!("nym-api reports no service provider endpoint available");
}
Err(e) => show_error(e),
}
}
@@ -1,20 +0,0 @@
[package]
name = "nym-name-service-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw2 = { workspace = true, optional = true }
cw-controllers = { workspace = true }
cw-utils = { workspace = true }
nym-contracts-common = { path = "../contracts-common", version = "0.5.0" }
serde = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
[features]
schema = ["cw2"]
@@ -1,83 +0,0 @@
use cosmwasm_std::{Addr, StdError};
use cw_controllers::AdminError;
use nym_contracts_common::signing::verifier::ApiVerifierError;
use thiserror::Error;
use crate::{Address, NameId, NymName};
#[derive(Error, Debug, PartialEq)]
pub enum NameServiceError {
#[error("{0}")]
Std(#[from] StdError),
#[error("{0}")]
AdminError(#[from] AdminError),
#[error("name id entry not found: {name_id}")]
NotFound { name_id: NameId },
#[error("name entry not found: {name}")]
NameNotFound { name: NymName },
#[error("{sender} is not registrator of name")]
Unauthorized { sender: Addr },
#[error("deposit required to register a name")]
DepositRequired { source: cw_utils::PaymentError },
#[error("insufficiant deposit: {funds}, required: {deposit_required}")]
InsufficientDeposit {
funds: cosmwasm_std::Uint128,
deposit_required: cosmwasm_std::Uint128,
},
#[error("deposit too large: {funds}, required: {deposit_required}")]
TooLargeDeposit {
funds: cosmwasm_std::Uint128,
deposit_required: cosmwasm_std::Uint128,
},
#[error("reached the max number of names ({max_names}) for owner {owner}")]
ReachedMaxNamesForOwner { max_names: u32, owner: Addr },
#[error("reached the max number of names ({max_names}) for address {0}", address.to_string())]
ReachedMaxNamesForAddress { max_names: u32, address: Address },
#[error("failed to parse {value} into a valid SemVer version: {error_message}")]
SemVerFailure {
value: String,
error_message: String,
},
#[error("failed to recover ed25519 public key from its base58 representation: {0}")]
MalformedEd25519IdentityKey(String),
#[error("failed to recover ed25519 signature from its base58 representation: {0}")]
MalformedEd25519Signature(String),
#[error("provided ed25519 signature did not verify correctly")]
InvalidEd25519Signature,
#[error("failed to verify message signature: {source}")]
SignatureVerificationFailure {
#[from]
source: ApiVerifierError,
},
#[error("duplicate entries detected for name: {name}")]
DuplicateNames { name: NymName },
#[error("name already registered: {name}")]
NameAlreadyRegistered { name: NymName },
#[error("invalid nym address format: {0}")]
InvalidNymAddress(String),
#[error("client identity in nym address does not match the provided identity key")]
IdentityKeyMismatch {
address: Address,
identity_key: String,
},
}
pub type Result<T, E = NameServiceError> = std::result::Result<T, E>;
@@ -1,66 +0,0 @@
use cosmwasm_std::{Coin, Event};
use crate::RegisteredName;
pub enum NameEventType {
Register,
DeleteId,
DeleteName,
UpdateDepositRequired,
}
impl std::fmt::Display for NameEventType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NameEventType::Register => write!(f, "register"),
NameEventType::DeleteId => write!(f, "delete_id"),
NameEventType::DeleteName => write!(f, "delete_name"),
NameEventType::UpdateDepositRequired => write!(f, "update_deposit_required"),
}
}
}
impl From<NameEventType> for String {
fn from(event_type: NameEventType) -> Self {
event_type.to_string()
}
}
pub const ACTION: &str = "action";
pub const NAME_ID: &str = "name_id";
pub const NAME: &str = "name";
pub const OWNER: &str = "owner";
pub const DEPOSIT_REQUIRED: &str = "deposit_required";
pub fn new_register_event(name: RegisteredName) -> Event {
Event::new(NameEventType::Register)
.add_attribute(ACTION, NameEventType::Register)
.add_attribute(NAME_ID, name.id.to_string())
.add_attribute(NAME, name.name.name.to_string())
.add_attribute(name.name.address.event_tag(), name.name.address.to_string())
.add_attribute(OWNER, name.owner.to_string())
}
pub fn new_delete_id_event(name: RegisteredName) -> Event {
Event::new(NameEventType::DeleteId)
.add_attribute(ACTION, NameEventType::DeleteId)
.add_attribute(NAME_ID, name.id.to_string())
.add_attribute(NAME, name.name.name.to_string())
.add_attribute(name.name.address.event_tag(), name.name.address.to_string())
}
pub fn new_delete_name_event(name: RegisteredName) -> Event {
Event::new(NameEventType::DeleteId)
.add_attribute(ACTION, NameEventType::DeleteName)
.add_attribute(NAME_ID, name.id.to_string())
.add_attribute(NAME, name.name.name.to_string())
.add_attribute(name.name.address.event_tag(), name.name.address.to_string())
}
pub fn new_update_deposit_required_event(deposit_required: Coin) -> Event {
Event::new(NameEventType::UpdateDepositRequired)
.add_attribute(ACTION, NameEventType::UpdateDepositRequired)
.add_attribute(DEPOSIT_REQUIRED, deposit_required.to_string())
}
@@ -1,11 +0,0 @@
pub mod error;
pub mod events;
pub mod msg;
pub mod response;
pub mod signing_types;
pub mod types;
// Re-export all types at the top-level
pub use types::*;
pub use cosmwasm_std::{Addr, Coin, Decimal, Fraction};
@@ -1,123 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{Address, NameDetails, NameId, NymName};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Coin;
use nym_contracts_common::signing::MessageSignature;
#[cfg(feature = "schema")]
use crate::{
response::{ConfigResponse, NamesListResponse, PagedNamesListResponse},
types::RegisteredName,
};
#[cfg(feature = "schema")]
use cosmwasm_schema::QueryResponses;
#[cfg(feature = "schema")]
use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
#[cw_serde]
pub struct InstantiateMsg {
pub deposit_required: Coin,
}
impl InstantiateMsg {
pub fn new(deposit_required: Coin) -> Self {
Self { deposit_required }
}
}
#[cw_serde]
pub struct MigrateMsg {}
#[cw_serde]
pub enum ExecuteMsg {
/// Announcing a name pointing to a nym-address
Register {
name: NameDetails,
owner_signature: MessageSignature,
},
/// Delete a name entry by id
DeleteId { name_id: NameId },
/// Delete a name entry by name
DeleteName { name: NymName },
/// Change the deposit required for announcing a name
UpdateDepositRequired { deposit_required: Coin },
}
impl ExecuteMsg {
pub fn delete_id(name_id: NameId) -> Self {
ExecuteMsg::DeleteId { name_id }
}
pub fn default_memo(&self) -> String {
match self {
ExecuteMsg::Register {
name,
owner_signature: _,
} => {
format!("registering {} as name: {}", name.address, name.name)
}
ExecuteMsg::DeleteId { name_id } => {
format!("deleting name with id {name_id}")
}
ExecuteMsg::DeleteName { name } => {
format!("deleting name: {name}")
}
ExecuteMsg::UpdateDepositRequired { deposit_required } => {
format!("updating the deposit required to {deposit_required}")
}
}
}
}
#[cw_serde]
#[cfg_attr(feature = "schema", derive(QueryResponses))]
pub enum QueryMsg {
/// Query the name by it's assigned id
#[cfg_attr(feature = "schema", returns(RegisteredName))]
NameId { name_id: NameId },
/// Query the names by the registrator
#[cfg_attr(feature = "schema", returns(NamesListResponse))]
ByOwner { owner: String },
#[cfg_attr(feature = "schema", returns(RegisteredName))]
ByName { name: NymName },
#[cfg_attr(feature = "schema", returns(NamesListResponse))]
ByAddress { address: Address },
#[cfg_attr(feature = "schema", returns(PagedNamesListResponse))]
All {
limit: Option<u32>,
start_after: Option<NameId>,
},
#[cfg_attr(feature = "schema", returns(Nonce))]
SigningNonce { address: String },
#[cfg_attr(feature = "schema", returns(ConfigResponse))]
Config {},
/// Gets build information of this contract, such as the commit hash used for the build or rustc version.
#[cfg_attr(feature = "schema", returns(ContractBuildInformation))]
GetContractVersion {},
/// Gets the stored contract version information that's required by the CW2 spec interface for migrations.
#[serde(rename = "get_cw2_contract_version")]
#[cfg_attr(feature = "schema", returns(cw2::ContractVersion))]
GetCW2ContractVersion {},
}
impl QueryMsg {
pub fn all() -> QueryMsg {
QueryMsg::All {
limit: None,
start_after: None,
}
}
}
@@ -1,53 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{NameId, RegisteredName};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Coin;
#[cw_serde]
pub struct NamesListResponse {
pub names: Vec<RegisteredName>,
}
impl NamesListResponse {
pub fn new(names: Vec<RegisteredName>) -> NamesListResponse {
NamesListResponse { names }
}
}
impl From<&[RegisteredName]> for NamesListResponse {
fn from(names: &[RegisteredName]) -> Self {
NamesListResponse {
names: names.to_vec(),
}
}
}
#[cw_serde]
pub struct PagedNamesListResponse {
pub names: Vec<RegisteredName>,
pub per_page: usize,
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
pub start_next_after: Option<NameId>,
}
impl PagedNamesListResponse {
pub fn new(
names: Vec<RegisteredName>,
per_page: usize,
start_next_after: Option<NameId>,
) -> PagedNamesListResponse {
PagedNamesListResponse {
names,
per_page,
start_next_after,
}
}
}
#[cw_serde]
pub struct ConfigResponse {
pub deposit_required: Coin,
}
@@ -1,32 +0,0 @@
use cosmwasm_std::{Addr, Coin};
use nym_contracts_common::signing::{
ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose,
};
use serde::Serialize;
use crate::NameDetails;
pub type SignableNameRegisterMsg = SignableMessage<ContractMessageContent<NameRegister>>;
#[derive(Serialize)]
pub struct NameRegister {
name: NameDetails,
}
impl SigningPurpose for NameRegister {
fn message_type() -> MessageType {
MessageType::new("name-register")
}
}
pub fn construct_name_register_sign_payload(
nonce: Nonce,
sender: Addr,
deposit: Coin,
name: NameDetails,
) -> SignableNameRegisterMsg {
let payload = NameRegister { name };
let proxy = None;
let content = ContractMessageContent::new(sender, proxy, vec![deposit], payload);
SignableMessage::new(nonce, content)
}
@@ -1,252 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Coin};
use nym_contracts_common::IdentityKey;
use std::{
fmt::{Display, Formatter},
str::FromStr,
};
use thiserror::Error;
use crate::error::{NameServiceError, Result};
/// The directory of names are indexed by [`NameId`].
pub type NameId = u32;
#[cw_serde]
pub struct RegisteredName {
/// Unique id assigned to the registerd name.
pub id: NameId,
/// The registerd name details.
pub name: NameDetails,
/// name owner.
pub owner: Addr,
/// Block height at which the name was added.
pub block_height: u64,
/// The deposit used to announce the name.
pub deposit: Coin,
}
impl RegisteredName {
// Shortcut for getting the actual name
pub fn entry(&self) -> &NymName {
&self.name.name
}
}
#[cw_serde]
pub struct NameDetails {
/// The name pointing to the nym address
pub name: NymName,
/// The address of the name alias.
pub address: Address,
/// The identity key of the registered name.
pub identity_key: IdentityKey,
}
/// String representation of a nym address, which is of the form
/// client_id.client_enc@gateway_id.
/// NOTE: entirely unvalidated.
#[cw_serde]
pub enum Address {
NymAddress(NymAddressInner),
// Possible extension:
//Gateway(String)
}
#[cw_serde]
pub struct NymAddressInner {
client_id: String,
client_enc: String,
gateway_id: String,
}
// ADDRESS . ENCRYPTION @ GATEWAY_ID
impl std::fmt::Display for NymAddressInner {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}.{}@{}",
self.client_id, self.client_enc, self.gateway_id
)
}
}
impl Address {
/// Create a new nym address.
pub fn new(address: &str) -> Result<Self> {
parse_nym_address(address)
.map(Self::NymAddress)
.ok_or_else(|| NameServiceError::InvalidNymAddress(address.to_string()))
}
pub fn client_id(&self) -> &str {
match self {
Address::NymAddress(address) => &address.client_id,
}
}
pub fn client_enc(&self) -> &str {
match self {
Address::NymAddress(address) => &address.client_enc,
}
}
pub fn gateway_id(&self) -> &str {
match self {
Address::NymAddress(address) => &address.gateway_id,
}
}
pub fn event_tag(&self) -> &str {
match self {
Address::NymAddress(_) => "nym_address",
//Address::Gateway(_) => "gatway_address",
}
}
}
impl Display for Address {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Address::NymAddress(address) => write!(f, "{}", address),
}
}
}
// A valid nym address is of the form client_id.client_enc@gateway_id
fn parse_nym_address(address: &str) -> Option<NymAddressInner> {
let parts: Vec<&str> = address.split('@').collect();
if parts.len() != 2 {
return None;
}
let client_part = parts[0];
let gateway_part = parts[1];
// The client part consists of two parts separated by a dot
let client_parts: Vec<&str> = client_part.split('.').collect();
if client_parts.len() != 2 {
return None;
}
// Check that the gateway part does not contain any dots
if gateway_part.contains('.') {
return None;
}
Some(NymAddressInner {
client_id: client_parts[0].to_string(),
client_enc: client_parts[1].to_string(),
gateway_id: gateway_part.to_string(),
})
}
/// Name stored and pointing a to a nym-address
#[cw_serde]
pub struct NymName(String);
#[derive(Debug, Error)]
pub enum NymNameError {
#[error("invalid name")]
InvalidName,
}
/// Defines what names are allowed
fn is_valid_name_char(c: char) -> bool {
// Normal lowercase letters
(c.is_alphabetic() && c.is_lowercase())
// or numbers
|| c.is_numeric()
// special case hyphen or underscore
|| c == '-' || c == '_'
}
impl NymName {
pub fn new(name: &str) -> Result<NymName, NymNameError> {
// We are a bit restrictive in which names we allow, to start out with. Consider relaxing
// this in the future.
if !name.chars().all(is_valid_name_char) {
return Err(NymNameError::InvalidName);
}
Ok(Self(name.to_string()))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl FromStr for NymName {
type Err = NymNameError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s)
}
}
impl Display for NymName {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[cfg(test)]
mod tests {
use super::NymName;
#[test]
fn parse_nym_name() {
// Test some valid cases
assert!(NymName::new("foo").is_ok());
assert!(NymName::new("foo-bar").is_ok());
assert!(NymName::new("foo-bar-123").is_ok());
assert!(NymName::new("foo_bar").is_ok());
assert!(NymName::new("foo_bar_123").is_ok());
// And now test all some invalid ones
assert!(NymName::new("Foo").is_err());
assert!(NymName::new("foo bar").is_err());
assert!(NymName::new("foo!bar").is_err());
assert!(NymName::new("foo#bar").is_err());
assert!(NymName::new("foo$bar").is_err());
assert!(NymName::new("foo%bar").is_err());
assert!(NymName::new("foo&bar").is_err());
assert!(NymName::new("foo'bar").is_err());
assert!(NymName::new("foo(bar").is_err());
assert!(NymName::new("foo)bar").is_err());
assert!(NymName::new("foo*bar").is_err());
assert!(NymName::new("foo+bar").is_err());
assert!(NymName::new("foo,bar").is_err());
assert!(NymName::new("foo.bar").is_err());
assert!(NymName::new("foo.bar").is_err());
assert!(NymName::new("foo/bar").is_err());
assert!(NymName::new("foo/bar").is_err());
assert!(NymName::new("foo:bar").is_err());
assert!(NymName::new("foo;bar").is_err());
assert!(NymName::new("foo<bar").is_err());
assert!(NymName::new("foo=bar").is_err());
assert!(NymName::new("foo>bar").is_err());
assert!(NymName::new("foo?bar").is_err());
assert!(NymName::new("foo@bar").is_err());
assert!(NymName::new("fooBar").is_err());
assert!(NymName::new("foo[bar").is_err());
assert!(NymName::new("foo\"bar").is_err());
assert!(NymName::new("foo\\bar").is_err());
assert!(NymName::new("foo]bar").is_err());
assert!(NymName::new("foo^bar").is_err());
assert!(NymName::new("foo`bar").is_err());
assert!(NymName::new("foo{bar").is_err());
assert!(NymName::new("foo|bar").is_err());
assert!(NymName::new("foo}bar").is_err());
assert!(NymName::new("foo~bar").is_err());
}
}
@@ -1,19 +0,0 @@
[package]
name = "nym-service-provider-directory-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw2 = { workspace = true, optional = true }
cw-controllers = { workspace = true }
cw-utils = { workspace = true }
nym-contracts-common = { path = "../contracts-common", version = "0.5.0" }
thiserror = { workspace = true }
[features]
schema = ["cw2"]
@@ -1,68 +0,0 @@
use cosmwasm_std::{Addr, StdError};
use cw_controllers::AdminError;
use nym_contracts_common::signing::verifier::ApiVerifierError;
use thiserror::Error;
use crate::{NymAddress, ServiceId};
#[derive(Error, Debug, PartialEq)]
pub enum SpContractError {
#[error("{0}")]
Std(#[from] StdError),
#[error("{0}")]
AdminError(#[from] AdminError),
#[error("service not found: {service_id}")]
NotFound { service_id: ServiceId },
#[error("{sender} is not announcer of service")]
Unauthorized { sender: Addr },
#[error("deposit required to announce service")]
DepositRequired { source: cw_utils::PaymentError },
#[error("insufficiant deposit: {funds}, required: {deposit_required}")]
InsufficientDeposit {
funds: cosmwasm_std::Uint128,
deposit_required: cosmwasm_std::Uint128,
},
#[error("deposit too large: {funds}, required: {deposit_required}")]
TooLargeDeposit {
funds: cosmwasm_std::Uint128,
deposit_required: cosmwasm_std::Uint128,
},
#[error("reached the max number of providers ({max_providers}) for announcer {announcer}")]
ReachedMaxProvidersForAdmin { max_providers: u32, announcer: Addr },
#[error("reached the max number of aliases ({max_aliases}) for nym address {0}", nym_address.to_string())]
ReachedMaxAliasesForNymAddress {
max_aliases: u32,
nym_address: NymAddress,
},
#[error("failed to parse {value} into a valid SemVer version: {error_message}")]
SemVerFailure {
value: String,
error_message: String,
},
#[error("Failed to recover ed25519 public key from its base58 representation - {0}")]
MalformedEd25519IdentityKey(String),
#[error("Failed to recover ed25519 signature from its base58 representation - {0}")]
MalformedEd25519Signature(String),
#[error("Provided ed25519 signature did not verify correctly")]
InvalidEd25519Signature,
#[error("failed to verify message signature: {source}")]
SignatureVerificationFailure {
#[from]
source: ApiVerifierError,
},
}
pub type Result<T, E = SpContractError> = std::result::Result<T, E>;
@@ -1,58 +0,0 @@
use cosmwasm_std::{Coin, Event};
use crate::{Service, ServiceId};
pub enum ServiceProviderEventType {
Announce,
DeleteId,
DeleteNymAddress,
UpdateDepositRequired,
}
impl std::fmt::Display for ServiceProviderEventType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ServiceProviderEventType::Announce => write!(f, "announce"),
ServiceProviderEventType::DeleteId => write!(f, "delete_id"),
ServiceProviderEventType::DeleteNymAddress => write!(f, "delete_nym_address"),
ServiceProviderEventType::UpdateDepositRequired => write!(f, "update_deposit_required"),
}
}
}
impl From<ServiceProviderEventType> for String {
fn from(event_type: ServiceProviderEventType) -> Self {
event_type.to_string()
}
}
pub const ACTION: &str = "action";
pub const SERVICE_ID: &str = "service_id";
pub const SERVICE_TYPE: &str = "service_type";
pub const NYM_ADDRESS: &str = "nym_address";
pub const OWNER: &str = "owner";
pub const DEPOSIT_REQUIRED: &str = "deposit_required";
pub fn new_announce_event(service_id: ServiceId, service: Service) -> Event {
Event::new(ServiceProviderEventType::Announce)
.add_attribute(ACTION, ServiceProviderEventType::Announce)
.add_attribute(SERVICE_ID, service_id.to_string())
.add_attribute(SERVICE_TYPE, service.service.service_type.to_string())
.add_attribute(NYM_ADDRESS, service.service.nym_address.to_string())
.add_attribute(OWNER, service.announcer.to_string())
}
pub fn new_delete_id_event(service: Service) -> Event {
Event::new(ServiceProviderEventType::DeleteId)
.add_attribute(ACTION, ServiceProviderEventType::DeleteId)
.add_attribute(SERVICE_ID, service.service_id.to_string())
.add_attribute(NYM_ADDRESS, service.service.nym_address.to_string())
}
pub fn new_update_deposit_required_event(deposit_required: Coin) -> Event {
Event::new(ServiceProviderEventType::UpdateDepositRequired)
.add_attribute(ACTION, ServiceProviderEventType::UpdateDepositRequired)
.add_attribute(DEPOSIT_REQUIRED, deposit_required.to_string())
}
@@ -1,11 +0,0 @@
pub mod error;
pub mod events;
pub mod msg;
pub mod response;
pub mod signing_types;
pub mod types;
// Re-export all types at the top-level
pub use types::*;
pub use cosmwasm_std::{Addr, Coin, Decimal, Fraction};
@@ -1,118 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{NymAddress, ServiceDetails, ServiceId};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Coin;
use nym_contracts_common::signing::MessageSignature;
#[cfg(feature = "schema")]
use crate::{
response::{ConfigResponse, PagedServicesListResponse, ServicesListResponse},
types::Service,
};
#[cfg(feature = "schema")]
use cosmwasm_schema::QueryResponses;
#[cfg(feature = "schema")]
use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
#[cw_serde]
pub struct InstantiateMsg {
pub deposit_required: Coin,
}
impl InstantiateMsg {
pub fn new(deposit_required: Coin) -> Self {
Self { deposit_required }
}
}
#[cw_serde]
pub struct MigrateMsg {}
#[cw_serde]
pub enum ExecuteMsg {
Announce {
service: ServiceDetails,
owner_signature: MessageSignature,
},
DeleteId {
service_id: ServiceId,
},
DeleteNymAddress {
nym_address: NymAddress,
},
UpdateDepositRequired {
deposit_required: Coin,
},
}
impl ExecuteMsg {
pub fn delete_id(service_id: ServiceId) -> Self {
ExecuteMsg::DeleteId { service_id }
}
pub fn default_memo(&self) -> String {
match self {
ExecuteMsg::Announce {
service,
owner_signature: _,
} => format!(
"announcing {} as type {}",
service.nym_address, service.service_type
),
ExecuteMsg::DeleteId { service_id } => {
format!("deleting service with service id {service_id}")
}
ExecuteMsg::DeleteNymAddress { nym_address } => {
format!("deleting service with nym address {nym_address}")
}
ExecuteMsg::UpdateDepositRequired { deposit_required } => {
format!("updating the deposit required to {deposit_required}")
}
}
}
}
#[cw_serde]
#[cfg_attr(feature = "schema", derive(QueryResponses))]
pub enum QueryMsg {
#[cfg_attr(feature = "schema", returns(Service))]
ServiceId { service_id: ServiceId },
#[cfg_attr(feature = "schema", returns(ServicesListResponse))]
ByAnnouncer { announcer: String },
#[cfg_attr(feature = "schema", returns(ServicesListResponse))]
ByNymAddress { nym_address: NymAddress },
#[cfg_attr(feature = "schema", returns(PagedServicesListResponse))]
All {
limit: Option<u32>,
start_after: Option<ServiceId>,
},
#[cfg_attr(feature = "schema", returns(Nonce))]
SigningNonce { address: String },
#[cfg_attr(feature = "schema", returns(ConfigResponse))]
Config {},
/// Gets build information of this contract, such as the commit hash used for the build or rustc version.
#[cfg_attr(feature = "schema", returns(ContractBuildInformation))]
GetContractVersion {},
/// Gets the stored contract version information that's required by the CW2 spec interface for migrations.
#[serde(rename = "get_cw2_contract_version")]
#[cfg_attr(feature = "schema", returns(cw2::ContractVersion))]
GetCW2ContractVersion {},
}
impl QueryMsg {
pub fn all() -> QueryMsg {
QueryMsg::All {
limit: None,
start_after: None,
}
}
}
@@ -1,59 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{Service, ServiceId};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Coin;
#[cw_serde]
pub struct ServiceInfoResponse {
pub service_id: ServiceId,
pub service: Option<Service>,
}
#[cw_serde]
pub struct ServicesListResponse {
pub services: Vec<Service>,
}
impl ServicesListResponse {
pub fn new(services: Vec<Service>) -> ServicesListResponse {
ServicesListResponse { services }
}
}
impl From<&[Service]> for ServicesListResponse {
fn from(services: &[Service]) -> Self {
Self {
services: services.to_vec(),
}
}
}
#[cw_serde]
pub struct PagedServicesListResponse {
pub services: Vec<Service>,
pub per_page: usize,
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
pub start_next_after: Option<ServiceId>,
}
impl PagedServicesListResponse {
pub fn new(
services: Vec<Service>,
per_page: usize,
start_next_after: Option<ServiceId>,
) -> PagedServicesListResponse {
PagedServicesListResponse {
services,
per_page,
start_next_after,
}
}
}
#[cw_serde]
pub struct ConfigResponse {
pub deposit_required: Coin,
}
@@ -1,35 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::ServiceDetails;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Coin};
use nym_contracts_common::signing::{
ContractMessageContent, MessageType, Nonce, SignableMessage, SigningPurpose,
};
pub type SignableServiceProviderAnnounceMsg =
SignableMessage<ContractMessageContent<ServiceProviderAnnounce>>;
#[cw_serde]
pub struct ServiceProviderAnnounce {
service: ServiceDetails,
}
impl SigningPurpose for ServiceProviderAnnounce {
fn message_type() -> MessageType {
MessageType::new("service-provider-announce")
}
}
pub fn construct_service_provider_announce_sign_payload(
nonce: Nonce,
sender: Addr,
deposit: Coin,
service: ServiceDetails,
) -> SignableServiceProviderAnnounceMsg {
let payload = ServiceProviderAnnounce { service };
let proxy = None;
let content = ContractMessageContent::new(sender, proxy, vec![deposit], payload);
SignableMessage::new(nonce, content)
}
@@ -1,79 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{Addr, Coin};
use nym_contracts_common::IdentityKey;
use std::fmt::{Display, Formatter};
/// The directory of services are indexed by [`ServiceId`].
pub type ServiceId = u32;
#[cw_serde]
pub struct Service {
/// Unique id assigned to the anounced service.
pub service_id: ServiceId,
/// The announced service.
pub service: ServiceDetails,
/// Address of the service owner.
pub announcer: Addr,
/// Block height at which the service was added.
pub block_height: u64,
/// The deposit used to announce the service.
pub deposit: Coin,
}
#[cw_serde]
pub struct ServiceDetails {
/// The address of the service.
pub nym_address: NymAddress,
/// The service type.
pub service_type: ServiceType,
/// The identity key of the service.
pub identity_key: IdentityKey,
}
/// The types of addresses supported.
#[cw_serde]
pub enum NymAddress {
/// String representation of a nym address, which is of the form
/// client_id.client_enc@gateway_id.
Address(String),
// String name that can looked up in the nym-name-service contract (once it exists)
//Name(String),
}
impl NymAddress {
/// Create a new nym address.
pub fn new(address: &str) -> Self {
Self::Address(address.to_string())
}
pub fn as_str(&self) -> &str {
match self {
NymAddress::Address(address) => address,
//NymAddress::Name(name) => name,
}
}
}
impl Display for NymAddress {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
/// The type of services provider supported
#[cw_serde]
pub enum ServiceType {
NetworkRequester,
}
impl std::fmt::Display for ServiceType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let service_type = match self {
ServiceType::NetworkRequester => "network_requester",
};
write!(f, "{service_type}")
}
}
-229
View File
@@ -840,101 +840,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8cbd1169bd7b4a0a20d92b9af7a7e0422888bd38a6f5ec29c1fd8c1558a272e"
[[package]]
name = "futures"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.59",
]
[[package]]
name = "futures-sink"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-timer"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "generic-array"
version = "0.12.4"
@@ -1382,46 +1287,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "nym-name-service"
version = "0.1.0"
dependencies = [
"anyhow",
"bs58 0.4.0",
"cosmwasm-schema",
"cosmwasm-std",
"cw-controllers",
"cw-multi-test",
"cw-storage-plus",
"cw-utils",
"cw2",
"nym-contracts-common",
"nym-crypto",
"nym-name-service-common",
"nym-sphinx-addressing",
"rand",
"rand_chacha",
"rstest",
"semver",
"serde",
"thiserror",
"vergen",
]
[[package]]
name = "nym-name-service-common"
version = "0.1.0"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
"cw-controllers",
"cw-utils",
"cw2",
"nym-contracts-common",
"serde",
"thiserror",
]
[[package]]
name = "nym-pemstore"
version = "0.3.0"
@@ -1429,53 +1294,6 @@ dependencies = [
"pem",
]
[[package]]
name = "nym-service-provider-directory"
version = "0.1.0"
dependencies = [
"anyhow",
"bs58 0.4.0",
"cosmwasm-schema",
"cosmwasm-std",
"cw-controllers",
"cw-multi-test",
"cw-storage-plus",
"cw-utils",
"cw2",
"nym-contracts-common",
"nym-crypto",
"nym-service-provider-directory-common",
"rand_chacha",
"rstest",
"semver",
"serde",
"thiserror",
"vergen",
]
[[package]]
name = "nym-service-provider-directory-common"
version = "0.1.0"
dependencies = [
"cosmwasm-schema",
"cosmwasm-std",
"cw-controllers",
"cw-utils",
"cw2",
"nym-contracts-common",
"thiserror",
]
[[package]]
name = "nym-sphinx-addressing"
version = "0.1.0"
dependencies = [
"nym-crypto",
"nym-sphinx-types",
"serde",
"thiserror",
]
[[package]]
name = "nym-sphinx-types"
version = "0.2.0"
@@ -1555,18 +1373,6 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkcs8"
version = "0.9.0"
@@ -1772,32 +1578,6 @@ dependencies = [
"subtle 2.4.1",
]
[[package]]
name = "rstest"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de1bb486a691878cd320c2f0d319ba91eeaa2e894066d8b5f8f117c000e9d962"
dependencies = [
"futures",
"futures-timer",
"rstest_macros",
"rustc_version",
]
[[package]]
name = "rstest_macros"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290ca1a1c8ca7edb7c3283bd44dc35dd54fdec6253a3912e201ba1072018fca8"
dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"rustc_version",
"syn 1.0.109",
"unicode-ident",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
@@ -1983,15 +1763,6 @@ dependencies = [
"rand_core 0.6.4",
]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]]
name = "sphinx-packet"
version = "0.1.1"
-2
View File
@@ -8,8 +8,6 @@ members = [
"mixnet-vesting-integration-tests",
"multisig/cw3-flex-multisig",
"multisig/cw4-group",
"name-service",
"service-provider-directory",
"vesting",
]
+1 -7
View File
@@ -1,4 +1,4 @@
schema: coconut-bandwidth-schema coconut-dkg-schema mixnet-schema name-service-schema service-provider-directory-schema vesting-schema multisig-schema group-schema
schema: coconut-bandwidth-schema coconut-dkg-schema mixnet-schema vesting-schema multisig-schema group-schema
coconut-bandwidth-schema:
$(MAKE) -C coconut-bandwidth generate-schema
@@ -9,12 +9,6 @@ coconut-dkg-schema:
mixnet-schema:
$(MAKE) -C mixnet generate-schema
name-service-schema:
$(MAKE) -C name-service generate-schema
service-provider-directory-schema:
$(MAKE) -C service-provider-directory generate-schema
vesting-schema:
$(MAKE) -C vesting generate-schema
-4
View File
@@ -1,4 +0,0 @@
[alias]
wasm = "build --release --lib --target wasm32-unknown-unknown"
unit-test = "test --lib"
schema = "run --bin schema --features=schema-gen"
-2
View File
@@ -1,2 +0,0 @@
generate-schema:
cargo schema
-13
View File
@@ -1,13 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use vergen::{vergen, Config};
fn main() {
let mut config = Config::default();
if std::env::var("DOCS_RS").is_ok() {
// If we don't have access to git information, such as in a docs.rs build, don't error
*config.git_mut().skip_if_error_mut() = true;
}
vergen(config).expect("failed to extract build metadata");
}
File diff suppressed because it is too large Load Diff
@@ -1,201 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ExecuteMsg",
"oneOf": [
{
"description": "Announcing a name pointing to a nym-address",
"type": "object",
"required": [
"register"
],
"properties": {
"register": {
"type": "object",
"required": [
"name",
"owner_signature"
],
"properties": {
"name": {
"$ref": "#/definitions/NameDetails"
},
"owner_signature": {
"$ref": "#/definitions/MessageSignature"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Delete a name entry by id",
"type": "object",
"required": [
"delete_id"
],
"properties": {
"delete_id": {
"type": "object",
"required": [
"name_id"
],
"properties": {
"name_id": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Delete a name entry by name",
"type": "object",
"required": [
"delete_name"
],
"properties": {
"delete_name": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"$ref": "#/definitions/NymName"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Change the deposit required for announcing a name",
"type": "object",
"required": [
"update_deposit_required"
],
"properties": {
"update_deposit_required": {
"type": "object",
"required": [
"deposit_required"
],
"properties": {
"deposit_required": {
"$ref": "#/definitions/Coin"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
"Address": {
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.",
"oneOf": [
{
"type": "object",
"required": [
"nym_address"
],
"properties": {
"nym_address": {
"$ref": "#/definitions/NymAddressInner"
}
},
"additionalProperties": false
}
]
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"MessageSignature": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
},
"NameDetails": {
"type": "object",
"required": [
"address",
"identity_key",
"name"
],
"properties": {
"address": {
"description": "The address of the name alias.",
"allOf": [
{
"$ref": "#/definitions/Address"
}
]
},
"identity_key": {
"description": "The identity key of the registered name.",
"type": "string"
},
"name": {
"description": "The name pointing to the nym address",
"allOf": [
{
"$ref": "#/definitions/NymName"
}
]
}
},
"additionalProperties": false
},
"NymAddressInner": {
"type": "object",
"required": [
"client_enc",
"client_id",
"gateway_id"
],
"properties": {
"client_enc": {
"type": "string"
},
"client_id": {
"type": "string"
},
"gateway_id": {
"type": "string"
}
},
"additionalProperties": false
},
"NymName": {
"description": "Name stored and pointing a to a nym-address",
"type": "string"
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,35 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "InstantiateMsg",
"type": "object",
"required": [
"deposit_required"
],
"properties": {
"deposit_required": {
"$ref": "#/definitions/Coin"
}
},
"additionalProperties": false,
"definitions": {
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,6 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "MigrateMsg",
"type": "object",
"additionalProperties": false
}
@@ -1,230 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "QueryMsg",
"oneOf": [
{
"description": "Query the name by it's assigned id",
"type": "object",
"required": [
"name_id"
],
"properties": {
"name_id": {
"type": "object",
"required": [
"name_id"
],
"properties": {
"name_id": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Query the names by the registrator",
"type": "object",
"required": [
"by_owner"
],
"properties": {
"by_owner": {
"type": "object",
"required": [
"owner"
],
"properties": {
"owner": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"by_name"
],
"properties": {
"by_name": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"$ref": "#/definitions/NymName"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"by_address"
],
"properties": {
"by_address": {
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"$ref": "#/definitions/Address"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"all"
],
"properties": {
"all": {
"type": "object",
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"signing_nonce"
],
"properties": {
"signing_nonce": {
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"config"
],
"properties": {
"config": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Gets build information of this contract, such as the commit hash used for the build or rustc version.",
"type": "object",
"required": [
"get_contract_version"
],
"properties": {
"get_contract_version": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.",
"type": "object",
"required": [
"get_cw2_contract_version"
],
"properties": {
"get_cw2_contract_version": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
"Address": {
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.",
"oneOf": [
{
"type": "object",
"required": [
"nym_address"
],
"properties": {
"nym_address": {
"$ref": "#/definitions/NymAddressInner"
}
},
"additionalProperties": false
}
]
},
"NymAddressInner": {
"type": "object",
"required": [
"client_enc",
"client_id",
"gateway_id"
],
"properties": {
"client_enc": {
"type": "string"
},
"client_id": {
"type": "string"
},
"gateway_id": {
"type": "string"
}
},
"additionalProperties": false
},
"NymName": {
"description": "Name stored and pointing a to a nym-address",
"type": "string"
}
}
}
@@ -1,178 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PagedNamesListResponse",
"type": "object",
"required": [
"names",
"per_page"
],
"properties": {
"names": {
"type": "array",
"items": {
"$ref": "#/definitions/RegisteredName"
}
},
"per_page": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Address": {
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.",
"oneOf": [
{
"type": "object",
"required": [
"nym_address"
],
"properties": {
"nym_address": {
"$ref": "#/definitions/NymAddressInner"
}
},
"additionalProperties": false
}
]
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"NameDetails": {
"type": "object",
"required": [
"address",
"identity_key",
"name"
],
"properties": {
"address": {
"description": "The address of the name alias.",
"allOf": [
{
"$ref": "#/definitions/Address"
}
]
},
"identity_key": {
"description": "The identity key of the registered name.",
"type": "string"
},
"name": {
"description": "The name pointing to the nym address",
"allOf": [
{
"$ref": "#/definitions/NymName"
}
]
}
},
"additionalProperties": false
},
"NymAddressInner": {
"type": "object",
"required": [
"client_enc",
"client_id",
"gateway_id"
],
"properties": {
"client_enc": {
"type": "string"
},
"client_id": {
"type": "string"
},
"gateway_id": {
"type": "string"
}
},
"additionalProperties": false
},
"NymName": {
"description": "Name stored and pointing a to a nym-address",
"type": "string"
},
"RegisteredName": {
"type": "object",
"required": [
"block_height",
"deposit",
"id",
"name",
"owner"
],
"properties": {
"block_height": {
"description": "Block height at which the name was added.",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"deposit": {
"description": "The deposit used to announce the name.",
"allOf": [
{
"$ref": "#/definitions/Coin"
}
]
},
"id": {
"description": "Unique id assigned to the registerd name.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"name": {
"description": "The registerd name details.",
"allOf": [
{
"$ref": "#/definitions/NameDetails"
}
]
},
"owner": {
"description": "name owner.",
"allOf": [
{
"$ref": "#/definitions/Addr"
}
]
}
},
"additionalProperties": false
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,163 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "NamesListResponse",
"type": "object",
"required": [
"names"
],
"properties": {
"names": {
"type": "array",
"items": {
"$ref": "#/definitions/RegisteredName"
}
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Address": {
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.",
"oneOf": [
{
"type": "object",
"required": [
"nym_address"
],
"properties": {
"nym_address": {
"$ref": "#/definitions/NymAddressInner"
}
},
"additionalProperties": false
}
]
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"NameDetails": {
"type": "object",
"required": [
"address",
"identity_key",
"name"
],
"properties": {
"address": {
"description": "The address of the name alias.",
"allOf": [
{
"$ref": "#/definitions/Address"
}
]
},
"identity_key": {
"description": "The identity key of the registered name.",
"type": "string"
},
"name": {
"description": "The name pointing to the nym address",
"allOf": [
{
"$ref": "#/definitions/NymName"
}
]
}
},
"additionalProperties": false
},
"NymAddressInner": {
"type": "object",
"required": [
"client_enc",
"client_id",
"gateway_id"
],
"properties": {
"client_enc": {
"type": "string"
},
"client_id": {
"type": "string"
},
"gateway_id": {
"type": "string"
}
},
"additionalProperties": false
},
"NymName": {
"description": "Name stored and pointing a to a nym-address",
"type": "string"
},
"RegisteredName": {
"type": "object",
"required": [
"block_height",
"deposit",
"id",
"name",
"owner"
],
"properties": {
"block_height": {
"description": "Block height at which the name was added.",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"deposit": {
"description": "The deposit used to announce the name.",
"allOf": [
{
"$ref": "#/definitions/Coin"
}
]
},
"id": {
"description": "Unique id assigned to the registerd name.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"name": {
"description": "The registerd name details.",
"allOf": [
{
"$ref": "#/definitions/NameDetails"
}
]
},
"owner": {
"description": "name owner.",
"allOf": [
{
"$ref": "#/definitions/Addr"
}
]
}
},
"additionalProperties": false
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,148 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "RegisteredName",
"type": "object",
"required": [
"block_height",
"deposit",
"id",
"name",
"owner"
],
"properties": {
"block_height": {
"description": "Block height at which the name was added.",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"deposit": {
"description": "The deposit used to announce the name.",
"allOf": [
{
"$ref": "#/definitions/Coin"
}
]
},
"id": {
"description": "Unique id assigned to the registerd name.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"name": {
"description": "The registerd name details.",
"allOf": [
{
"$ref": "#/definitions/NameDetails"
}
]
},
"owner": {
"description": "name owner.",
"allOf": [
{
"$ref": "#/definitions/Addr"
}
]
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Address": {
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.",
"oneOf": [
{
"type": "object",
"required": [
"nym_address"
],
"properties": {
"nym_address": {
"$ref": "#/definitions/NymAddressInner"
}
},
"additionalProperties": false
}
]
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"NameDetails": {
"type": "object",
"required": [
"address",
"identity_key",
"name"
],
"properties": {
"address": {
"description": "The address of the name alias.",
"allOf": [
{
"$ref": "#/definitions/Address"
}
]
},
"identity_key": {
"description": "The identity key of the registered name.",
"type": "string"
},
"name": {
"description": "The name pointing to the nym address",
"allOf": [
{
"$ref": "#/definitions/NymName"
}
]
}
},
"additionalProperties": false
},
"NymAddressInner": {
"type": "object",
"required": [
"client_enc",
"client_id",
"gateway_id"
],
"properties": {
"client_enc": {
"type": "string"
},
"client_id": {
"type": "string"
},
"gateway_id": {
"type": "string"
}
},
"additionalProperties": false
},
"NymName": {
"description": "Name stored and pointing a to a nym-address",
"type": "string"
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,163 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "NamesListResponse",
"type": "object",
"required": [
"names"
],
"properties": {
"names": {
"type": "array",
"items": {
"$ref": "#/definitions/RegisteredName"
}
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Address": {
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.",
"oneOf": [
{
"type": "object",
"required": [
"nym_address"
],
"properties": {
"nym_address": {
"$ref": "#/definitions/NymAddressInner"
}
},
"additionalProperties": false
}
]
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"NameDetails": {
"type": "object",
"required": [
"address",
"identity_key",
"name"
],
"properties": {
"address": {
"description": "The address of the name alias.",
"allOf": [
{
"$ref": "#/definitions/Address"
}
]
},
"identity_key": {
"description": "The identity key of the registered name.",
"type": "string"
},
"name": {
"description": "The name pointing to the nym address",
"allOf": [
{
"$ref": "#/definitions/NymName"
}
]
}
},
"additionalProperties": false
},
"NymAddressInner": {
"type": "object",
"required": [
"client_enc",
"client_id",
"gateway_id"
],
"properties": {
"client_enc": {
"type": "string"
},
"client_id": {
"type": "string"
},
"gateway_id": {
"type": "string"
}
},
"additionalProperties": false
},
"NymName": {
"description": "Name stored and pointing a to a nym-address",
"type": "string"
},
"RegisteredName": {
"type": "object",
"required": [
"block_height",
"deposit",
"id",
"name",
"owner"
],
"properties": {
"block_height": {
"description": "Block height at which the name was added.",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"deposit": {
"description": "The deposit used to announce the name.",
"allOf": [
{
"$ref": "#/definitions/Coin"
}
]
},
"id": {
"description": "Unique id assigned to the registerd name.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"name": {
"description": "The registerd name details.",
"allOf": [
{
"$ref": "#/definitions/NameDetails"
}
]
},
"owner": {
"description": "name owner.",
"allOf": [
{
"$ref": "#/definitions/Addr"
}
]
}
},
"additionalProperties": false
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,35 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ConfigResponse",
"type": "object",
"required": [
"deposit_required"
],
"properties": {
"deposit_required": {
"$ref": "#/definitions/Coin"
}
},
"additionalProperties": false,
"definitions": {
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,20 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ContractVersion",
"type": "object",
"required": [
"contract",
"version"
],
"properties": {
"contract": {
"description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing",
"type": "string"
},
"version": {
"description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)",
"type": "string"
}
},
"additionalProperties": false
}
@@ -1,40 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ContractBuildInformation",
"type": "object",
"required": [
"build_timestamp",
"build_version",
"commit_branch",
"commit_sha",
"commit_timestamp",
"rustc_version"
],
"properties": {
"build_timestamp": {
"description": "Provides the build timestamp, for example `2021-02-23T20:14:46.558472672+00:00`.",
"type": "string"
},
"build_version": {
"description": "Provides the build version, for example `0.1.0-9-g46f83e1`.",
"type": "string"
},
"commit_branch": {
"description": "Provides the name of the git branch that was used for the build, for example `master`.",
"type": "string"
},
"commit_sha": {
"description": "Provides the hash of the commit that was used for the build, for example `46f83e112520533338245862d366f6a02cef07d4`.",
"type": "string"
},
"commit_timestamp": {
"description": "Provides the timestamp of the commit that was used for the build, for example `2021-02-23T08:08:02-05:00`.",
"type": "string"
},
"rustc_version": {
"description": "Provides the rustc version that was used for the build, for example `1.52.0-nightly`.",
"type": "string"
}
},
"additionalProperties": false
}
@@ -1,148 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "RegisteredName",
"type": "object",
"required": [
"block_height",
"deposit",
"id",
"name",
"owner"
],
"properties": {
"block_height": {
"description": "Block height at which the name was added.",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"deposit": {
"description": "The deposit used to announce the name.",
"allOf": [
{
"$ref": "#/definitions/Coin"
}
]
},
"id": {
"description": "Unique id assigned to the registerd name.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"name": {
"description": "The registerd name details.",
"allOf": [
{
"$ref": "#/definitions/NameDetails"
}
]
},
"owner": {
"description": "name owner.",
"allOf": [
{
"$ref": "#/definitions/Addr"
}
]
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Address": {
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id. NOTE: entirely unvalidated.",
"oneOf": [
{
"type": "object",
"required": [
"nym_address"
],
"properties": {
"nym_address": {
"$ref": "#/definitions/NymAddressInner"
}
},
"additionalProperties": false
}
]
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"NameDetails": {
"type": "object",
"required": [
"address",
"identity_key",
"name"
],
"properties": {
"address": {
"description": "The address of the name alias.",
"allOf": [
{
"$ref": "#/definitions/Address"
}
]
},
"identity_key": {
"description": "The identity key of the registered name.",
"type": "string"
},
"name": {
"description": "The name pointing to the nym address",
"allOf": [
{
"$ref": "#/definitions/NymName"
}
]
}
},
"additionalProperties": false
},
"NymAddressInner": {
"type": "object",
"required": [
"client_enc",
"client_id",
"gateway_id"
],
"properties": {
"client_enc": {
"type": "string"
},
"client_id": {
"type": "string"
},
"gateway_id": {
"type": "string"
}
},
"additionalProperties": false
},
"NymName": {
"description": "Name stored and pointing a to a nym-address",
"type": "string"
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,7 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "uint32",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
-14
View File
@@ -1,14 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::write_api;
use nym_name_service_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
fn main() {
write_api! {
instantiate: InstantiateMsg,
query: QueryMsg,
execute: ExecuteMsg,
migrate: MigrateMsg,
}
}
-18
View File
@@ -1,18 +0,0 @@
// We limit the these for simplicity and to avoid having to deal with paging.
pub const MAX_NUMBER_OF_NAMES_PER_OWNER: u32 = 100;
pub const MAX_NUMBER_OF_NAMES_FOR_ADDRESS: u32 = 100;
pub const NAME_DEFAULT_RETRIEVAL_LIMIT: u32 = 100;
pub const NAME_MAX_RETRIEVAL_LIMIT: u32 = 150;
// Storage keys
pub const CONFIG_KEY: &str = "config";
pub const ADMIN_KEY: &str = "admin";
pub const NAME_ID_COUNTER_KEY: &str = "nidc";
pub const NAMES_PK_NAMESPACE: &str = "nanames";
pub const NAMES_OWNER_IDX_NAMESPACE: &str = "naowner";
pub const NAMES_ADDRESS_IDX_NAMESPACE: &str = "naaddress";
pub const NAMES_NAME_IDX_NAMESPACE: &str = "naname";
pub const SIGNING_NONCES_NAMESPACE: &str = "nasn";
-384
View File
@@ -1,384 +0,0 @@
use crate::{
state::{self, Config},
NameServiceError, Result,
};
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response};
use nym_name_service_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
use semver::Version;
mod execute;
mod query;
// version info for migration info
const CONTRACT_NAME: &str = "crate:nym-name-service";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn instantiate(
mut deps: DepsMut<'_>,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Response> {
state::set_admin(deps.branch(), info.sender.clone())?;
let config = Config {
deposit_required: msg.deposit_required,
};
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
state::save_config(deps.storage, &config)?;
Ok(Response::new()
.add_attribute("method", "instantiate")
.add_attribute("admin", info.sender))
}
pub fn migrate(
deps: DepsMut<'_>,
_env: Env,
_msg: MigrateMsg,
) -> Result<Response, NameServiceError> {
// Note: don't remove this particular bit of code as we have to ALWAYS check whether we have to
// update the stored version
let version: Version = CONTRACT_VERSION.parse().map_err(|error: semver::Error| {
NameServiceError::SemVerFailure {
value: CONTRACT_VERSION.to_string(),
error_message: error.to_string(),
}
})?;
let storage_version_raw = cw2::get_contract_version(deps.storage)?.version;
let storage_version: Version =
storage_version_raw
.parse()
.map_err(|error: semver::Error| NameServiceError::SemVerFailure {
value: storage_version_raw,
error_message: error.to_string(),
})?;
if storage_version < version {
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
// If state structure changed in any contract version in the way migration is needed, it
// should occur here, for example anything from `crate::queued_migrations::`
}
Ok(Response::new())
}
pub fn execute(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, NameServiceError> {
match msg {
ExecuteMsg::Register {
name,
owner_signature,
} => execute::register(deps, env, info, name, owner_signature),
ExecuteMsg::DeleteId { name_id } => execute::delete_id(deps, info, name_id),
ExecuteMsg::DeleteName { name } => execute::delete_name(deps, info, name),
ExecuteMsg::UpdateDepositRequired { deposit_required } => {
execute::update_deposit_required(deps, info, deposit_required)
}
}
}
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> Result<Binary> {
let response = match msg {
QueryMsg::NameId { name_id } => to_binary(&query::query_id(deps, name_id)?),
QueryMsg::ByOwner { owner } => to_binary(&query::query_owner(deps, owner)?),
QueryMsg::ByAddress { address } => to_binary(&query::query_address(deps, address)?),
QueryMsg::ByName { name } => to_binary(&query::query_name(deps, name)?),
QueryMsg::All { limit, start_after } => {
to_binary(&query::query_all_paged(deps, limit, start_after)?)
}
QueryMsg::SigningNonce { address } => {
to_binary(&query::query_current_signing_nonce(deps, address)?)
}
QueryMsg::Config {} => to_binary(&query::query_config(deps)?),
QueryMsg::GetContractVersion {} => to_binary(&query::query_contract_version()),
QueryMsg::GetCW2ContractVersion {} => to_binary(&cw2::get_contract_version(deps.storage)?),
};
Ok(response?)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::{
assert::{
assert_config, assert_current_nonce, assert_empty, assert_name, assert_names,
assert_not_found,
},
fixture::new_name_details_with_sign,
helpers::{get_attribute, nyms, test_rng},
};
use cosmwasm_std::{
testing::{mock_dependencies, mock_env, mock_info},
Addr, Coin,
};
use nym_name_service_common::{NameId, RegisteredName};
const DENOM: &str = "unym";
#[test]
fn instantiate_contract() {
let mut deps = mock_dependencies();
let msg = InstantiateMsg {
deposit_required: Coin::new(100u128, DENOM),
};
let info = mock_info("creator", &[]);
let admin = info.sender.clone();
// Instantiate contract
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
// Check that it worked by querying the config, and checking that the list of names is
// empty
assert_config(deps.as_ref(), &admin, Coin::new(100u128, DENOM));
assert_empty(deps.as_ref());
}
#[test]
fn register_fails_deposit_too_small() {
let mut rng = test_rng();
let mut deps = mock_dependencies();
let msg = InstantiateMsg::new(nyms(100));
let info = mock_info("creator", &[]);
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
let deposit = nyms(99);
let owner = "steve";
let (name, owner_signature) =
new_name_details_with_sign(deps.as_mut(), &mut rng, "foo", owner, deposit);
let msg = ExecuteMsg::Register {
name,
owner_signature,
};
assert_eq!(
execute(
deps.as_mut(),
mock_env(),
mock_info(owner, &[nyms(99)]),
msg.clone()
)
.unwrap_err(),
NameServiceError::InsufficientDeposit {
funds: 99u128.into(),
deposit_required: 100u128.into(),
}
);
// Since we signed for 99unym deposit.
assert_eq!(
execute(
deps.as_mut(),
mock_env(),
mock_info(owner, &[nyms(100)]),
msg
)
.unwrap_err(),
NameServiceError::InvalidEd25519Signature,
);
}
#[test]
fn register_fails_deposit_too_large() {
let mut rng = test_rng();
let mut deps = mock_dependencies();
let msg = InstantiateMsg::new(nyms(100));
let info = mock_info("creator", &[]);
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
let deposit = nyms(101);
let owner = "steve";
let (name, owner_signature) =
new_name_details_with_sign(deps.as_mut(), &mut rng, "foo", owner, deposit);
let msg = ExecuteMsg::Register {
name,
owner_signature,
};
assert_eq!(
execute(
deps.as_mut(),
mock_env(),
mock_info(owner, &[nyms(101)]),
msg.clone()
)
.unwrap_err(),
NameServiceError::TooLargeDeposit {
funds: 101u128.into(),
deposit_required: 100u128.into(),
}
);
// Since we signed for 101unym deposit.
assert_eq!(
execute(
deps.as_mut(),
mock_env(),
mock_info(owner, &[nyms(100)]),
msg
)
.unwrap_err(),
NameServiceError::InvalidEd25519Signature,
);
}
#[test]
fn register_fails_owner_mismatch() {
let mut rng = test_rng();
let mut deps = mock_dependencies();
let msg = InstantiateMsg::new(nyms(100));
let info = mock_info("creator", &[]);
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
// Setup
let deposit = nyms(100);
let owner = "steve";
let (name, owner_signature) =
new_name_details_with_sign(deps.as_mut(), &mut rng, "my-name", owner, deposit);
// Register
let msg = ExecuteMsg::Register {
name,
owner_signature,
};
assert_eq!(
execute(
deps.as_mut(),
mock_env(),
mock_info("timmy", &[nyms(100)]),
msg.clone(),
)
.unwrap_err(),
NameServiceError::InvalidEd25519Signature,
);
assert!(execute(
deps.as_mut(),
mock_env(),
mock_info("steve", &[nyms(100)]),
msg
)
.is_ok());
}
#[test]
fn register_success() {
let mut rng = test_rng();
let mut deps = mock_dependencies();
let msg = InstantiateMsg::new(nyms(100));
let info = mock_info("creator", &[]);
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
// Setup
let deposit = nyms(100);
let owner = "steve";
let (name, owner_signature) =
new_name_details_with_sign(deps.as_mut(), &mut rng, "my-name", owner, deposit.clone());
// Register
let msg = ExecuteMsg::Register {
name: name.clone(),
owner_signature,
};
let info = mock_info("steve", &[nyms(100)]);
let res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
// Check that the name has had name id assigned to it
let expected_id = 1;
let id: NameId = get_attribute(&res, "register", "name_id").parse().unwrap();
assert_eq!(id, expected_id);
assert_eq!(
get_attribute(&res, "register", "name"),
"my-name".to_string()
);
// Check that the nonce has been incremented, but only for the owner
assert_current_nonce(deps.as_ref(), &Addr::unchecked("steve"), 1);
assert_current_nonce(deps.as_ref(), &Addr::unchecked("timmy"), 0);
// The expected registered name
let expected_name = RegisteredName {
id: expected_id,
name,
owner: Addr::unchecked(owner),
block_height: 12345,
deposit,
};
assert_names(deps.as_ref(), &[expected_name.clone()]);
assert_name(deps.as_ref(), &expected_name);
}
#[test]
fn delete() {
let mut rng = test_rng();
let mut deps = mock_dependencies();
let msg = InstantiateMsg::new(Coin::new(100, "unym"));
let info = mock_info("creator", &[]);
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
// Register
let deposit = nyms(100);
let steve = "steve";
let (name, owner_signature) =
new_name_details_with_sign(deps.as_mut(), &mut rng, "my-name", steve, deposit.clone());
let msg = ExecuteMsg::Register {
name: name.clone(),
owner_signature,
};
let info_steve = mock_info("steve", &[nyms(100)]);
execute(deps.as_mut(), mock_env(), info_steve.clone(), msg).unwrap();
// The expected registerd name
let expected_id = 1;
let expected_name = RegisteredName {
id: expected_id,
name,
owner: Addr::unchecked(steve),
block_height: 12345,
deposit,
};
assert_names(deps.as_ref(), &[expected_name]);
// Removing someone else's name will fail
let msg = ExecuteMsg::delete_id(expected_id);
let info_timmy = mock_info("timmy", &[]);
assert_eq!(
execute(deps.as_mut(), mock_env(), info_timmy, msg).unwrap_err(),
NameServiceError::Unauthorized {
sender: Addr::unchecked("timmy")
}
);
// Removing an non-existent name will fail
let msg = ExecuteMsg::delete_id(expected_id + 1);
assert_eq!(
execute(deps.as_mut(), mock_env(), info_steve.clone(), msg).unwrap_err(),
NameServiceError::NotFound {
name_id: expected_id + 1
}
);
// Remove as correct owner succeeds
let msg = ExecuteMsg::delete_id(expected_id);
let res = execute(deps.as_mut(), mock_env(), info_steve, msg).unwrap();
assert_eq!(
get_attribute(&res, "delete_id", "name_id"),
expected_id.to_string()
);
assert_names(deps.as_ref(), &[]);
assert_not_found(deps.as_ref(), expected_id);
}
}
@@ -1,224 +0,0 @@
use crate::{
constants::{MAX_NUMBER_OF_NAMES_FOR_ADDRESS, MAX_NUMBER_OF_NAMES_PER_OWNER},
state, NameServiceError, Result,
};
use cosmwasm_std::{Addr, BankMsg, Coin, Deps, DepsMut, Env, MessageInfo, Response, Uint128};
use nym_contracts_common::{
signing::{MessageSignature, Verifier},
IdentityKey,
};
use nym_name_service_common::{
events::{
new_delete_id_event, new_delete_name_event, new_register_event,
new_update_deposit_required_event,
},
signing_types::construct_name_register_sign_payload,
Address, NameDetails, NameId, NymName, RegisteredName,
};
use super::query;
fn ensure_correct_deposit(will_deposit: Uint128, deposit_required: Uint128) -> Result<()> {
match will_deposit.cmp(&deposit_required) {
std::cmp::Ordering::Less => Err(NameServiceError::InsufficientDeposit {
funds: will_deposit,
deposit_required,
}),
std::cmp::Ordering::Equal => Ok(()),
std::cmp::Ordering::Greater => Err(NameServiceError::TooLargeDeposit {
funds: will_deposit,
deposit_required,
}),
}
}
fn ensure_max_names_per_owner(deps: Deps, owner: Addr) -> Result<()> {
let current_entries = query::query_owner(deps, owner.to_string())?;
if current_entries.names.len() < MAX_NUMBER_OF_NAMES_PER_OWNER as usize {
Ok(())
} else {
Err(NameServiceError::ReachedMaxNamesForOwner {
max_names: MAX_NUMBER_OF_NAMES_PER_OWNER,
owner,
})
}
}
fn ensure_max_names_per_address(deps: Deps, address: Address) -> Result<()> {
let current_entries = query::query_address(deps, address.clone())?;
if current_entries.names.len() < MAX_NUMBER_OF_NAMES_FOR_ADDRESS as usize {
Ok(())
} else {
Err(NameServiceError::ReachedMaxNamesForAddress {
max_names: MAX_NUMBER_OF_NAMES_FOR_ADDRESS,
address,
})
}
}
fn ensure_name_exists(deps: Deps, name_id: NameId) -> Result<()> {
if state::names::has_name_id(deps.storage, name_id) {
Ok(())
} else {
Err(NameServiceError::NotFound { name_id })
}
}
fn ensure_name_not_exists(deps: Deps, name: &NymName) -> Result<()> {
if state::names::has_name(deps.storage, name) {
Err(NameServiceError::NameAlreadyRegistered { name: name.clone() })
} else {
Ok(())
}
}
fn ensure_identity_key_is_part_of_nym_address(address: &Address, identity_key: &str) -> Result<()> {
if address.client_id() == identity_key {
Ok(())
} else {
Err(NameServiceError::IdentityKeyMismatch {
address: address.clone(),
identity_key: identity_key.to_string(),
})
}
}
fn ensure_sender_authorized(info: MessageInfo, names: &RegisteredName) -> Result<()> {
if info.sender == names.owner {
Ok(())
} else {
Err(NameServiceError::Unauthorized {
sender: info.sender,
})
}
}
fn return_deposit(name_to_delete: &RegisteredName) -> BankMsg {
BankMsg::Send {
to_address: name_to_delete.owner.to_string(),
amount: vec![name_to_delete.deposit.clone()],
}
}
fn verify_register_signature(
deps: Deps<'_>,
sender: Addr,
deposit: Coin,
name: NameDetails,
signature: MessageSignature,
) -> Result<()> {
// recover the public key
let public_key = decode_ed25519_identity_key(&name.identity_key)?;
// reconstruct the payload
let nonce = state::get_signing_nonce(deps.storage, sender.clone())?;
let msg = construct_name_register_sign_payload(nonce, sender, deposit, name);
if deps.api.verify_message(msg, signature, &public_key)? {
Ok(())
} else {
Err(NameServiceError::InvalidEd25519Signature)
}
}
fn decode_ed25519_identity_key(encoded: &IdentityKey) -> Result<[u8; 32]> {
let mut public_key = [0u8; 32];
let used = bs58::decode(encoded)
.into(&mut public_key)
.map_err(|err| NameServiceError::MalformedEd25519IdentityKey(err.to_string()))?;
if used != 32 {
return Err(NameServiceError::MalformedEd25519IdentityKey(
"Too few bytes provided for the public key".into(),
));
}
Ok(public_key)
}
/// Register a new name. It will be assigned a new name id.
pub fn register(
deps: DepsMut,
env: Env,
info: MessageInfo,
name: NameDetails,
owner_signature: MessageSignature,
) -> Result<Response> {
ensure_identity_key_is_part_of_nym_address(&name.address, &name.identity_key)?;
ensure_name_not_exists(deps.as_ref(), &name.name)?;
ensure_max_names_per_owner(deps.as_ref(), info.sender.clone())?;
ensure_max_names_per_address(deps.as_ref(), name.address.clone())?;
let deposit_required = state::deposit_required(deps.storage)?;
let denom = deposit_required.denom.clone();
let will_deposit = cw_utils::must_pay(&info, &denom)
.map_err(|err| NameServiceError::DepositRequired { source: err })?;
ensure_correct_deposit(will_deposit, deposit_required.amount)?;
let deposit = Coin::new(will_deposit.u128(), denom);
verify_register_signature(
deps.as_ref(),
info.sender.clone(),
deposit.clone(),
name.clone(),
owner_signature,
)?;
state::increment_signing_nonce(deps.storage, info.sender.clone())?;
let id = state::next_name_id_counter(deps.storage)?;
let new_name = RegisteredName {
id,
name,
owner: info.sender,
block_height: env.block.height,
deposit,
};
state::names::save(deps.storage, &new_name)?;
Ok(Response::new().add_event(new_register_event(new_name)))
}
/// Delete an exsisting name.
pub fn delete_id(deps: DepsMut, info: MessageInfo, name_id: NameId) -> Result<Response> {
ensure_name_exists(deps.as_ref(), name_id)?;
let name_to_delete = state::names::load_id(deps.storage, name_id)?;
ensure_sender_authorized(info, &name_to_delete)?;
state::names::remove_id(deps.storage, name_id)?;
let return_deposit_msg = return_deposit(&name_to_delete);
Ok(Response::new()
.add_message(return_deposit_msg)
.add_event(new_delete_id_event(name_to_delete)))
}
/// Delete an existing name by name.
pub(crate) fn delete_name(deps: DepsMut, info: MessageInfo, name: NymName) -> Result<Response> {
let name_to_delete = query::query_name(deps.as_ref(), name)?;
ensure_sender_authorized(info, &name_to_delete)?;
state::names::remove_id(deps.storage, name_to_delete.id)?;
let return_deposit_msg = return_deposit(&name_to_delete);
Ok(Response::new()
.add_message(return_deposit_msg)
.add_event(new_delete_name_event(name_to_delete)))
}
/// Update the deposit required to register new names
pub(crate) fn update_deposit_required(
deps: DepsMut,
info: MessageInfo,
deposit_required: Coin,
) -> Result<Response> {
state::assert_admin(deps.as_ref(), &info.sender)?;
let mut config = state::load_config(deps.storage)?;
config.deposit_required = deposit_required.clone();
state::save_config(deps.storage, &config)?;
Ok(Response::new().add_event(new_update_deposit_required_event(deposit_required)))
}
@@ -1,71 +0,0 @@
use cosmwasm_std::Deps;
use nym_contracts_common::{signing::Nonce, ContractBuildInformation};
use nym_name_service_common::{
response::{ConfigResponse, NamesListResponse, PagedNamesListResponse},
Address, NameId, NymName, RegisteredName,
};
use crate::{
state::{self, names::PagedLoad},
Result,
};
pub fn query_id(deps: Deps, name_id: NameId) -> Result<RegisteredName> {
state::names::load_id(deps.storage, name_id)
}
pub fn query_owner(deps: Deps, owner: String) -> Result<NamesListResponse> {
let owner = deps.api.addr_validate(&owner)?;
let names = state::names::load_owner(deps.storage, owner)?;
Ok(NamesListResponse::new(names))
}
pub fn query_address(deps: Deps, address: Address) -> Result<NamesListResponse> {
let names = state::names::load_address(deps.storage, &address)?;
Ok(NamesListResponse::new(names))
}
pub fn query_name(deps: Deps, name: NymName) -> Result<RegisteredName> {
state::names::load_name(deps.storage, &name)
}
pub fn query_all_paged(
deps: Deps,
limit: Option<u32>,
start_after: Option<NameId>,
) -> Result<PagedNamesListResponse> {
let PagedLoad {
names,
limit,
start_next_after,
} = state::names::load_all_paged(deps.storage, limit, start_after)?;
Ok(PagedNamesListResponse::new(names, limit, start_next_after))
}
pub fn query_current_signing_nonce(deps: Deps<'_>, address: String) -> Result<Nonce> {
let address = deps.api.addr_validate(&address)?;
state::get_signing_nonce(deps.storage, address)
}
pub fn query_config(deps: Deps) -> Result<ConfigResponse> {
let config = state::load_config(deps.storage)?;
Ok(config.into())
}
pub fn query_contract_version() -> ContractBuildInformation {
// as per docs
// env! macro will expand to the value of the named environment variable at
// compile time, yielding an expression of type `&'static str`
ContractBuildInformation {
build_timestamp: env!("VERGEN_BUILD_TIMESTAMP").to_string(),
build_version: env!("VERGEN_BUILD_SEMVER").to_string(),
commit_sha: option_env!("VERGEN_GIT_SHA").unwrap_or("NONE").to_string(),
commit_timestamp: option_env!("VERGEN_GIT_COMMIT_TIMESTAMP")
.unwrap_or("NONE")
.to_string(),
commit_branch: option_env!("VERGEN_GIT_BRANCH")
.unwrap_or("NONE")
.to_string(),
rustc_version: env!("VERGEN_RUSTC_SEMVER").to_string(),
}
}
@@ -1,143 +0,0 @@
use cosmwasm_std::Addr;
use nym_name_service_common::{response::PagedNamesListResponse, NymName};
use rstest::rstest;
use crate::{
constants::NAME_DEFAULT_RETRIEVAL_LIMIT,
test_helpers::{fixture::new_name, helpers::nyms},
NameServiceError,
};
use super::test_setup::TestSetup;
#[rstest::fixture]
fn setup() -> TestSetup {
TestSetup::new()
}
#[rstest]
fn delete_name(mut setup: TestSetup) {
setup.sign_and_register(
&NymName::new("my_name").unwrap(),
&Addr::unchecked("owner"),
&nyms(100),
);
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(setup.balance("owner"), nyms(150));
assert!(!setup.query_all().names.is_empty());
setup.delete(1, Addr::unchecked("owner"));
// Deleting the name returns the deposit to the owner
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance("owner"), nyms(250));
assert!(setup.query_all().names.is_empty());
}
#[rstest]
fn only_owner_can_delete_name(mut setup: TestSetup) {
assert_eq!(setup.contract_balance(), nyms(0));
setup.sign_and_register(
&NymName::new("name").unwrap(),
&Addr::unchecked("owner"),
&nyms(100),
);
assert_eq!(setup.contract_balance(), nyms(100));
assert!(!setup.query_all().names.is_empty());
let delete_resp = setup
.try_delete(1, Addr::unchecked("not_owner"))
.unwrap_err();
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(
delete_resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::Unauthorized {
sender: Addr::unchecked("not_owner")
}
);
}
#[rstest]
fn cant_delete_name_that_does_not_exist(mut setup: TestSetup) {
setup.sign_and_register(
&NymName::new("foo").unwrap(),
&Addr::unchecked("owner"),
&nyms(100),
);
assert_eq!(setup.contract_balance(), nyms(100));
assert!(!setup.query_all().names.is_empty());
let delete_resp = setup.try_delete(0, Addr::unchecked("owner")).unwrap_err();
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(
delete_resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::NotFound { name_id: 0 }
);
let delete_resp = setup.try_delete(2, Addr::unchecked("owner")).unwrap_err();
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(
delete_resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::NotFound { name_id: 2 }
);
assert!(!setup.query_all().names.is_empty());
setup.delete(1, Addr::unchecked("owner"));
assert_eq!(setup.contract_balance(), nyms(0));
assert!(setup.query_all().names.is_empty());
}
#[rstest]
fn register_multiple_names_and_deleting_by_name(mut setup: TestSetup) {
let owner1 = Addr::unchecked("wealthy_owner_1");
let owner2 = Addr::unchecked("wealthy_owner_2");
let name1 = NymName::new("name1").unwrap();
let name2 = NymName::new("name2").unwrap();
let name3 = NymName::new("name3").unwrap();
let name4 = NymName::new("name4").unwrap();
let name5 = NymName::new("name5").unwrap();
// We register the same address three times, but with different owners
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance(&owner1), nyms(1000));
let s1 = setup.sign_and_register(&name1, &owner1, &nyms(100));
let s2 = setup.sign_and_register(&name2, &owner1, &nyms(100));
let s3 = setup.sign_and_register(&name3, &owner1, &nyms(100));
let s4 = setup.sign_and_register(&name4, &owner2, &nyms(100));
let s5 = setup.sign_and_register(&name5, &owner2, &nyms(100));
assert_eq!(setup.contract_balance(), nyms(500));
assert_eq!(setup.balance(&owner1), nyms(700));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
new_name(1, &name1, s1.address(), &owner1, s1.identity_key()),
new_name(2, &name2, s2.address(), &owner1, s2.identity_key()),
new_name(3, &name3, s3.address(), &owner1, s3.identity_key()),
new_name(4, &name4, s4.address(), &owner2, s4.identity_key()),
new_name(5, &name5, s5.address(), &owner2, s5.identity_key()),
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(5),
}
);
setup.delete_name(name1, owner1.clone());
assert_eq!(setup.contract_balance(), nyms(400));
assert_eq!(setup.balance(&owner1), nyms(800));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
new_name(2, &name2, s2.address(), &owner1, s2.identity_key()),
new_name(3, &name3, s3.address(), &owner1, s3.identity_key()),
new_name(4, &name4, s4.address(), &owner2, s4.identity_key()),
new_name(5, &name5, s5.address(), &owner2, s5.identity_key()),
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(5),
}
);
}
@@ -1,13 +0,0 @@
//! Integration tests using cw-multi-test.
mod delete;
mod name_id;
mod query;
mod register;
mod test_name;
mod test_setup;
#[test]
fn instantiate_contract() {
test_setup::TestSetup::new();
}
@@ -1,66 +0,0 @@
use cosmwasm_std::Addr;
use nym_name_service_common::NymName;
use crate::test_helpers::{fixture::new_name, helpers::nyms};
use super::test_setup::TestSetup;
#[test]
fn name_id_is_not_resused_when_deleting_and_then_adding_a_new_names() {
let mut setup = TestSetup::new();
setup.sign_and_register(
&NymName::new("myname1").unwrap(),
&Addr::unchecked("owner1"),
&nyms(100),
);
let s2 = setup.sign_and_register(
&NymName::new("myname2").unwrap(),
&Addr::unchecked("owner2"),
&nyms(100),
);
setup.sign_and_register(
&NymName::new("myname3").unwrap(),
&Addr::unchecked("owner3"),
&nyms(100),
);
setup.delete(1, Addr::unchecked("owner1"));
setup.delete(3, Addr::unchecked("owner3"));
assert_eq!(
setup.query_all().names,
vec![new_name(
2,
&NymName::new("myname2").unwrap(),
s2.address(),
&Addr::unchecked("owner2"),
s2.identity_key(),
)]
);
let s4 = setup.sign_and_register(
&NymName::new("myname4").unwrap(),
&Addr::unchecked("owner4"),
&nyms(100),
);
assert_eq!(
setup.query_all().names,
vec![
new_name(
2,
&NymName::new("myname2").unwrap(),
s2.address(),
&Addr::unchecked("owner2"),
s2.identity_key(),
),
new_name(
4,
&NymName::new("myname4").unwrap(),
s4.address(),
&Addr::unchecked("owner4"),
s4.identity_key(),
)
]
);
}
@@ -1,85 +0,0 @@
use cosmwasm_std::Addr;
use nym_name_service_common::{
response::{ConfigResponse, PagedNamesListResponse},
NymName,
};
use crate::{
constants::NAME_DEFAULT_RETRIEVAL_LIMIT,
test_helpers::{fixture::new_name, helpers::nyms},
};
use super::test_setup::TestSetup;
#[test]
fn query_config() {
assert_eq!(
TestSetup::new().query_config(),
ConfigResponse {
deposit_required: nyms(100),
}
);
}
#[test]
fn check_paging() {
let mut setup = TestSetup::new();
let owner1 = Addr::unchecked("wealthy_owner_1");
let owner2 = Addr::unchecked("wealthy_owner_2");
let name1 = NymName::new("name1").unwrap();
let name2 = NymName::new("name2").unwrap();
let name3 = NymName::new("name3").unwrap();
let name4 = NymName::new("name4").unwrap();
let name5 = NymName::new("name5").unwrap();
// We register the same address three times, but with different owners
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance(&owner1), nyms(1000));
let s1 = setup.sign_and_register(&name1, &owner1, &nyms(100));
let s2 = setup.sign_and_register(&name2, &owner1, &nyms(100));
let s3 = setup.sign_and_register(&name3, &owner1, &nyms(100));
let s4 = setup.sign_and_register(&name4, &owner2, &nyms(100));
let s5 = setup.sign_and_register(&name5, &owner2, &nyms(100));
assert_eq!(setup.contract_balance(), nyms(500));
assert_eq!(setup.balance(&owner1), nyms(700));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
new_name(1, &name1, s1.address(), &owner1, s1.identity_key()),
new_name(2, &name2, s2.address(), &owner1, s2.identity_key()),
new_name(3, &name3, s3.address(), &owner1, s3.identity_key()),
new_name(4, &name4, s4.address(), &owner2, s4.identity_key()),
new_name(5, &name5, s5.address(), &owner2, s5.identity_key()),
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(5),
}
);
setup.delete_name(name1, owner1.clone());
assert_eq!(
setup.query_all_with_limit(Some(2), None),
PagedNamesListResponse {
names: vec![
new_name(2, &name2, s2.address(), &owner1, s2.identity_key()),
new_name(3, &name3, s3.address(), &owner1, s3.identity_key()),
],
per_page: 2,
start_next_after: Some(3),
}
);
assert_eq!(
setup.query_all_with_limit(Some(2), Some(2)),
PagedNamesListResponse {
names: vec![
new_name(3, &name3, s3.address(), &owner1, s3.identity_key()),
new_name(4, &name4, s4.address(), &owner2, s4.identity_key()),
],
per_page: 2,
start_next_after: Some(4),
}
);
}
@@ -1,275 +0,0 @@
use cosmwasm_std::Addr;
use nym_crypto::asymmetric::identity;
use nym_name_service_common::{
error::NameServiceError, response::PagedNamesListResponse, NameDetails, NymName, RegisteredName,
};
use rstest::rstest;
use crate::{
constants::NAME_DEFAULT_RETRIEVAL_LIMIT,
test_helpers::{fixture::new_name, helpers::nyms},
};
use super::test_setup::TestSetup;
#[rstest::fixture]
fn setup() -> TestSetup {
TestSetup::new()
}
#[rstest]
fn basic_register(mut setup: TestSetup) {
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: None,
}
);
// Register a first name
let owner = Addr::unchecked("owner");
let name = NymName::new("steves-server").unwrap();
let (nym_address, id_keypair) = setup.new_nym_address();
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance(&owner), nyms(250));
assert_eq!(setup.query_signing_nonce(owner.to_string()), 0);
let reg_name = setup.new_name_from_address(&name, &nym_address, id_keypair);
let payload = setup.payload_to_sign(&owner, &nyms(100), &reg_name.name);
let reg_name = reg_name.sign(payload);
setup.register(&reg_name, &owner);
// Confirm that the client id in the name matches the identity key.
let address_client_id = reg_name.name.address.client_id();
let identity_key = reg_name.keys.public_key().to_base58_string();
assert_eq!(address_client_id, &identity_key);
// Deposit is deposited to contract and deducted from owners's balance
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(setup.balance(&owner), nyms(150));
// The signing nonce has been incremented
assert_eq!(setup.query_signing_nonce(owner.to_string()), 1);
// We can query the full name list
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![RegisteredName {
id: 1,
name: NameDetails {
name: name.clone(),
address: nym_address.clone(),
identity_key: reg_name.identity_key().to_string(),
},
owner: owner.clone(),
block_height: 12345,
deposit: nyms(100),
}],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(1),
}
);
// ... and we can query by id
assert_eq!(
setup.query_id(1),
RegisteredName {
id: 1,
name: reg_name.details().clone(),
owner: owner.clone(),
block_height: 12345,
deposit: nyms(100),
}
);
// Register a second name
let owner2 = Addr::unchecked("owner2");
let name2 = NymName::new("another_server").unwrap();
let reg_name2 = setup.new_signed_name(&name2, &owner2, &nyms(100));
let nym_address2 = reg_name2.address().clone();
setup.register(&reg_name2, &owner2);
assert_eq!(setup.contract_balance(), nyms(200));
assert_eq!(
setup.query_all(),
PagedNamesListResponse {
names: vec![
new_name(1, &name, &nym_address, &owner, reg_name.identity_key()),
new_name(2, &name2, &nym_address2, &owner2, reg_name2.identity_key()),
],
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(2),
}
);
}
#[rstest]
fn register_fails_when_owner_mismatch(mut setup: TestSetup) {
let owner = Addr::unchecked("owner");
let name = NymName::new("steves-server").unwrap();
let reg_name = setup.new_signed_name(&name, &owner, &nyms(100));
let res = setup
.try_register(&reg_name, &Addr::unchecked("owner2"))
.unwrap_err();
assert_eq!(
res.downcast::<NameServiceError>().unwrap(),
NameServiceError::InvalidEd25519Signature
);
}
#[rstest]
fn signing_nonce_is_increased_when_registering(mut setup: TestSetup) {
let owner1 = Addr::unchecked("owner1");
let owner2 = Addr::unchecked("owner2");
assert_eq!(setup.query_signing_nonce(owner1.to_string()), 0);
assert_eq!(setup.query_signing_nonce(owner2.to_string()), 0);
setup.sign_and_register(&NymName::new("myname1").unwrap(), &owner1, &nyms(100));
assert_eq!(setup.query_signing_nonce(owner1.to_string()), 1);
assert_eq!(setup.query_signing_nonce(owner2.to_string()), 0);
setup.sign_and_register(&NymName::new("myname2").unwrap(), &owner2, &nyms(100));
assert_eq!(setup.query_signing_nonce(owner1.to_string()), 1);
assert_eq!(setup.query_signing_nonce(owner2.to_string()), 1);
setup.sign_and_register(&NymName::new("myname3").unwrap(), &owner2, &nyms(100));
assert_eq!(setup.query_signing_nonce(owner1.to_string()), 1);
assert_eq!(setup.query_signing_nonce(owner2.to_string()), 2);
}
#[rstest]
fn creating_two_names_in_a_row_without_announcing_fails(mut setup: TestSetup) {
let owner = Addr::unchecked("wealthy_owner_1");
let name1 = NymName::new("steves-server1").unwrap();
let name2 = NymName::new("steves-server2").unwrap();
let deposit = nyms(100);
let s1 = setup.new_signed_name(&name1, &owner, &deposit);
// This second name will be signed with the same nonce
let s2 = setup.new_signed_name(&name2, &owner, &deposit);
// Announce the first service works, and this increments the nonce
setup.register(&s1, &owner);
// Now the nonce has been incremented, and the signature will not match
let resp: NameServiceError = setup
.try_register(&s2, &owner)
.unwrap_err()
.downcast()
.unwrap();
assert_eq!(resp, NameServiceError::InvalidEd25519Signature,);
}
#[rstest]
fn cant_register_a_name_without_funds(mut setup: TestSetup) {
assert_eq!(setup.contract_balance(), nyms(0));
assert_eq!(setup.balance("owner"), nyms(250));
let name1 = setup.new_signed_name(
&NymName::new("my_name").unwrap(),
&Addr::unchecked("owner"),
&nyms(100),
);
setup.register(&name1, &Addr::unchecked("owner"));
assert_eq!(setup.contract_balance(), nyms(100));
assert_eq!(setup.balance("owner"), nyms(150));
let name2 = setup.new_signed_name(
&NymName::new("my_name2").unwrap(),
&Addr::unchecked("owner"),
&nyms(100),
);
setup.register(&name2, &Addr::unchecked("owner"));
assert_eq!(setup.contract_balance(), nyms(200));
assert_eq!(setup.balance("owner"), nyms(50));
let name3 = setup.new_signed_name(
&NymName::new("my_name3").unwrap(),
&Addr::unchecked("owner"),
&nyms(100),
);
let res = setup
.try_register(&name3, &Addr::unchecked("owner"))
.unwrap_err();
assert_eq!(
res.downcast::<cosmwasm_std::StdError>().unwrap(),
cosmwasm_std::StdError::Overflow {
source: cosmwasm_std::OverflowError::new(
cosmwasm_std::OverflowOperation::Sub,
"50",
"100"
)
}
);
}
#[rstest]
fn cant_register_the_same_name_multiple_times(mut setup: TestSetup) {
let name1 = setup.new_signed_name(
&NymName::new("name").unwrap(),
&Addr::unchecked("owner"),
&nyms(100),
);
setup.register(&name1, &Addr::unchecked("owner"));
let resp = setup
.try_register(&name1, &Addr::unchecked("owner"))
.unwrap_err();
assert_eq!(
resp.downcast::<NameServiceError>().unwrap(),
NameServiceError::NameAlreadyRegistered {
name: NymName::new("name").unwrap()
}
);
}
fn clone_keys(keys: &identity::KeyPair) -> identity::KeyPair {
let priv_bytes = keys.private_key().to_bytes();
let pub_bytes = keys.public_key().to_bytes();
identity::KeyPair::from_bytes(&priv_bytes, &pub_bytes).unwrap()
}
#[rstest]
fn can_register_multiple_names_for_the_same_nym_address(mut setup: TestSetup) {
let name1 = NymName::new("name1").unwrap();
let name2 = NymName::new("name2").unwrap();
// let address = Address::new("nym.add@ress").unwrap();
let (address, id_keys) = setup.new_nym_address();
let owner = Addr::unchecked("owner");
// We duplicate the keypair here to ensure that the same keypair is used for both names.
// The private key lacks a clone method, for good reason, so we have to serialize and
// deserialize it.
let id_keys2 = clone_keys(&id_keys);
assert_eq!(id_keys.public_key(), id_keys2.public_key());
assert_eq!(
id_keys.private_key().to_base58_string(),
id_keys2.private_key().to_base58_string()
);
// let reg_name1 = setup.new_signed_name(&name1, &owner, &nyms(100));
let reg_name1 = setup.new_name_from_address(&name1, &address, id_keys);
let payload = setup.payload_to_sign(&owner, &nyms(100), &reg_name1.name);
let reg_name1 = reg_name1.sign(payload);
setup.register(&reg_name1, &owner);
// let reg_name2 = setup.new_signed_name(&name2, &owner, &nyms(100));
let reg_name2 = setup.new_name_from_address(&name2, &address, id_keys2);
let payload = setup.payload_to_sign(&owner, &nyms(100), &reg_name2.name);
let reg_name2 = reg_name2.sign(payload);
setup.register(&reg_name2, &owner);
assert_eq!(
setup.query_all().names,
vec![
new_name(1, &name1, &address, &owner, reg_name1.identity_key()),
new_name(2, &name2, &address, &owner, reg_name2.identity_key()),
],
);
}
@@ -1,78 +0,0 @@
use nym_contracts_common::{signing::MessageSignature, IdentityKey};
use nym_crypto::asymmetric::identity;
use nym_name_service_common::{
signing_types::SignableNameRegisterMsg, Address, NameDetails, NymName,
};
use crate::test_helpers::signing::ed25519_sign_message;
pub struct TestName {
pub name: NameDetails,
pub id_keys: identity::KeyPair,
}
impl TestName {
pub fn new(name: NymName, address: Address, id_keys: identity::KeyPair) -> Self {
let identity_key = id_keys.public_key().to_base58_string();
assert_eq!(
identity_key,
address.client_id().to_string(),
"address and identity key must match"
);
let name = NameDetails {
name,
address,
identity_key,
};
Self { name, id_keys }
}
pub fn identity_key(&self) -> &IdentityKey {
&self.name.identity_key
}
pub fn details(&self) -> &NameDetails {
&self.name
}
pub fn sign(self, payload: SignableNameRegisterMsg) -> SignedTestName {
let owner_signature = ed25519_sign_message(payload, self.id_keys.private_key());
SignedTestName {
name: self.name,
keys: self.id_keys,
owner_signature,
}
}
}
impl From<TestName> for NameDetails {
fn from(test_name: TestName) -> Self {
test_name.name
}
}
pub struct SignedTestName {
pub name: NameDetails,
pub keys: identity::KeyPair,
pub owner_signature: MessageSignature,
}
impl SignedTestName {
pub fn identity_key(&self) -> &IdentityKey {
&self.name.identity_key
}
pub fn details(&self) -> &NameDetails {
&self.name
}
pub fn address(&self) -> &Address {
&self.name.address
}
}
impl From<SignedTestName> for NameDetails {
fn from(signed_name: SignedTestName) -> Self {
signed_name.name
}
}
@@ -1,222 +0,0 @@
use cosmwasm_std::{coins, Addr, Coin, Uint128};
use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor};
use nym_contracts_common::signing::Nonce;
use nym_crypto::asymmetric::identity;
use nym_name_service_common::{
msg::{ExecuteMsg, InstantiateMsg, QueryMsg},
response::{ConfigResponse, PagedNamesListResponse},
signing_types::{construct_name_register_sign_payload, SignableNameRegisterMsg},
Address, NameDetails, NameId, NymName, RegisteredName,
};
use rand_chacha::ChaCha20Rng;
use serde::de::DeserializeOwned;
use crate::test_helpers::helpers::{get_app_attribute, test_rng};
use super::test_name::{SignedTestName, TestName};
const DENOM: &str = "unym";
const ADDRESSES: &[&str] = &[
"user", "admin", "owner", "owner1", "owner2", "owner3", "owner4",
];
const WEALTHY_ADDRESSES: &[&str] = &["wealthy_owner_1", "wealthy_owner_2"];
/// Helper for being able to systematic integration tests
pub struct TestSetup {
app: App,
addr: Addr,
rng: ChaCha20Rng,
}
impl Default for TestSetup {
fn default() -> Self {
TestSetup::new()
}
}
impl TestSetup {
pub fn new() -> Self {
let mut app = AppBuilder::new().build(|router, _, storage| {
let mut init_balance = |account: &str, amount: u128| {
router
.bank
.init_balance(storage, &Addr::unchecked(account), coins(amount, DENOM))
.unwrap();
};
ADDRESSES.iter().for_each(|addr| init_balance(addr, 250));
WEALTHY_ADDRESSES
.iter()
.for_each(|addr| init_balance(addr, 1000));
});
let code = ContractWrapper::new(crate::execute, crate::instantiate, crate::query);
let code_id = app.store_code(Box::new(code));
let addr = Self::instantiate(&mut app, code_id);
let rng = test_rng();
TestSetup { app, addr, rng }
}
fn instantiate(app: &mut App, code_id: u64) -> Addr {
app.instantiate_contract(
code_id,
Addr::unchecked("admin"),
&InstantiateMsg {
deposit_required: Coin::new(100, DENOM),
},
&[],
"contract_label",
None,
)
.unwrap()
}
pub fn contract_balance(&self) -> Coin {
self.app.wrap().query_balance(&self.addr, DENOM).unwrap()
}
pub fn query<T: DeserializeOwned>(&self, query_msg: &QueryMsg) -> T {
self.app
.wrap()
.query_wasm_smart(&self.addr, query_msg)
.unwrap()
}
pub fn query_config(&self) -> ConfigResponse {
self.query(&QueryMsg::Config {})
}
pub fn query_id(&self, name_id: NameId) -> RegisteredName {
self.query(&QueryMsg::NameId { name_id })
}
pub fn query_all(&self) -> PagedNamesListResponse {
self.query(&QueryMsg::all())
}
pub fn query_all_with_limit(
&self,
limit: Option<u32>,
start_after: Option<u32>,
) -> PagedNamesListResponse {
self.query(&QueryMsg::All { limit, start_after })
}
pub fn query_signing_nonce(&self, address: String) -> Nonce {
self.query(&QueryMsg::SigningNonce { address })
}
// Create a new random address and a corresponding keypair.
pub fn new_nym_address(&mut self) -> (Address, identity::KeyPair) {
crate::test_helpers::fixture::new_address(&mut self.rng)
}
// Create a new name with a given address. It's up to the caller to make sure that the keys
// match.
pub fn new_name_from_address(
&mut self,
name: &NymName,
address: &Address,
id_keys: identity::KeyPair,
) -> TestName {
TestName::new(name.clone(), address.clone(), id_keys)
}
// Create a new name with a random address
pub fn new_name(&mut self, name: &NymName) -> TestName {
let (address, id_keys) = self.new_nym_address();
self.new_name_from_address(name, &address, id_keys)
}
pub fn payload_to_sign(
&mut self,
owner: &Addr,
deposit: &Coin,
name: &NameDetails,
) -> SignableNameRegisterMsg {
let nonce = self.query_signing_nonce(owner.to_string());
construct_name_register_sign_payload(nonce, owner.clone(), deposit.clone(), name.clone())
}
pub fn new_signed_name(
&mut self,
name: &NymName,
owner: &Addr,
deposit: &Coin,
) -> SignedTestName {
let name = self.new_name(name);
let payload = self.payload_to_sign(owner, deposit, name.details());
name.sign(payload)
}
pub fn try_register(
&mut self,
name: &SignedTestName,
owner: &Addr,
) -> anyhow::Result<AppResponse> {
self.app.execute_contract(
owner.clone(),
self.addr.clone(),
&ExecuteMsg::Register {
name: name.name.clone(),
owner_signature: name.owner_signature.clone(),
},
&[Coin {
denom: DENOM.to_string(),
amount: Uint128::new(100),
}],
)
}
pub fn register(&mut self, name: &SignedTestName, owner: &Addr) -> AppResponse {
let resp = self.try_register(name, owner).unwrap();
assert_eq!(
get_app_attribute(&resp, "wasm-register", "action"),
"register"
);
resp
}
// Convenience function for creating a new signed name, and regsitering it
pub fn sign_and_register(
&mut self,
name: &NymName,
owner: &Addr,
deposit: &Coin,
) -> SignedTestName {
let signed_name = self.new_signed_name(name, owner, deposit);
self.register(&signed_name, owner);
signed_name
}
pub fn try_delete(&mut self, name_id: NameId, owner: Addr) -> anyhow::Result<AppResponse> {
self.app.execute_contract(
owner,
self.addr.clone(),
&ExecuteMsg::DeleteId { name_id },
&[],
)
}
pub fn delete(&mut self, name_id: NameId, owner: Addr) -> AppResponse {
let delete_resp = self.try_delete(name_id, owner).unwrap();
assert_eq!(
get_app_attribute(&delete_resp, "wasm-delete_id", "action"),
"delete_id"
);
delete_resp
}
pub fn delete_name(&mut self, name: NymName, owner: Addr) -> AppResponse {
self.app
.execute_contract(
owner,
self.addr.clone(),
&ExecuteMsg::DeleteName { name },
&[],
)
.unwrap()
}
pub fn balance(&self, address: impl Into<String>) -> Coin {
self.app.wrap().query_balance(address, DENOM).unwrap()
}
}
-56
View File
@@ -1,56 +0,0 @@
//! The nym name service contract is for users to register names for the nym addresses.
#![warn(clippy::expect_used)]
#![warn(clippy::unwrap_used)]
pub use nym_name_service_common::error::{NameServiceError, Result};
use nym_name_service_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
#[cfg(not(feature = "library"))]
use cosmwasm_std::entry_point;
use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response};
mod contract;
mod state;
pub mod constants;
#[cfg(test)]
mod integration_tests;
#[cfg(test)]
mod test_helpers;
/// Contract entry point for instantiation.
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Response> {
contract::instantiate(deps, env, info, msg)
}
/// Contract entry point for migrations.
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn migrate(deps: DepsMut<'_>, env: Env, msg: MigrateMsg) -> Result<Response, NameServiceError> {
contract::migrate(deps, env, msg)
}
/// Contract entry point for execution
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, NameServiceError> {
contract::execute(deps, env, info, msg)
}
/// Contract entry point for queries
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<Binary> {
contract::query(deps, env, msg)
}
-14
View File
@@ -1,14 +0,0 @@
use cosmwasm_std::{Addr, Deps, DepsMut};
use cw_controllers::Admin;
use crate::{constants::ADMIN_KEY, Result};
const ADMIN: Admin = Admin::new(ADMIN_KEY);
pub(crate) fn set_admin(deps: DepsMut<'_>, admin: Addr) -> Result<()> {
Ok(ADMIN.set(deps, Some(admin))?)
}
pub(crate) fn assert_admin(deps: Deps, sender: &Addr) -> Result<()> {
Ok(ADMIN.assert_admin(deps, sender)?)
}
@@ -1,34 +0,0 @@
use cosmwasm_std::{Coin, Storage};
use cw_storage_plus::Item;
use nym_name_service_common::response::ConfigResponse;
use serde::{Deserialize, Serialize};
use crate::{constants::CONFIG_KEY, Result};
const CONFIG: Item<Config> = Item::new(CONFIG_KEY);
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub(crate) struct Config {
pub deposit_required: Coin,
}
impl From<Config> for ConfigResponse {
fn from(config: Config) -> Self {
ConfigResponse {
deposit_required: config.deposit_required,
}
}
}
pub(crate) fn save_config(store: &mut dyn Storage, config: &Config) -> Result<()> {
Ok(CONFIG.save(store, config)?)
}
pub(crate) fn load_config(store: &dyn Storage) -> Result<Config> {
Ok(CONFIG.load(store)?)
}
/// Return the deposit required to register a name.
pub(crate) fn deposit_required(store: &dyn Storage) -> Result<Coin> {
Ok(CONFIG.load(store).map(|config| config.deposit_required)?)
}
-10
View File
@@ -1,10 +0,0 @@
pub mod admin;
pub mod config;
pub mod name_id_counter;
pub mod names;
pub mod nonce;
pub(crate) use admin::{assert_admin, set_admin};
pub(crate) use config::{deposit_required, load_config, save_config, Config};
pub(crate) use name_id_counter::next_name_id_counter;
pub(crate) use nonce::{get_signing_nonce, increment_signing_nonce};
@@ -1,97 +0,0 @@
use cosmwasm_std::Storage;
use cw_storage_plus::Item;
use nym_name_service_common::NameId;
use crate::{constants::NAME_ID_COUNTER_KEY, Result};
const NAME_ID_COUNTER: Item<NameId> = Item::new(NAME_ID_COUNTER_KEY);
/// Generate the next name id, store it and return it
pub(crate) fn next_name_id_counter(store: &mut dyn Storage) -> Result<NameId> {
// The first id is 1.
let id = NAME_ID_COUNTER.may_load(store)?.unwrap_or_default() + 1;
NAME_ID_COUNTER.save(store, &id)?;
Ok(id)
}
#[cfg(test)]
mod tests {
use cosmwasm_std::Addr;
use nym_name_service_common::RegisteredName;
use crate::test_helpers::{
assert::assert_names,
helpers::{nyms, test_rng},
transactions::{delete_name_id, instantiate_test_contract, register_name},
};
#[test]
fn get_next_name_id() {
let mut deps = instantiate_test_contract();
let mut rng = test_rng();
let (id1, name1) = register_name(deps.as_mut(), &mut rng, "foo", "steve");
let (id2, name2) = register_name(deps.as_mut(), &mut rng, "bar", "steve");
let (id3, name3) = register_name(deps.as_mut(), &mut rng, "baz", "steve");
assert_eq!(id1, 1);
assert_eq!(id2, 2);
assert_eq!(id3, 3);
assert_eq!(name1.name.as_str(), "foo");
assert_eq!(name2.name.as_str(), "bar");
assert_eq!(name3.name.as_str(), "baz");
assert_names(
deps.as_ref(),
&[
RegisteredName {
id: 1,
name: name1,
owner: Addr::unchecked("steve"),
block_height: 12345,
deposit: nyms(100),
},
RegisteredName {
id: 2,
name: name2,
owner: Addr::unchecked("steve"),
block_height: 12345,
deposit: nyms(100),
},
RegisteredName {
id: 3,
name: name3,
owner: Addr::unchecked("steve"),
block_height: 12345,
deposit: nyms(100),
},
],
);
}
#[test]
fn deleted_name_id_is_not_reused() {
let mut deps = instantiate_test_contract();
let mut rng = test_rng();
// Register two names
let (_, name1) = register_name(deps.as_mut(), &mut rng, "one", "steve");
register_name(deps.as_mut(), &mut rng, "two", "steve");
// Delete the last entry
delete_name_id(deps.as_mut(), 2, "steve");
assert_names(
deps.as_ref(),
&[RegisteredName {
id: 1,
name: name1,
owner: Addr::unchecked("steve"),
block_height: 12345,
deposit: nyms(100),
}],
);
// Create a third entry. The index should not reuse the previous entry that we just
// deleted.
let (id3, _) = register_name(deps.as_mut(), &mut rng, "three", "steve");
assert_eq!(id3, 3);
}
}
-637
View File
@@ -1,637 +0,0 @@
use cosmwasm_std::{Addr, Order, StdError, StdResult, Storage};
use cw_storage_plus::{Bound, Index, IndexList, IndexedMap, MultiIndex, UniqueIndex};
use nym_name_service_common::{Address, NameId, NymName, RegisteredName};
use crate::{
constants::{
MAX_NUMBER_OF_NAMES_FOR_ADDRESS, MAX_NUMBER_OF_NAMES_PER_OWNER,
NAMES_ADDRESS_IDX_NAMESPACE, NAMES_NAME_IDX_NAMESPACE, NAMES_OWNER_IDX_NAMESPACE,
NAMES_PK_NAMESPACE, NAME_DEFAULT_RETRIEVAL_LIMIT, NAME_MAX_RETRIEVAL_LIMIT,
},
NameServiceError, Result,
};
struct NameIndex<'a> {
// A name can only point to a single address
pub(crate) name: UniqueIndex<'a, String, RegisteredName, NameId>,
// An addresses can be pointed to by multiple names.
pub(crate) address: MultiIndex<'a, String, RegisteredName, NameId>,
pub(crate) owner: MultiIndex<'a, Addr, RegisteredName, NameId>,
}
impl<'a> IndexList<RegisteredName> for NameIndex<'a> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<RegisteredName>> + '_> {
let v: Vec<&dyn Index<RegisteredName>> = vec![&self.name, &self.address, &self.owner];
Box::new(v.into_iter())
}
}
fn names<'a>() -> IndexedMap<'a, NameId, RegisteredName, NameIndex<'a>> {
let indexes = NameIndex {
name: UniqueIndex::new(|d| d.name.name.to_string(), NAMES_NAME_IDX_NAMESPACE),
address: MultiIndex::new(
|_pk, d| d.name.address.to_string(),
NAMES_PK_NAMESPACE,
NAMES_ADDRESS_IDX_NAMESPACE,
),
owner: MultiIndex::new(
|_pk, d| d.owner.clone(),
NAMES_PK_NAMESPACE,
NAMES_OWNER_IDX_NAMESPACE,
),
};
IndexedMap::new(NAMES_PK_NAMESPACE, indexes)
}
pub fn save(store: &mut dyn Storage, new_name: &RegisteredName) -> Result<()> {
let name_id = new_name.id;
names().save(store, name_id, new_name)?;
Ok(())
}
#[cfg(test)]
pub fn save_all(state: &mut dyn Storage, names: &[RegisteredName]) -> Result<()> {
for name in names {
save(state, name)?;
}
Ok(())
}
pub fn remove_id(store: &mut dyn Storage, name_id: NameId) -> Result<()> {
Ok(names().remove(store, name_id)?)
}
#[cfg(test)]
pub fn remove_name(store: &mut dyn Storage, name: NymName) -> Result<NameId> {
let registered_name = load_name(store, &name)?;
remove_id(store, registered_name.id)?;
Ok(registered_name.id)
}
pub fn has_name_id(store: &dyn Storage, name_id: NameId) -> bool {
names().has(store, name_id)
}
pub fn has_name(store: &dyn Storage, name: &NymName) -> bool {
load_name(store, name).is_ok()
}
pub fn load_id(store: &dyn Storage, name_id: NameId) -> Result<RegisteredName> {
names().load(store, name_id).map_err(|err| match err {
StdError::NotFound { .. } => NameServiceError::NotFound { name_id },
err => err.into(),
})
}
pub fn load_name(store: &dyn Storage, name: &NymName) -> Result<RegisteredName> {
names()
.idx
.name
.item(store, name.to_string())?
.map(|record| record.1)
.ok_or(NameServiceError::NameNotFound { name: name.clone() })
}
pub fn load_address(store: &dyn Storage, address: &Address) -> Result<Vec<RegisteredName>> {
names()
.idx
.address
.prefix(address.to_string())
.range(store, None, None, Order::Ascending)
.take(MAX_NUMBER_OF_NAMES_FOR_ADDRESS as usize)
.map(|res| res.map(|(_, name)| name))
.collect::<StdResult<Vec<_>>>()
.map_err(NameServiceError::from)
}
pub fn load_owner(store: &dyn Storage, owner: Addr) -> Result<Vec<RegisteredName>> {
names()
.idx
.owner
.prefix(owner)
.range(store, None, None, Order::Ascending)
.take(MAX_NUMBER_OF_NAMES_PER_OWNER as usize)
.map(|res| res.map(|(_, name)| name))
.collect::<StdResult<Vec<_>>>()
.map_err(NameServiceError::from)
}
#[derive(Debug, PartialEq)]
pub struct PagedLoad {
pub names: Vec<RegisteredName>,
pub limit: usize,
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
pub start_next_after: Option<NameId>,
}
pub fn load_all_paged(
store: &dyn Storage,
limit: Option<u32>,
start_after: Option<NameId>,
) -> Result<PagedLoad> {
let limit = limit
.unwrap_or(NAME_DEFAULT_RETRIEVAL_LIMIT)
.min(NAME_MAX_RETRIEVAL_LIMIT) as usize;
let start = start_after.map(Bound::exclusive);
let names = names()
.range(store, start, None, Order::Ascending)
.take(limit)
.map(|res| res.map(|(_, name)| name))
.collect::<StdResult<Vec<_>>>()?;
let start_next_after = names.last().map(|name| name.id);
Ok(PagedLoad {
names,
limit,
start_next_after,
})
}
#[cfg(test)]
mod tests {
use cosmwasm_std::{
testing::{MockApi, MockQuerier},
MemoryStorage, OwnedDeps,
};
use rstest::rstest;
use crate::test_helpers::{
fixture::{name_fixture, name_fixture_full},
transactions::instantiate_test_contract,
};
use super::*;
type TestDeps = OwnedDeps<MemoryStorage, MockApi, MockQuerier>;
#[rstest::fixture]
fn deps() -> TestDeps {
instantiate_test_contract()
}
#[rstest::fixture]
fn uniq_names() -> Vec<RegisteredName> {
vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(2, "two", "address.two@b", "owner_two"),
name_fixture_full(3, "three", "address.three@c", "owner_three"),
]
}
#[rstest::fixture]
fn overlapping_addresses() -> Vec<RegisteredName> {
vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(2, "two", "address.two@b", "owner_two"),
name_fixture_full(3, "three", "address.two@b", "owner_three"),
]
}
#[rstest::fixture]
fn overlapping_owners() -> Vec<RegisteredName> {
vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(2, "two", "address.two@b", "owner_two"),
name_fixture_full(3, "three", "address.three@c", "owner_two"),
]
}
fn assert_not_registered(store: &dyn Storage, names: Vec<RegisteredName>) {
let loaded = load_all_paged(store, None, None).unwrap();
for name in &names {
assert!(!has_name_id(store, name.id));
assert!(!has_name(store, name.entry()));
assert!(!loaded.names.iter().any(|l_name| l_name.id == name.id));
assert!(!loaded.names.iter().any(|l_name| l_name == name));
}
}
fn assert_registered(store: &dyn Storage, names: Vec<RegisteredName>) {
let loaded = load_all_paged(store, None, None).unwrap();
for name in &names {
assert!(has_name_id(store, name.id));
assert!(has_name(store, name.entry()));
assert!(loaded.names.iter().filter(|n| n == &name).count() == 1);
}
}
fn assert_only_these_registered(store: &dyn Storage, names: Vec<RegisteredName>) {
for name in &names {
assert!(has_name_id(store, name.id));
assert!(has_name(store, name.entry()));
}
let last_id = names.last().unwrap().id;
assert_eq!(
load_all_paged(store, None, None).unwrap(),
PagedLoad {
names,
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(last_id),
}
)
}
#[rstest]
fn single_basic_save_works(mut deps: TestDeps) {
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
}
#[rstest]
fn save_same_name_twice_fails(mut deps: TestDeps) {
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(matches!(
save(deps.as_mut().storage, &name_fixture(2)).unwrap_err(),
NameServiceError::Std(StdError::GenericErr { .. })
));
}
#[rstest]
fn remove_id_works(mut deps: TestDeps) {
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(has_name_id(&deps.storage, 1));
assert!(has_name(&deps.storage, name_fixture(1).entry()));
remove_id(deps.as_mut().storage, 1).unwrap();
assert!(!has_name_id(&deps.storage, 1));
assert!(!has_name(&deps.storage, name_fixture(1).entry()));
}
#[rstest]
fn remove_name_works(mut deps: TestDeps) {
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(has_name_id(&deps.storage, 1));
assert!(has_name(&deps.storage, name_fixture(1).entry()));
remove_name(deps.as_mut().storage, name_fixture(1).name.name).unwrap();
assert!(!has_name_id(&deps.storage, 1));
assert!(!has_name(&deps.storage, name_fixture(1).entry()));
}
#[rstest]
fn has_name_works(mut deps: TestDeps) {
assert!(!has_name(&deps.storage, name_fixture(1).entry()));
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(has_name(&deps.storage, name_fixture(1).entry()));
}
#[rstest]
fn has_name_id_works(mut deps: TestDeps) {
assert!(!has_name_id(&deps.storage, 1));
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(has_name_id(&deps.storage, 1));
}
#[rstest]
fn has_name_id_with_incorrect_id_fails(mut deps: TestDeps) {
assert!(!has_name_id(&deps.storage, 2));
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert!(!has_name_id(&deps.storage, 2));
}
#[rstest]
fn load_id_works(mut deps: TestDeps) {
assert_eq!(
load_id(deps.as_ref().storage, 1).unwrap_err(),
NameServiceError::NotFound { name_id: 1 }
);
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert_eq!(load_id(deps.as_ref().storage, 1).unwrap(), name_fixture(1),);
}
#[rstest]
fn load_name_works(mut deps: TestDeps) {
assert_eq!(
load_name(deps.as_ref().storage, name_fixture(1).entry()).unwrap_err(),
NameServiceError::NameNotFound {
name: name_fixture(1).name.name,
}
);
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert_eq!(
load_name(deps.as_ref().storage, name_fixture(1).entry()).unwrap(),
name_fixture(1),
);
}
#[rstest]
fn load_address_works(mut deps: TestDeps) {
assert_eq!(
load_address(deps.as_ref().storage, &name_fixture(1).name.address).unwrap(),
vec![],
);
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert_eq!(
load_address(deps.as_ref().storage, &name_fixture(1).name.address).unwrap(),
vec![name_fixture(1)],
);
}
#[rstest]
fn load_owner_works(mut deps: TestDeps) {
assert_eq!(
load_owner(deps.as_ref().storage, name_fixture(1).owner).unwrap(),
vec![],
);
save(deps.as_mut().storage, &name_fixture(1)).unwrap();
assert_eq!(
load_owner(deps.as_ref().storage, name_fixture(1).owner).unwrap(),
vec![name_fixture(1)],
);
}
#[rstest]
fn load_all_paged_works(mut deps: TestDeps, uniq_names: Vec<RegisteredName>) {
assert_eq!(
load_all_paged(&deps.storage, None, None).unwrap(),
PagedLoad {
names: vec![],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: None
}
);
save(deps.as_mut().storage, &uniq_names[0]).unwrap();
assert_eq!(
load_all_paged(&deps.storage, None, None).unwrap(),
PagedLoad {
names: vec![uniq_names[0].clone()],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(1),
}
);
}
#[rstest]
fn save_set_of_unique_names_works(mut deps: TestDeps, uniq_names: Vec<RegisteredName>) {
assert_not_registered(&deps.storage, uniq_names.clone());
save_all(deps.as_mut().storage, &uniq_names).unwrap();
assert_registered(&deps.storage, uniq_names.clone());
assert_only_these_registered(&deps.storage, uniq_names);
}
#[rstest]
fn load_name_for_unique_set_works(mut deps: TestDeps, uniq_names: Vec<RegisteredName>) {
save_all(deps.as_mut().storage, &uniq_names).unwrap();
for name in uniq_names {
assert_eq!(
load_name(deps.as_ref().storage, name.entry()).unwrap(),
name.clone(),
);
}
}
#[rstest]
fn save_and_remove_name_id_from_set_of_unique_set_works(
mut deps: TestDeps,
uniq_names: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &uniq_names).unwrap();
remove_id(deps.as_mut().storage, 2).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(3, "three", "address.three@c", "owner_three"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
}
);
}
#[rstest]
fn save_and_remove_name_from_set_of_unique_set_works(
mut deps: TestDeps,
uniq_names: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &uniq_names).unwrap();
remove_name(deps.as_mut().storage, uniq_names[1].entry().clone()).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(3, "three", "address.three@c", "owner_three"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
}
);
}
#[rstest]
fn save_set_of_overlapping_addressed_works(
mut deps: TestDeps,
overlapping_addresses: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &overlapping_addresses).unwrap();
assert_registered(&deps.storage, overlapping_addresses.clone());
assert_only_these_registered(&deps.storage, overlapping_addresses);
}
#[rstest]
fn load_address_with_overlapping_addresses_works(
mut deps: TestDeps,
overlapping_addresses: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &overlapping_addresses).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(2, "two", "address.two@b", "owner_two"),
name_fixture_full(3, "three", "address.two@b", "owner_three"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
}
);
assert_eq!(
load_address(
deps.as_ref().storage,
&Address::new("address.two@b").unwrap()
)
.unwrap(),
vec![
name_fixture_full(2, "two", "address.two@b", "owner_two"),
name_fixture_full(3, "three", "address.two@b", "owner_three"),
]
);
}
#[rstest]
fn save_and_remove_name_id_from_set_of_overlapping_addresses_works(
mut deps: TestDeps,
overlapping_addresses: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &overlapping_addresses).unwrap();
remove_id(deps.as_mut().storage, 2).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(3, "three", "address.two@b", "owner_three"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
}
);
}
#[rstest]
fn save_and_remove_name_from_set_of_overlapping_addresses_works(
mut deps: TestDeps,
overlapping_addresses: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &overlapping_addresses).unwrap();
remove_name(
deps.as_mut().storage,
overlapping_addresses[1].name.name.clone(),
)
.unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(3, "three", "address.two@b", "owner_three"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
}
);
}
#[rstest]
fn save_set_of_overlapping_owners_works(
mut deps: TestDeps,
overlapping_owners: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &overlapping_owners).unwrap();
assert_registered(&deps.storage, overlapping_owners.clone());
assert_only_these_registered(&deps.storage, overlapping_owners);
}
#[rstest]
fn load_owner_with_overlapping_owners_works(
mut deps: TestDeps,
overlapping_owners: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &overlapping_owners).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(2, "two", "address.two@b", "owner_two"),
name_fixture_full(3, "three", "address.three@c", "owner_two"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
}
);
assert_eq!(
load_owner(deps.as_ref().storage, Addr::unchecked("owner_two")).unwrap(),
vec![
name_fixture_full(2, "two", "address.two@b", "owner_two"),
name_fixture_full(3, "three", "address.three@c", "owner_two"),
]
);
}
#[rstest]
fn save_and_remove_name_id_from_set_of_overlapping_owners_works(
mut deps: TestDeps,
overlapping_owners: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &overlapping_owners).unwrap();
assert_registered(&deps.storage, overlapping_owners.clone());
assert_only_these_registered(&deps.storage, overlapping_owners);
remove_id(deps.as_mut().storage, 2).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(3, "three", "address.three@c", "owner_two"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
}
);
}
#[rstest]
fn save_and_remove_name_from_set_of_overlapping_owners_works(
mut deps: TestDeps,
overlapping_owners: Vec<RegisteredName>,
) {
save_all(deps.as_mut().storage, &overlapping_owners).unwrap();
assert_registered(&deps.storage, overlapping_owners.clone());
assert_only_these_registered(&deps.storage, overlapping_owners.clone());
remove_name(deps.as_mut().storage, overlapping_owners[1].entry().clone()).unwrap();
assert_eq!(
load_all_paged(deps.as_ref().storage, None, None).unwrap(),
PagedLoad {
names: vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(3, "three", "address.three@c", "owner_two"),
],
limit: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after: Some(3),
}
);
}
#[rstest]
fn load_all_paged_with_limit_works(mut deps: TestDeps, uniq_names: Vec<RegisteredName>) {
save_all(deps.as_mut().storage, &uniq_names).unwrap();
assert_eq!(
load_all_paged(&deps.storage, Some(2), None).unwrap(),
PagedLoad {
names: vec![
name_fixture_full(1, "one", "address.one@a", "owner_one"),
name_fixture_full(2, "two", "address.two@b", "owner_two"),
],
limit: 2,
start_next_after: Some(2),
}
);
assert_eq!(
load_all_paged(&deps.storage, Some(1), Some(2)).unwrap(),
PagedLoad {
names: vec![name_fixture_full(
3,
"three",
"address.three@c",
"owner_three"
)],
limit: 1,
start_next_after: Some(3),
}
);
assert_eq!(
load_all_paged(&deps.storage, Some(2), Some(2)).unwrap(),
PagedLoad {
names: vec![name_fixture_full(
3,
"three",
"address.three@c",
"owner_three"
)],
limit: 2,
start_next_after: Some(3),
}
);
}
#[test]
#[ignore]
fn max_page_limit_is_applied() {
todo!();
}
}
-71
View File
@@ -1,71 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{constants::SIGNING_NONCES_NAMESPACE, Result};
use cosmwasm_std::{Addr, Storage};
use cw_storage_plus::Map;
use nym_contracts_common::signing::Nonce;
pub const NONCES: Map<'_, Addr, Nonce> = Map::new(SIGNING_NONCES_NAMESPACE);
pub fn get_signing_nonce(storage: &dyn Storage, address: Addr) -> Result<Nonce> {
let nonce = NONCES.may_load(storage, address)?.unwrap_or(0);
Ok(nonce)
}
fn update_signing_nonce(storage: &mut dyn Storage, address: Addr, value: Nonce) -> Result<()> {
NONCES
.save(storage, address, &value)
.map_err(|err| err.into())
}
pub fn increment_signing_nonce(storage: &mut dyn Storage, address: Addr) -> Result<()> {
// get the current nonce
let nonce = get_signing_nonce(storage, address.clone())?;
// increment it for the next use
update_signing_nonce(storage, address, nonce + 1)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::transactions::instantiate_test_contract;
use cosmwasm_std::{
testing::{MockApi, MockQuerier},
MemoryStorage, OwnedDeps,
};
use rstest::rstest;
type TestDeps = OwnedDeps<MemoryStorage, MockApi, MockQuerier>;
#[rstest::fixture]
fn deps() -> TestDeps {
instantiate_test_contract()
}
fn addr(s: &str) -> Addr {
Addr::unchecked(s)
}
#[rstest]
fn getting_signing_nonce_doesnt_increment_it(deps: TestDeps) {
assert_eq!(get_signing_nonce(&deps.storage, addr("gunnar")).unwrap(), 0);
assert_eq!(get_signing_nonce(&deps.storage, addr("gunnar")).unwrap(), 0);
}
#[rstest]
fn increment_works(mut deps: TestDeps) {
assert_eq!(get_signing_nonce(&deps.storage, addr("gunnar")).unwrap(), 0);
increment_signing_nonce(&mut deps.storage, addr("gunnar")).unwrap();
assert_eq!(get_signing_nonce(&deps.storage, addr("gunnar")).unwrap(), 1);
}
#[rstest]
fn incrementing_is_independent(mut deps: TestDeps) {
increment_signing_nonce(&mut deps.storage, addr("gunnar")).unwrap();
assert_eq!(get_signing_nonce(&deps.storage, addr("gunnar")).unwrap(), 1);
assert_eq!(get_signing_nonce(&deps.storage, addr("bjorn")).unwrap(), 0);
}
}
@@ -1,79 +0,0 @@
use cosmwasm_std::{from_binary, testing::mock_env, Addr, Coin, Deps};
use nym_contracts_common::signing::Nonce;
use nym_name_service_common::{
msg::QueryMsg,
response::{ConfigResponse, PagedNamesListResponse},
NameId, RegisteredName,
};
use crate::{constants::NAME_DEFAULT_RETRIEVAL_LIMIT, NameServiceError};
pub fn assert_config(deps: Deps, admin: &Addr, deposit_required: Coin) {
crate::state::assert_admin(deps, admin).unwrap();
let res = crate::contract::query(deps, mock_env(), QueryMsg::Config {}).unwrap();
let config: ConfigResponse = from_binary(&res).unwrap();
assert_eq!(config, ConfigResponse { deposit_required });
}
pub fn assert_names(deps: Deps, expected_names: &[RegisteredName]) {
let res = crate::contract::query(deps, mock_env(), QueryMsg::all()).unwrap();
let names: PagedNamesListResponse = from_binary(&res).unwrap();
let start_next_after = expected_names.iter().last().map(|s| s.id);
assert_eq!(
names,
PagedNamesListResponse {
names: expected_names.to_vec(),
per_page: NAME_DEFAULT_RETRIEVAL_LIMIT as usize,
start_next_after,
}
);
}
pub fn assert_name(deps: Deps, expected_name: &RegisteredName) {
let res = crate::contract::query(
deps,
mock_env(),
QueryMsg::NameId {
name_id: expected_name.id,
},
)
.unwrap();
let names: RegisteredName = from_binary(&res).unwrap();
assert_eq!(&names, expected_name);
}
pub fn assert_empty(deps: Deps) {
let res = crate::contract::query(deps, mock_env(), QueryMsg::all()).unwrap();
let names: PagedNamesListResponse = from_binary(&res).unwrap();
assert!(names.names.is_empty());
}
pub fn assert_not_found(deps: Deps, expected_id: NameId) {
let res = crate::contract::query(
deps,
mock_env(),
QueryMsg::NameId {
name_id: expected_id,
},
)
.unwrap_err();
assert!(matches!(
res,
NameServiceError::NotFound {
name_id: _expected_id
}
));
}
pub fn assert_current_nonce(deps: Deps, address: &Addr, expected_nonce: Nonce) {
let res = crate::contract::query(
deps,
mock_env(),
QueryMsg::SigningNonce {
address: address.to_string(),
},
)
.unwrap();
let nonce: Nonce = from_binary(&res).unwrap();
assert_eq!(nonce, expected_nonce);
}
@@ -1,141 +0,0 @@
use cosmwasm_std::{Addr, Coin, DepsMut};
use nym_contracts_common::{signing::MessageSignature, IdentityKeyRef};
use nym_crypto::asymmetric::{encryption, identity};
use nym_name_service_common::{Address, NameDetails, NameId, NymName, RegisteredName};
use nym_sphinx_addressing::clients::Recipient;
use rand_chacha::rand_core::{CryptoRng, RngCore};
use super::{
helpers::nyms,
signing::{ed25519_sign_message, name_register_sign_payload},
};
pub fn new_name(
name_id: NameId,
name: &NymName,
address: &Address,
owner: &Addr,
identity_key: IdentityKeyRef,
) -> RegisteredName {
RegisteredName {
id: name_id,
name: NameDetails {
name: name.clone(),
address: address.clone(),
identity_key: identity_key.to_string(),
},
owner: owner.clone(),
block_height: 12345,
deposit: nyms(100),
}
}
pub fn name_fixture(id: NameId) -> RegisteredName {
new_name(
id,
&NymName::new("my-service").unwrap(),
&Address::new("client_id.client_key@gateway_id").unwrap(),
&Addr::unchecked("steve"),
"identity",
)
}
pub fn name_fixture_full(id: NameId, name: &str, address: &str, owner: &str) -> RegisteredName {
new_name(
id,
&NymName::new(name).unwrap(),
&Address::new(address).unwrap(),
&Addr::unchecked(owner),
"identity",
)
}
fn new_recipient<R>(rng: &mut R) -> (Recipient, identity::KeyPair)
where
R: RngCore + CryptoRng,
{
let client_id_keys = identity::KeyPair::new(rng);
let client_enc_keys = encryption::KeyPair::new(rng);
let gateway_id_keys = identity::KeyPair::new(rng);
(
Recipient::new(
*client_id_keys.public_key(),
*client_enc_keys.public_key(),
*gateway_id_keys.public_key(),
),
client_id_keys,
)
}
pub fn new_address<R>(rng: &mut R) -> (Address, identity::KeyPair)
where
R: RngCore + CryptoRng,
{
let (recipient, client_id_keys) = new_recipient(rng);
let address = Address::new(&recipient.to_string()).unwrap();
(address, client_id_keys)
}
// Create a new name with a correctly generated nym address and matching identity key
pub fn new_name_details<R>(rng: &mut R, name: &str) -> (NameDetails, identity::KeyPair)
where
R: RngCore + CryptoRng,
{
let (address, client_id_keys) = new_address(rng);
let identity_key = client_id_keys.public_key().to_base58_string();
(
NameDetails {
name: NymName::new(name).unwrap(),
address,
identity_key,
},
client_id_keys,
)
}
// Create a new name, with a correctly generated nym adress and identity key, and sign it
pub fn new_name_details_with_sign<R>(
deps: DepsMut<'_>,
rng: &mut R,
name: &str,
owner: &str,
deposit: Coin,
) -> (NameDetails, MessageSignature)
where
R: RngCore + CryptoRng,
{
// Name
let (name, client_id_keys) = new_name_details(rng, name);
// Sign
let sign_msg = name_register_sign_payload(deps.as_ref(), owner, name.clone(), deposit);
let owner_signature = ed25519_sign_message(sign_msg, client_id_keys.private_key());
(name, owner_signature)
}
#[cfg(test)]
mod test {
use crate::test_helpers::helpers;
use super::*;
#[test]
fn new_recipient_creates_matching_identity_and_client_id_public_key() {
let mut test_rng = helpers::test_rng();
let (recipient, client_id_keys) = new_recipient(&mut test_rng);
assert_eq!(recipient.identity(), client_id_keys.public_key());
// A test not for our code, but that we actually confirm our understanding of Recipient.
// One might view it as unnecessary, but it's a good sanity check for such a core assumption.
let recipient_str = format!(
"{}.{}@{}",
recipient.identity().to_base58_string(),
recipient.encryption_key().to_base58_string(),
recipient.gateway().to_base58_string()
);
assert_eq!(recipient.to_string(), recipient_str);
}
}
@@ -1,54 +0,0 @@
use cosmwasm_std::{Coin, Event, Response};
use cw_multi_test::AppResponse;
use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng};
pub fn nyms(amount: u64) -> Coin {
Coin::new(amount.into(), "unym")
}
pub fn test_rng() -> ChaCha20Rng {
let dummy_seed = [42u8; 32];
ChaCha20Rng::from_seed(dummy_seed)
}
pub fn get_event_types(response: &Response, event_type: &str) -> Vec<Event> {
response
.events
.iter()
.filter(|ev| ev.ty == event_type)
.cloned()
.collect()
}
pub fn get_attribute(response: &Response, event_type: &str, key: &str) -> String {
get_event_types(response, event_type)
.first()
.unwrap()
.attributes
.iter()
.find(|attr| attr.key == key)
.unwrap()
.value
.clone()
}
pub fn get_app_event_types(response: &AppResponse, event_type: &str) -> Vec<Event> {
response
.events
.iter()
.filter(|ev| ev.ty == event_type)
.cloned()
.collect()
}
pub fn get_app_attribute(response: &AppResponse, event_type: &str, key: &str) -> String {
get_app_event_types(response, event_type)
.first()
.unwrap()
.attributes
.iter()
.find(|attr| attr.key == key)
.unwrap()
.value
.clone()
}
@@ -1,5 +0,0 @@
pub mod assert;
pub mod fixture;
pub mod helpers;
pub mod signing;
pub mod transactions;
@@ -1,39 +0,0 @@
use cosmwasm_std::{Addr, Coin, Deps};
use nym_contracts_common::signing::{
MessageSignature, SignableMessage, SigningAlgorithm, SigningPurpose,
};
use nym_crypto::asymmetric::identity;
use nym_name_service_common::{
signing_types::{construct_name_register_sign_payload, SignableNameRegisterMsg},
NameDetails,
};
use serde::Serialize;
use crate::state;
pub fn name_register_sign_payload(
deps: Deps<'_>,
owner: &str,
name: NameDetails,
deposit: Coin,
) -> SignableNameRegisterMsg {
let owner = Addr::unchecked(owner);
let nonce = state::get_signing_nonce(deps.storage, owner.clone()).unwrap();
construct_name_register_sign_payload(nonce, owner, deposit, name)
}
pub fn ed25519_sign_message<T: Serialize + SigningPurpose>(
message: SignableMessage<T>,
private_key: &identity::PrivateKey,
) -> MessageSignature {
match message.algorithm {
SigningAlgorithm::Ed25519 => {
let plaintext = message.to_plaintext().unwrap();
let signature = private_key.sign(plaintext);
MessageSignature::from(signature.to_bytes().as_ref())
}
SigningAlgorithm::Secp256k1 => {
unimplemented!()
}
}
}
@@ -1,70 +0,0 @@
use cosmwasm_std::{
coin,
testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier},
DepsMut, MemoryStorage, OwnedDeps,
};
use nym_name_service_common::{
events::{NameEventType, NAME_ID},
msg::{ExecuteMsg, InstantiateMsg},
NameDetails, NameId, NymName,
};
use rand_chacha::rand_core::{CryptoRng, RngCore};
use super::helpers::{get_attribute, nyms};
pub fn instantiate_test_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier> {
let mut deps = mock_dependencies();
let msg = InstantiateMsg {
deposit_required: coin(100, "unym"),
};
let env = mock_env();
let info = mock_info("creator", &[]);
let res = crate::instantiate(deps.as_mut(), env, info, msg).unwrap();
assert_eq!(res.messages.len(), 0);
deps
}
pub fn register_name<R>(
mut deps: DepsMut<'_>,
rng: &mut R,
name: &str,
owner: &str,
) -> (NameId, NameDetails)
where
R: RngCore + CryptoRng,
{
let deposit = nyms(100);
let (name, owner_signature) = super::fixture::new_name_details_with_sign(
deps.branch(),
rng,
name,
owner,
deposit.clone(),
);
// Register
let msg = ExecuteMsg::Register {
name: name.clone(),
owner_signature,
};
let info = mock_info(owner, &[deposit]);
let res = crate::execute(deps, mock_env(), info, msg).unwrap();
let name_id: NameId = get_attribute(&res, &NameEventType::Register.to_string(), NAME_ID)
.parse()
.unwrap();
(name_id, name)
}
pub fn delete_name_id(deps: DepsMut<'_>, name_id: NameId, owner: &str) {
let msg = ExecuteMsg::DeleteId { name_id };
let info = mock_info(owner, &[]);
crate::execute(deps, mock_env(), info, msg).unwrap();
}
#[allow(unused)]
pub fn delete_name(deps: DepsMut<'_>, name: NymName, owner: &str) {
let msg = ExecuteMsg::DeleteName { name };
let info = mock_info(owner, &[]);
crate::execute(deps, mock_env(), info, msg).unwrap();
}
@@ -1,5 +0,0 @@
[alias]
wasm = "build --release --lib --target wasm32-unknown-unknown"
wasm-debug = "build --target wasm32-unknown-unknown"
unit-test = "test --lib"
schema = "run --bin schema --features=schema-gen"
@@ -1,2 +0,0 @@
generate-schema:
cargo schema
@@ -1,13 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use vergen::{vergen, Config};
fn main() {
let mut config = Config::default();
if std::env::var("DOCS_RS").is_ok() {
// If we don't have access to git information, such as in a docs.rs build, don't error
*config.git_mut().skip_if_error_mut() = true;
}
vergen(config).expect("failed to extract build metadata");
}
File diff suppressed because it is too large Load Diff
@@ -1,181 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ExecuteMsg",
"oneOf": [
{
"type": "object",
"required": [
"announce"
],
"properties": {
"announce": {
"type": "object",
"required": [
"owner_signature",
"service"
],
"properties": {
"owner_signature": {
"$ref": "#/definitions/MessageSignature"
},
"service": {
"$ref": "#/definitions/ServiceDetails"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"delete_id"
],
"properties": {
"delete_id": {
"type": "object",
"required": [
"service_id"
],
"properties": {
"service_id": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"delete_nym_address"
],
"properties": {
"delete_nym_address": {
"type": "object",
"required": [
"nym_address"
],
"properties": {
"nym_address": {
"$ref": "#/definitions/NymAddress"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"update_deposit_required"
],
"properties": {
"update_deposit_required": {
"type": "object",
"required": [
"deposit_required"
],
"properties": {
"deposit_required": {
"$ref": "#/definitions/Coin"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"MessageSignature": {
"type": "array",
"items": {
"type": "integer",
"format": "uint8",
"minimum": 0.0
}
},
"NymAddress": {
"description": "The types of addresses supported.",
"oneOf": [
{
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.",
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string"
}
},
"additionalProperties": false
}
]
},
"ServiceDetails": {
"type": "object",
"required": [
"identity_key",
"nym_address",
"service_type"
],
"properties": {
"identity_key": {
"description": "The identity key of the service.",
"type": "string"
},
"nym_address": {
"description": "The address of the service.",
"allOf": [
{
"$ref": "#/definitions/NymAddress"
}
]
},
"service_type": {
"description": "The service type.",
"allOf": [
{
"$ref": "#/definitions/ServiceType"
}
]
}
},
"additionalProperties": false
},
"ServiceType": {
"description": "The type of services provider supported",
"type": "string",
"enum": [
"network_requester"
]
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,35 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "InstantiateMsg",
"type": "object",
"required": [
"deposit_required"
],
"properties": {
"deposit_required": {
"$ref": "#/definitions/Coin"
}
},
"additionalProperties": false,
"definitions": {
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,6 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "MigrateMsg",
"type": "object",
"additionalProperties": false
}
@@ -1,184 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "QueryMsg",
"oneOf": [
{
"type": "object",
"required": [
"service_id"
],
"properties": {
"service_id": {
"type": "object",
"required": [
"service_id"
],
"properties": {
"service_id": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"by_announcer"
],
"properties": {
"by_announcer": {
"type": "object",
"required": [
"announcer"
],
"properties": {
"announcer": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"by_nym_address"
],
"properties": {
"by_nym_address": {
"type": "object",
"required": [
"nym_address"
],
"properties": {
"nym_address": {
"$ref": "#/definitions/NymAddress"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"all"
],
"properties": {
"all": {
"type": "object",
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"signing_nonce"
],
"properties": {
"signing_nonce": {
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"config"
],
"properties": {
"config": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Gets build information of this contract, such as the commit hash used for the build or rustc version.",
"type": "object",
"required": [
"get_contract_version"
],
"properties": {
"get_contract_version": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.",
"type": "object",
"required": [
"get_cw2_contract_version"
],
"properties": {
"get_cw2_contract_version": {
"type": "object",
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
"NymAddress": {
"description": "The types of addresses supported.",
"oneOf": [
{
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.",
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string"
}
},
"additionalProperties": false
}
]
}
}
}
@@ -1,162 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PagedServicesListResponse",
"type": "object",
"required": [
"per_page",
"services"
],
"properties": {
"per_page": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"services": {
"type": "array",
"items": {
"$ref": "#/definitions/Service"
}
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"NymAddress": {
"description": "The types of addresses supported.",
"oneOf": [
{
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.",
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string"
}
},
"additionalProperties": false
}
]
},
"Service": {
"type": "object",
"required": [
"announcer",
"block_height",
"deposit",
"service",
"service_id"
],
"properties": {
"announcer": {
"description": "Address of the service owner.",
"allOf": [
{
"$ref": "#/definitions/Addr"
}
]
},
"block_height": {
"description": "Block height at which the service was added.",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"deposit": {
"description": "The deposit used to announce the service.",
"allOf": [
{
"$ref": "#/definitions/Coin"
}
]
},
"service": {
"description": "The announced service.",
"allOf": [
{
"$ref": "#/definitions/ServiceDetails"
}
]
},
"service_id": {
"description": "Unique id assigned to the anounced service.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
"ServiceDetails": {
"type": "object",
"required": [
"identity_key",
"nym_address",
"service_type"
],
"properties": {
"identity_key": {
"description": "The identity key of the service.",
"type": "string"
},
"nym_address": {
"description": "The address of the service.",
"allOf": [
{
"$ref": "#/definitions/NymAddress"
}
]
},
"service_type": {
"description": "The service type.",
"allOf": [
{
"$ref": "#/definitions/ServiceType"
}
]
}
},
"additionalProperties": false
},
"ServiceType": {
"description": "The type of services provider supported",
"type": "string",
"enum": [
"network_requester"
]
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,147 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ServicesListResponse",
"type": "object",
"required": [
"services"
],
"properties": {
"services": {
"type": "array",
"items": {
"$ref": "#/definitions/Service"
}
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"NymAddress": {
"description": "The types of addresses supported.",
"oneOf": [
{
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.",
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string"
}
},
"additionalProperties": false
}
]
},
"Service": {
"type": "object",
"required": [
"announcer",
"block_height",
"deposit",
"service",
"service_id"
],
"properties": {
"announcer": {
"description": "Address of the service owner.",
"allOf": [
{
"$ref": "#/definitions/Addr"
}
]
},
"block_height": {
"description": "Block height at which the service was added.",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"deposit": {
"description": "The deposit used to announce the service.",
"allOf": [
{
"$ref": "#/definitions/Coin"
}
]
},
"service": {
"description": "The announced service.",
"allOf": [
{
"$ref": "#/definitions/ServiceDetails"
}
]
},
"service_id": {
"description": "Unique id assigned to the anounced service.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
"ServiceDetails": {
"type": "object",
"required": [
"identity_key",
"nym_address",
"service_type"
],
"properties": {
"identity_key": {
"description": "The identity key of the service.",
"type": "string"
},
"nym_address": {
"description": "The address of the service.",
"allOf": [
{
"$ref": "#/definitions/NymAddress"
}
]
},
"service_type": {
"description": "The service type.",
"allOf": [
{
"$ref": "#/definitions/ServiceType"
}
]
}
},
"additionalProperties": false
},
"ServiceType": {
"description": "The type of services provider supported",
"type": "string",
"enum": [
"network_requester"
]
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}
@@ -1,147 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ServicesListResponse",
"type": "object",
"required": [
"services"
],
"properties": {
"services": {
"type": "array",
"items": {
"$ref": "#/definitions/Service"
}
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"NymAddress": {
"description": "The types of addresses supported.",
"oneOf": [
{
"description": "String representation of a nym address, which is of the form client_id.client_enc@gateway_id.",
"type": "object",
"required": [
"address"
],
"properties": {
"address": {
"type": "string"
}
},
"additionalProperties": false
}
]
},
"Service": {
"type": "object",
"required": [
"announcer",
"block_height",
"deposit",
"service",
"service_id"
],
"properties": {
"announcer": {
"description": "Address of the service owner.",
"allOf": [
{
"$ref": "#/definitions/Addr"
}
]
},
"block_height": {
"description": "Block height at which the service was added.",
"type": "integer",
"format": "uint64",
"minimum": 0.0
},
"deposit": {
"description": "The deposit used to announce the service.",
"allOf": [
{
"$ref": "#/definitions/Coin"
}
]
},
"service": {
"description": "The announced service.",
"allOf": [
{
"$ref": "#/definitions/ServiceDetails"
}
]
},
"service_id": {
"description": "Unique id assigned to the anounced service.",
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"additionalProperties": false
},
"ServiceDetails": {
"type": "object",
"required": [
"identity_key",
"nym_address",
"service_type"
],
"properties": {
"identity_key": {
"description": "The identity key of the service.",
"type": "string"
},
"nym_address": {
"description": "The address of the service.",
"allOf": [
{
"$ref": "#/definitions/NymAddress"
}
]
},
"service_type": {
"description": "The service type.",
"allOf": [
{
"$ref": "#/definitions/ServiceType"
}
]
}
},
"additionalProperties": false
},
"ServiceType": {
"description": "The type of services provider supported",
"type": "string",
"enum": [
"network_requester"
]
},
"Uint128": {
"description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```",
"type": "string"
}
}
}

Some files were not shown because too many files have changed in this diff Show More