Delete nym-name-service contract
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
[alias]
|
||||
wasm = "build --release --lib --target wasm32-unknown-unknown"
|
||||
unit-test = "test --lib"
|
||||
schema = "run --bin schema --features=schema-gen"
|
||||
@@ -1,40 +0,0 @@
|
||||
[package]
|
||||
name = "nym-name-service"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "schema"
|
||||
required-features = ["schema-gen"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
bs58 = { workspace = true }
|
||||
cosmwasm-schema = { workspace = true, optional = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
cw-controllers = { workspace = true }
|
||||
cw-storage-plus = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.5.0" }
|
||||
nym-name-service-common = { path = "../../common/cosmwasm-smart-contracts/name-service" }
|
||||
semver = { workspace = true, default-features = false }
|
||||
serde = { version = "1.0.155", default-features = false, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "=7.4.3", default-features = false, features = ["build", "git", "rustc"] }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.40"
|
||||
cw-multi-test = { workspace = true }
|
||||
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
|
||||
nym-sphinx-addressing = { path = "../../common/nymsphinx/addressing" }
|
||||
rand = "0.8.5"
|
||||
rand_chacha = "0.2"
|
||||
rstest = "0.17.0"
|
||||
|
||||
[features]
|
||||
schema-gen = ["nym-name-service-common/schema", "cosmwasm-schema"]
|
||||
@@ -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,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
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
@@ -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), ®_name.name);
|
||||
let reg_name = reg_name.sign(payload);
|
||||
setup.register(®_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(®_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(®_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), ®_name1.name);
|
||||
let reg_name1 = reg_name1.sign(payload);
|
||||
setup.register(®_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), ®_name2.name);
|
||||
let reg_name2 = reg_name2.sign(payload);
|
||||
setup.register(®_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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)?)
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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!();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user