feature: upgrade cosmwasm to 2.2 (#5479)

* updated contracts to cosmwasm2.2 and fixed build issues

* removed old coconut contract code + additional dkg fixes

* replace deprecated to_binary and from_binary functions

* mixnet contract tests compiling

some are failing due to incorrect addresses

* made other contract tests compile

* fixed remaining tests

* allow usage of manually dispatching contract replies

* nym-api test fixes

* removed old toolchain from contracts CI

* linter fixes

* regenerated contract schema

* fixed easy_addr

* further license fixes

* post rebase fixes + update to 2.2.2

* change ci runner

* minor CI adjustments

* change wallet CI to use node 20

* more CI changes...

* run cosmwasm-check against release contracts

* test ci changes

* wip...
This commit is contained in:
Jędrzej Stuczyński
2025-03-21 13:43:35 +00:00
committed by GitHub
parent 0a92f04048
commit 33992542b1
213 changed files with 4331 additions and 11679 deletions
@@ -1,6 +0,0 @@
[
{
"rust":"stable",
"runOnEvent":"always"
}
]
@@ -31,7 +31,6 @@ jobs:
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: 1.77
target: wasm32-unknown-unknown
override: true
@@ -40,6 +39,9 @@ jobs:
with:
version: '114'
- name: Install cosmwasm-check
run: cargo install cosmwasm-check
- name: Build release contracts
run: make contracts
@@ -50,7 +52,6 @@ jobs:
run: |
cp contracts/target/wasm32-unknown-unknown/release/mixnet_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/vesting_contract.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_bandwidth.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/nym_coconut_dkg.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw3_flex_multisig.wasm $OUTPUT_DIR
cp contracts/target/wasm32-unknown-unknown/release/cw4_group.wasm $OUTPUT_DIR
+13 -17
View File
@@ -14,28 +14,12 @@ on:
- '.github/workflows/ci-contracts.yml'
jobs:
matrix_prep:
runs-on: ubuntu-20.04
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
# creates the matrix strategy from ci-contracts-matrix-includes.json
- uses: actions/checkout@v4
- id: set-matrix
uses: JoshuaTheMiller/conditional-build-matrix@main
with:
inputFile: '.github/workflows/ci-contracts-matrix-includes.json'
filter: '[?runOnEvent==`${{ github.event_name }}` || runOnEvent==`always`]'
build:
# since it's going to be compiled into wasm, there's absolutely
# no point in running CI on different OS-es
runs-on: ubuntu-20.04
env:
CARGO_TERM_COLOR: always
needs: matrix_prep
strategy:
fail-fast: false
matrix: ${{fromJson(needs.matrix_prep.outputs.matrix)}}
steps:
- uses: actions/checkout@v4
@@ -43,11 +27,19 @@ jobs:
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
toolchain: stable
target: wasm32-unknown-unknown
override: true
components: rustfmt, clippy
- name: Install cosmwasm-check
run: cargo install cosmwasm-check
- name: Install wasm-opt
uses: ./.github/actions/install-wasm-opt
with:
version: '114'
- name: Build contracts
uses: actions-rs/cargo@v1
env:
@@ -73,3 +65,7 @@ jobs:
with:
command: clippy
args: --lib --manifest-path contracts/Cargo.toml --workspace --all-targets -- -D warnings
- name: Check chain compatibility against release build
# this will build contracts in release mode, run wasm-opt and finally cosmwasm-check
run: make contracts
@@ -20,7 +20,7 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- name: Setup yarn
run: npm install -g yarn
+2 -3
View File
@@ -2,19 +2,18 @@ name: publish-nym-contracts
on:
workflow_dispatch:
release:
types: [created]
types: [ created ]
jobs:
build:
if: ${{ (startsWith(github.ref, 'refs/tags/nym-contracts-') && github.event_name == 'release') || github.event_name == 'workflow_dispatch' }}
runs-on: [self-hosted, custom-ubuntu-20.04]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: 1.77
target: wasm32-unknown-unknown
override: true
Generated
+311 -267
View File
File diff suppressed because it is too large Load Diff
+12 -14
View File
@@ -32,9 +32,8 @@ members = [
"common/client-libs/validator-client",
"common/commands",
"common/config",
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/coconut-dkg",
"common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/contracts-common", "common/cosmwasm-smart-contracts/easy_addr",
"common/cosmwasm-smart-contracts/ecash-contract",
"common/cosmwasm-smart-contracts/group-contract",
"common/cosmwasm-smart-contracts/mixnet-contract",
@@ -361,7 +360,7 @@ vergen = { version = "=8.3.1", default-features = false }
walkdir = "2"
wasm-bindgen-test = "0.3.49"
x25519-dalek = "2.0.0"
zeroize = "1.8.1"
zeroize = "1.7.0"
prometheus = { version = "0.13.0" }
@@ -375,19 +374,18 @@ ff = { version = "0.13.1", default-features = false }
subtle = "2.5.0"
# cosmwasm-related
cosmwasm-schema = "=1.4.3"
cosmwasm-std = "=1.4.3"
# use 0.5.0 as that's the version used by cosmwasm-std 1.4.3
cosmwasm-schema = "=2.2.2"
cosmwasm-std = "=2.2.2"
# use 1.0.1 as that's the version used by cosmwasm-std 2.2.1
# (and ideally we don't want to pull the same dependency twice)
serde-json-wasm = "=0.5.0"
cosmwasm-storage = "=1.4.3"
serde-json-wasm = "=1.0.1"
# same version as used by cosmwasm
cw-utils = "=1.0.1"
cw-storage-plus = "=1.2.0"
cw2 = { version = "=1.1.2" }
cw3 = { version = "=1.1.2" }
cw4 = { version = "=1.1.2" }
cw-controllers = { version = "=1.1.0" }
cw-utils = "=2.0.0"
cw-storage-plus = "=2.0.0"
cw2 = { version = "=2.0.0" }
cw3 = { version = "=2.0.0" }
cw4 = { version = "=2.0.0" }
cw-controllers = { version = "=2.0.0" }
# cosmrs-related
bip32 = { version = "0.5.3", default-features = false }
+8 -18
View File
@@ -133,17 +133,22 @@ clippy: sdk-wasm-lint
# Build contracts ready for deploy
# -----------------------------------------------------------------------------
CONTRACTS=vesting_contract mixnet_contract nym_ecash
CONTRACTS=vesting_contract mixnet_contract nym_ecash cw3_flex_multisig cw4_group nym_coconut_dkg
CONTRACTS_WASM=$(addsuffix .wasm, $(CONTRACTS))
CONTRACTS_OUT_DIR=contracts/target/wasm32-unknown-unknown/release
contracts: build-release-contracts wasm-opt-contracts
contracts: build-release-contracts wasm-opt-contracts cosmwasm-check-contracts
wasm-opt-contracts:
for contract in $(CONTRACTS_WASM); do \
wasm-opt --signext-lowering -Os $(CONTRACTS_OUT_DIR)/$$contract -o $(CONTRACTS_OUT_DIR)/$$contract; \
done
cosmwasm-check-contracts:
for contract in $(CONTRACTS_WASM); do \
cosmwasm-check $(CONTRACTS_OUT_DIR)/$$contract; \
done
# Consider adding 's' to make plural consistent (beware: used in github workflow)
contract-schema:
$(MAKE) -C contracts schema
@@ -152,18 +157,9 @@ contract-schema:
# Convenience targets for crates that are already part of the main workspace
# -----------------------------------------------------------------------------
build-explorer-api:
cargo build -p explorer-api
build-nym-cli:
cargo build -p nym-cli --release
build-nym-gateway:
cargo build -p nym-gateway --release
build-nym-mixnode:
cargo build -p nym-mixnode --release
# -----------------------------------------------------------------------------
# Misc
# -----------------------------------------------------------------------------
@@ -176,13 +172,7 @@ run-api-tests:
cd nym-api/tests/functional_test && yarn test:qa
# Build debian package, and update PPA
deb-mixnode: build-nym-mixnode
cargo deb -p nym-mixnode
deb-gateway: build-nym-gateway
cargo deb -p nym-gateway
deb-cli: build-nym-cli
cargo deb -p nym-cli
deb: deb-mixnode deb-gateway deb-cli
deb: deb-cli
@@ -16,7 +16,6 @@ nym-coconut-dkg-common = { path = "../../cosmwasm-smart-contracts/coconut-dkg" }
nym-contracts-common = { path = "../../cosmwasm-smart-contracts/contracts-common" }
nym-mixnet-contract-common = { path = "../../cosmwasm-smart-contracts/mixnet-contract" }
nym-vesting-contract-common = { path = "../../cosmwasm-smart-contracts/vesting-contract" }
nym-coconut-bandwidth-contract-common = { path = "../../cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-ecash-contract-common = { path = "../../cosmwasm-smart-contracts/ecash-contract" }
nym-multisig-contract-common = { path = "../../cosmwasm-smart-contracts/multisig-contract" }
nym-group-contract-common = { path = "../../cosmwasm-smart-contracts/group-contract" }
@@ -48,7 +48,7 @@ impl Div<GasPrice> for &Coin {
panic!("attempted to divide by zero!")
};
let implicit_gas_limit = gas_price_inv * Uint128::new(self.amount);
let implicit_gas_limit = Uint128::new(self.amount).mul_floor(gas_price_inv);
if implicit_gas_limit.u128() >= u64::MAX as u128 {
u64::MAX
} else {
@@ -169,13 +169,7 @@ impl CoinConverter for CosmosCoin {
type Target = CosmWasmCoin;
fn convert_coin(&self) -> Self::Target {
CosmWasmCoin::new(
self.amount
.to_string()
.parse()
.expect("cosmos coin had an invalid amount assigned"),
self.denom.to_string(),
)
CosmWasmCoin::new(self.amount, self.denom.to_string())
}
}
@@ -7,10 +7,10 @@ use crate::nyxd::error::NyxdError;
use crate::nyxd::{Coin, Fee, SigningCosmWasmClient};
use crate::signing::signer::OfflineSigner;
use async_trait::async_trait;
use cosmwasm_std::{to_binary, CosmosMsg, WasmMsg};
use cosmwasm_std::{CosmosMsg, Empty};
use cw3::Vote;
use cw4::{MemberChangedHookMsg, MemberDiff};
use nym_coconut_bandwidth_contract_common::msg::ExecuteMsg as CoconutBandwidthExecuteMsg;
use cw_utils::Expiration;
use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -24,35 +24,23 @@ pub trait MultisigSigningClient: NymContractsProvider {
funds: Vec<Coin>,
) -> Result<ExecuteResult, NyxdError>;
async fn propose_release_funds(
async fn propose(
&self,
title: String,
blinded_serial_number: String,
voucher_value: Coin,
description: String,
msgs: Vec<CosmosMsg<Empty>>,
latest: Option<Expiration>,
fee: Option<Fee>,
) -> Result<ExecuteResult, NyxdError> {
let ecash_contract_address = self
.ecash_contract_address()
.ok_or_else(|| NyxdError::unavailable_contract_address("coconut bandwidth contract"))?;
let release_funds_req = CoconutBandwidthExecuteMsg::ReleaseFunds {
funds: voucher_value.into(),
};
let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: ecash_contract_address.to_string(),
msg: to_binary(&release_funds_req)?,
funds: vec![],
});
let req = MultisigExecuteMsg::Propose {
title,
description: blinded_serial_number,
msgs: vec![release_funds_msg],
latest: None,
};
self.execute_multisig_contract(
fee,
req,
"Multisig::Propose::Execute::ReleaseFunds".to_string(),
MultisigExecuteMsg::Propose {
title,
description,
msgs,
latest,
},
"Multisig::Propose".to_string(),
vec![],
)
.await
@@ -161,7 +149,7 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::nyxd::contract_traits::tests::{mock_coin, IgnoreValue};
use crate::nyxd::contract_traits::tests::IgnoreValue;
// it's enough that this compiles and clippy is happy about it
#[allow(dead_code)]
@@ -171,9 +159,12 @@ mod tests {
) {
match msg {
MultisigExecuteMsg::Propose {
title, description, ..
title,
description,
msgs,
latest,
} => client
.propose_release_funds(title, description, mock_coin(), None)
.propose(title, description, msgs, latest, None)
.ignore(),
MultisigExecuteMsg::Vote { proposal_id, vote } => {
client.vote(proposal_id, vote, None).ignore()
@@ -27,7 +27,7 @@ impl Mul<Gas> for &GasPrice {
fn mul(self, gas_limit: Gas) -> Self::Output {
let limit_uint128 = Uint128::from(gas_limit);
let mut amount = self.amount * limit_uint128;
let mut amount = limit_uint128.mul_floor(self.amount);
let gas_price_numerator = self.amount.numerator();
let gas_price_denominator = self.amount.denominator();
@@ -35,7 +35,7 @@ impl Mul<Gas> for &GasPrice {
// gas price is a fraction of the smallest fee token unit, so we must ensure that
// for any multiplication, we have rounded up
//
// I don't really like the this solution as it has a theoretical chance of
// I don't really like this solution as it has a theoretical chance of
// overflowing (internally cosmwasm uses U256 to avoid that)
// however, realistically that is impossible to happen as the resultant value
// would have to be way higher than our token limit of 10^15 (1 billion of tokens * 1 million for denomination)
@@ -155,7 +155,7 @@ async fn fetch_delegation_data(
match event.event.kind {
// If a pending undelegate tx is found, remove it from delegation map
PendingEpochEventKind::Undelegate { owner, node_id, .. } => {
if owner == address.as_ref()
if owner.as_str() == address.as_ref()
&& existing_delegation_map.contains_key(&node_id.to_string())
{
existing_delegation_map.remove(&node_id.to_string());
@@ -169,7 +169,7 @@ async fn fetch_delegation_data(
amount,
..
} => {
if owner == address.as_ref() {
if owner.as_str() == address.as_ref() {
let mut amount = Coin::from(amount);
if let Some(pending_record) = pending_delegation_map.get(&node_id.to_string()) {
amount.amount += pending_record.amount;
@@ -54,7 +54,7 @@ pub async fn create(args: Args, client: SigningClient, network_details: &NymNetw
let denom = network_details.chain_details.mix_denom.base.to_string();
let coin = Coin::new(args.amount.into(), &denom);
let coin = Coin::new(args.amount, &denom);
let res = client
.create_periodic_vesting_account(
@@ -1,16 +0,0 @@
[package]
name = "nym-coconut-bandwidth-contract-common"
version = "0.1.0"
edition = "2021"
license.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true }
cw2 = { workspace = true, optional = true }
nym-multisig-contract-common = { path = "../multisig-contract" }
[features]
schema = ["cw2"]
@@ -1,33 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::cw_serde;
#[cw_serde]
pub struct DepositData {
deposit_info: String,
identity_key: String,
encryption_key: String,
}
impl DepositData {
pub fn new(deposit_info: String, identity_key: String, encryption_key: String) -> Self {
DepositData {
deposit_info,
identity_key,
encryption_key,
}
}
pub fn deposit_info(&self) -> &str {
&self.deposit_info
}
pub fn identity_key(&self) -> &str {
&self.identity_key
}
pub fn encryption_key(&self) -> &str {
&self.encryption_key
}
}
@@ -1,4 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub const BANDWIDTH_PROPOSAL_ID: &str = "proposal_id";
@@ -1,11 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// event types
pub const DEPOSITED_FUNDS_EVENT_TYPE: &str = "deposited-funds";
// attributes that are used in multiple places
pub const DEPOSIT_VALUE: &str = "deposit-value";
pub const DEPOSIT_INFO: &str = "deposit-info";
pub const DEPOSIT_IDENTITY_KEY: &str = "deposit-identity-key";
pub const DEPOSIT_ENCRYPTION_KEY: &str = "deposit-encryption-key";
@@ -1,5 +0,0 @@
pub mod deposit;
pub mod event_attributes;
pub mod events;
pub mod msg;
pub mod spend_credential;
@@ -1,41 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::{deposit::DepositData, spend_credential::SpendCredentialData};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Coin;
#[cfg(feature = "schema")]
use crate::spend_credential::{PagedSpendCredentialResponse, SpendCredentialResponse};
#[cfg(feature = "schema")]
use cosmwasm_schema::QueryResponses;
#[cw_serde]
pub struct InstantiateMsg {
pub multisig_addr: String,
pub pool_addr: String,
pub mix_denom: String,
}
#[cw_serde]
pub enum ExecuteMsg {
DepositFunds { data: DepositData },
SpendCredential { data: SpendCredentialData },
ReleaseFunds { funds: Coin },
}
#[cw_serde]
#[cfg_attr(feature = "schema", derive(QueryResponses))]
pub enum QueryMsg {
#[cfg_attr(feature = "schema", returns(SpendCredentialResponse))]
GetSpentCredential { blinded_serial_number: String },
#[cfg_attr(feature = "schema", returns(PagedSpendCredentialResponse))]
GetAllSpentCredentials {
limit: Option<u32>,
start_after: Option<String>,
},
}
#[cw_serde]
pub struct MigrateMsg {}
@@ -1,152 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{from_binary, to_binary, Addr, Coin, CosmosMsg, StdResult, WasmMsg};
use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg;
use crate::msg::ExecuteMsg;
#[cw_serde]
pub struct SpendCredentialData {
funds: Coin,
blinded_serial_number: String,
gateway_cosmos_address: String,
}
impl SpendCredentialData {
pub fn new(funds: Coin, blinded_serial_number: String, gateway_cosmos_address: String) -> Self {
SpendCredentialData {
funds,
blinded_serial_number,
gateway_cosmos_address,
}
}
pub fn funds(&self) -> &Coin {
&self.funds
}
pub fn blinded_serial_number(&self) -> &str {
&self.blinded_serial_number
}
pub fn gateway_cosmos_address(&self) -> &str {
&self.gateway_cosmos_address
}
}
#[cw_serde]
#[derive(Copy)]
pub enum SpendCredentialStatus {
#[serde(alias = "InProgress")]
InProgress,
#[serde(alias = "Spent")]
Spent,
}
#[cw_serde]
pub struct SpendCredential {
funds: Coin,
blinded_serial_number: String,
gateway_cosmos_address: Addr,
status: SpendCredentialStatus,
}
impl SpendCredential {
pub fn new(funds: Coin, blinded_serial_number: String, gateway_cosmos_address: Addr) -> Self {
SpendCredential {
funds,
blinded_serial_number,
gateway_cosmos_address,
status: SpendCredentialStatus::InProgress,
}
}
pub fn blinded_serial_number(&self) -> &str {
&self.blinded_serial_number
}
pub fn status(&self) -> SpendCredentialStatus {
self.status
}
pub fn mark_as_spent(&mut self) {
self.status = SpendCredentialStatus::Spent;
}
}
#[cw_serde]
pub struct PagedSpendCredentialResponse {
pub spend_credentials: Vec<SpendCredential>,
pub per_page: usize,
/// Field indicating paging information for the following queries if the caller wishes to get further entries.
pub start_next_after: Option<String>,
}
impl PagedSpendCredentialResponse {
pub fn new(
spend_credentials: Vec<SpendCredential>,
per_page: usize,
start_next_after: Option<String>,
) -> Self {
PagedSpendCredentialResponse {
spend_credentials,
per_page,
start_next_after,
}
}
}
#[cw_serde]
pub struct SpendCredentialResponse {
pub spend_credential: Option<SpendCredential>,
}
impl SpendCredentialResponse {
pub fn new(spend_credential: Option<SpendCredential>) -> Self {
SpendCredentialResponse { spend_credential }
}
}
pub fn to_cosmos_msg(
funds: Coin,
blinded_serial_number: String,
coconut_bandwidth_addr: String,
multisig_addr: String,
) -> StdResult<CosmosMsg> {
let release_funds_req = ExecuteMsg::ReleaseFunds { funds };
let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: coconut_bandwidth_addr,
msg: to_binary(&release_funds_req)?,
funds: vec![],
});
let req = MultisigExecuteMsg::Propose {
title: String::from("Release funds, as ordered by Coconut Bandwidth Contract"),
description: blinded_serial_number,
msgs: vec![release_funds_msg],
latest: None,
};
let msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: multisig_addr,
msg: to_binary(&req)?,
funds: vec![],
});
Ok(msg)
}
pub fn funds_from_cosmos_msgs(msgs: Vec<CosmosMsg>) -> Option<Coin> {
if let Some(CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: _,
msg,
funds: _,
})) = msgs.first()
{
if let Ok(ExecuteMsg::ReleaseFunds { funds }) = from_binary::<ExecuteMsg>(msg) {
return Some(funds);
}
}
None
}
@@ -4,7 +4,7 @@
use crate::msg::ExecuteMsg;
use crate::types::{EpochId, NodeIndex};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{from_binary, to_binary, Addr, CosmosMsg, StdResult, Timestamp, WasmMsg};
use cosmwasm_std::{from_json, to_json_binary, Addr, CosmosMsg, StdResult, Timestamp, WasmMsg};
use cw_utils::Expiration;
use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg;
@@ -49,7 +49,7 @@ pub fn to_cosmos_msg(
};
let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: coconut_dkg_addr,
msg: to_binary(&verify_vk_share_req)?,
msg: to_json_binary(&verify_vk_share_req)?,
funds: vec![],
});
let req = MultisigExecuteMsg::Propose {
@@ -60,7 +60,7 @@ pub fn to_cosmos_msg(
};
let msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: multisig_addr,
msg: to_binary(&req)?,
msg: to_json_binary(&req)?,
funds: vec![],
});
@@ -82,7 +82,7 @@ pub fn owner_from_cosmos_msgs(msgs: &[CosmosMsg]) -> Option<String> {
})) = msgs.first()
{
if let Ok(ExecuteMsg::VerifyVerificationKeyShare { owner, .. }) =
from_binary::<ExecuteMsg>(msg)
from_json::<ExecuteMsg>(msg)
{
return Some(owner);
}
@@ -1,7 +1,7 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{from_slice, to_vec, Addr, Coin, MessageInfo, StdResult};
use cosmwasm_std::{from_json, to_json_vec, Addr, Coin, MessageInfo, StdResult};
use schemars::JsonSchema;
use serde::de::DeserializeOwned;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
@@ -164,7 +164,7 @@ where
where
T: Serialize,
{
to_vec(self)
to_json_vec(self)
}
pub fn to_sha256_plaintext_digest(&self) -> StdResult<Vec<u8>>
@@ -195,7 +195,7 @@ where
where
T: DeserializeOwned,
{
from_slice(bytes)
from_json(bytes)
}
pub fn try_from_string(raw: &str) -> StdResult<SignableMessage<T>>
@@ -2,9 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Decimal;
use cosmwasm_std::OverflowError;
use cosmwasm_std::Uint128;
use cosmwasm_std::{Decimal, Fraction};
use serde::de::Error;
use serde::{Deserialize, Deserializer};
use std::fmt::{self, Display, Formatter};
@@ -17,7 +17,7 @@ pub type IdentityKey = String;
pub type IdentityKeyRef<'a> = &'a str;
pub fn truncate_decimal(amount: Decimal) -> Uint128 {
amount * Uint128::new(1)
Uint128::new(1).mul_floor(amount)
}
#[derive(Error, Debug)]
@@ -113,11 +113,17 @@ impl Mul<Percent> for Decimal {
}
}
impl Mul<Uint128> for Percent {
type Output = Uint128;
impl Fraction<Uint128> for Percent {
fn numerator(&self) -> Uint128 {
self.0.numerator()
}
fn mul(self, rhs: Uint128) -> Self::Output {
self.0 * rhs
fn denominator(&self) -> Uint128 {
self.0.denominator()
}
fn inv(&self) -> Option<Self> {
Percent::new(self.0.inv()?).ok()
}
}
@@ -0,0 +1,14 @@
[package]
name = "easy-addr"
version = "0.1.0"
edition = "2021"
publish = false
license.workspace = true
[lib]
proc-macro = true
[dependencies]
cosmwasm-std = { workspace = true }
quote = { workspace = true }
syn = { workspace = true, features = ["full", "printing", "extra-traits"] }
@@ -0,0 +1,12 @@
use cosmwasm_std::testing::MockApi;
use proc_macro::TokenStream;
use quote::quote;
use syn::parse_macro_input;
#[proc_macro]
pub fn addr(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as syn::LitStr).value();
let addr = MockApi::default().addr_make(input.as_str()).to_string();
TokenStream::from(quote! {#addr})
}
@@ -241,10 +241,10 @@ mod tests {
#[test]
fn gateway_bond_partial_ord() {
let _150foos = Coin::new(150, "foo");
let _140foos = Coin::new(140, "foo");
let _50foos = Coin::new(50, "foo");
let _0foos = Coin::new(0, "foo");
let _150foos = Coin::new(150u32, "foo");
let _140foos = Coin::new(140u32, "foo");
let _50foos = Coin::new(50u32, "foo");
let _0foos = Coin::new(0u32, "foo");
let gate1 = GatewayBond {
pledge_amount: _150foos.clone(),
@@ -34,8 +34,10 @@ where
{
fn into_base_decimal(self) -> StdResult<Decimal> {
let atomics = self.into();
Decimal::from_atomics(atomics, 0).map_err(|_| StdError::GenericErr {
msg: format!("Decimal range exceeded for {atomics} with 0 decimal places."),
Decimal::from_atomics(atomics, 0).map_err(|_| {
StdError::generic_err(format!(
"Decimal range exceeded for {atomics} with 0 decimal places."
))
})
}
}
@@ -77,6 +77,8 @@ impl<'a> PrimaryKey<'a> for Role {
impl KeyDeserialize for Role {
type Output = Role;
const KEY_ELEMS: u16 = 1;
fn from_vec(value: Vec<u8>) -> StdResult<Self::Output> {
let u8_key: <u8 as KeyDeserialize>::Output = <u8 as KeyDeserialize>::from_vec(value)?;
Role::try_from(u8_key).map_err(|err| StdError::generic_err(err.to_string()))
@@ -242,7 +242,7 @@ mod tests {
#[allow(clippy::unwrap_used)]
fn base_simulator(initial_pledge: u128) -> Simulator {
let profit_margin = Percent::from_percentage_value(10).unwrap();
let interval_operating_cost = Coin::new(40_000_000, "unym");
let interval_operating_cost = Coin::new(40_000_000u64, "unym");
let epochs_in_interval = 720u32;
let interval_pool_emission = Percent::from_percentage_value(2).unwrap();
@@ -347,7 +347,7 @@ mod tests {
fn single_delegation_at_genesis() {
let mut simulator = base_simulator(10000_000000);
simulator
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
.delegate("alice", Coin::new(18000_000000u64, "unym"), 0)
.unwrap();
let node_params = NodeRewardingParameters::new(
@@ -393,7 +393,7 @@ mod tests {
compare_decimals(rewards1.operator, expected_operator1, None);
simulator
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
.delegate("alice", Coin::new(18000_000000u64, "unym"), 0)
.unwrap();
let rewards2 = simulator.simulate_epoch_single_node(node_params).unwrap();
@@ -439,10 +439,10 @@ mod tests {
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
// delegating at different times still work)
simulator
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
.delegate("alice", Coin::new(18000_000000u64, "unym"), 0)
.unwrap();
simulator
.delegate("bob", Coin::new(4000_000000, "unym"), 0)
.delegate("bob", Coin::new(4000_000000u64, "unym"), 0)
.unwrap();
// "normal", sanity check rewarding
@@ -484,10 +484,10 @@ mod tests {
// add 2 delegations at genesis (because it makes things easier and as shown with previous tests
// delegating at different times still work)
simulator
.delegate("alice", Coin::new(18000_000000, "unym"), 0)
.delegate("alice", Coin::new(18000_000000u64, "unym"), 0)
.unwrap();
simulator
.delegate("bob", Coin::new(4000_000000, "unym"), 0)
.delegate("bob", Coin::new(4000_000000u64, "unym"), 0)
.unwrap();
// "normal", sanity check rewarding
@@ -553,12 +553,12 @@ mod tests {
for epoch in 0..720 {
if epoch == 0 {
simulator
.delegate("a", Coin::new(18000_000000, "unym"), 0)
.delegate("a", Coin::new(18000_000000u64, "unym"), 0)
.unwrap()
}
if epoch == 42 {
simulator
.delegate("b", Coin::new(2000_000000, "unym"), 0)
.delegate("b", Coin::new(2000_000000u64, "unym"), 0)
.unwrap()
}
if epoch == 89 {
@@ -566,7 +566,7 @@ mod tests {
}
if epoch == 123 {
simulator
.delegate("c", Coin::new(6666_000000, "unym"), 0)
.delegate("c", Coin::new(6666_000000u64, "unym"), 0)
.unwrap()
}
if epoch == 167 {
@@ -574,7 +574,7 @@ mod tests {
}
if epoch == 245 {
simulator
.delegate("d", Coin::new(2050_000000, "unym"), 0)
.delegate("d", Coin::new(2050_000000u64, "unym"), 0)
.unwrap()
}
if epoch == 264 {
@@ -597,7 +597,7 @@ mod tests {
}
if epoch == 545 {
simulator
.delegate("e", Coin::new(5000_000000, "unym"), 0)
.delegate("e", Coin::new(5000_000000u64, "unym"), 0)
.unwrap()
}
@@ -666,132 +666,132 @@ mod tests {
let n0 = simulator
.bond(
Coin::new(11_000_000_000000, "unym"),
Coin::new(11_000_000_000000u64, "unym"),
NodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
interval_operating_cost: Coin::new(40_000_000u64, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(1_000_000_000000, "unym"), n0)
.delegate("delegator", Coin::new(1_000_000_000000u64, "unym"), n0)
.unwrap();
let n1 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
Coin::new(1_000_000_000000u64, "unym"),
NodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
interval_operating_cost: Coin::new(40_000_000u64, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(11_000_000_000000, "unym"), n1)
.delegate("delegator", Coin::new(11_000_000_000000u64, "unym"), n1)
.unwrap();
let n2 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
Coin::new(1_000_000_000000u64, "unym"),
NodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
interval_operating_cost: Coin::new(40_000_000u64, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(9_000_000_000000, "unym"), n2)
.delegate("delegator", Coin::new(9_000_000_000000u64, "unym"), n2)
.unwrap();
let n3 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
Coin::new(1_000_000_000000u64, "unym"),
NodeCostParams {
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
interval_operating_cost: Coin::new(500_000_000, "unym"),
interval_operating_cost: Coin::new(500_000_000u64, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n3)
.delegate("delegator", Coin::new(7_000_000_000000u64, "unym"), n3)
.unwrap();
let n4 = simulator
.bond(
Coin::new(1000_000000, "unym"),
Coin::new(1000_000000u64, "unym"),
NodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
interval_operating_cost: Coin::new(40_000_000u64, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(7_999_000_000000, "unym"), n4)
.delegate("delegator", Coin::new(7_999_000_000000u64, "unym"), n4)
.unwrap();
let n5 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
Coin::new(1_000_000_000000u64, "unym"),
NodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
interval_operating_cost: Coin::new(40_000_000u64, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n5)
.delegate("delegator", Coin::new(7_000_000_000000u64, "unym"), n5)
.unwrap();
let n6 = simulator
.bond(
Coin::new(11_000_000_000000, "unym"),
Coin::new(11_000_000_000000u64, "unym"),
NodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
interval_operating_cost: Coin::new(40_000_000u64, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(1_000_000_000000, "unym"), n6)
.delegate("delegator", Coin::new(1_000_000_000000u64, "unym"), n6)
.unwrap();
let n7 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
Coin::new(1_000_000_000000u64, "unym"),
NodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
interval_operating_cost: Coin::new(40_000_000u64, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(9_000_000_000000, "unym"), n7)
.delegate("delegator", Coin::new(9_000_000_000000u64, "unym"), n7)
.unwrap();
let n8 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
Coin::new(1_000_000_000000u64, "unym"),
NodeCostParams {
profit_margin_percent: Percent::from_percentage_value(0).unwrap(),
interval_operating_cost: Coin::new(500_000_000, "unym"),
interval_operating_cost: Coin::new(500_000_000u64, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n8)
.delegate("delegator", Coin::new(7_000_000_000000u64, "unym"), n8)
.unwrap();
let n9 = simulator
.bond(
Coin::new(1_000_000_000000, "unym"),
Coin::new(1_000_000_000000u64, "unym"),
NodeCostParams {
profit_margin_percent: Percent::from_percentage_value(10).unwrap(),
interval_operating_cost: Coin::new(40_000_000, "unym"),
interval_operating_cost: Coin::new(40_000_000u64, "unym"),
},
)
.unwrap();
simulator
.delegate("delegator", Coin::new(7_000_000_000000, "unym"), n9)
.delegate("delegator", Coin::new(7_000_000_000000u64, "unym"), n9)
.unwrap();
let uptime_1 = Percent::from_percentage_value(100).unwrap();
@@ -3,7 +3,7 @@
use crate::ecash::error::EcashTicketError;
use crate::Error;
use cosmwasm_std::{from_binary, CosmosMsg, WasmMsg};
use cosmwasm_std::{from_json, CosmosMsg, WasmMsg};
use nym_credentials_interface::VerificationKeyAuth;
use nym_ecash_contract_common::msg::ExecuteMsg;
use nym_gateway_storage::GatewayStorage;
@@ -72,7 +72,7 @@ impl SharedState {
let CosmosMsg::Wasm(WasmMsg::Execute { msg, .. }) = msg else {
return false;
};
let Ok(ExecuteMsg::RedeemTickets { gw, .. }) = from_binary(msg) else {
let Ok(ExecuteMsg::RedeemTickets { gw, .. }) = from_json(msg) else {
return false;
};
@@ -269,9 +269,8 @@ pub fn check_vk_pairing(
#[cfg(test)]
mod tests {
use rand::RngCore;
use super::*;
use rand::RngCore;
#[test]
fn polynomial_evaluation() {
+559 -487
View File
File diff suppressed because it is too large Load Diff
+17 -19
View File
@@ -1,9 +1,7 @@
[workspace]
resolver = "2"
members = [
# "coconut-bandwidth",
"coconut-dkg",
"coconut-test",
"ecash",
"mixnet",
"mixnet-vesting-integration-tests",
@@ -33,24 +31,24 @@ overflow-checks = true
[workspace.dependencies]
anyhow = "1.0.86"
bs58 = "0.4.0"
cosmwasm-crypto = "=1.4.3"
cosmwasm-derive = "=1.4.3"
cosmwasm-schema = "=1.4.3"
cosmwasm-std = "=1.4.3"
cosmwasm-storage = "=1.4.3"
cw-controllers = "=1.1.0"
cw-multi-test = "=0.16.5"
cw-storage-plus = "=1.2.0"
cw-utils = "=1.0.1"
cw2 = "=1.1.2"
cw3 = "=1.1.2"
cw3-fixed-multisig = "=1.1.2"
cw4 = "=1.1.2"
cw20 = "=1.1.2"
bs58 = "0.5.1"
cosmwasm-crypto = "=2.2.2"
cosmwasm-derive = "=2.2.2"
cosmwasm-schema = "=2.2.2"
cosmwasm-std = "=2.2.2"
cw-controllers = "=2.0.0"
cw-multi-test = "=2.3.2"
cw-storage-plus = "=2.0.0"
cw-utils = "=2.0.0"
cw2 = "=2.0.0"
cw3 = "=2.0.0"
cw3-fixed-multisig = "=2.0.0"
cw4 = "=2.0.0"
cw20 = "=2.0.0"
cw20-base = "2.0.0"
semver = "1.0.21"
serde = "1.0.196"
sylvia = "0.8.0"
sylvia = "1.3.3"
schemars = "0.8.16"
thiserror = "1.0.48"
thiserror = "2.0.11"
-3
View File
@@ -1,8 +1,5 @@
schema: coconut-dkg-schema mixnet-schema vesting-schema multisig-schema group-schema ecash-schema
#coconut-bandwidth-schema:
# $(MAKE) -C coconut-bandwidth generate-schema
coconut-dkg-schema:
$(MAKE) -C coconut-dkg generate-schema
@@ -1,4 +0,0 @@
[alias]
wasm = "build --release --lib --target wasm32-unknown-unknown"
unit-test = "test --lib"
schema = "run --bin schema --features=schema-gen"
-29
View File
@@ -1,29 +0,0 @@
[package]
name = "nym-coconut-bandwidth"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "schema"
required-features = ["schema-gen"]
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
nym-coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" }
cosmwasm-std = { workspace = true }
cosmwasm-schema = { workspace = true, optional = true }
cosmwasm-storage = { workspace = true }
cw-storage-plus = { workspace = true }
cw-controllers = { workspace = true }
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { workspace = true }
[features]
schema-gen = ["nym-coconut-bandwidth-contract-common/schema", "cosmwasm-schema"]
-2
View File
@@ -1,2 +0,0 @@
generate-schema:
cargo schema
@@ -1,382 +0,0 @@
{
"contract_name": "nym-coconut-bandwidth",
"contract_version": "0.1.0",
"idl_version": "1.0.0",
"instantiate": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "InstantiateMsg",
"type": "object",
"required": [
"mix_denom",
"multisig_addr",
"pool_addr"
],
"properties": {
"mix_denom": {
"type": "string"
},
"multisig_addr": {
"type": "string"
},
"pool_addr": {
"type": "string"
}
},
"additionalProperties": false
},
"execute": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ExecuteMsg",
"oneOf": [
{
"type": "object",
"required": [
"deposit_funds"
],
"properties": {
"deposit_funds": {
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"$ref": "#/definitions/DepositData"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"spend_credential"
],
"properties": {
"spend_credential": {
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"$ref": "#/definitions/SpendCredentialData"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"release_funds"
],
"properties": {
"release_funds": {
"type": "object",
"required": [
"funds"
],
"properties": {
"funds": {
"$ref": "#/definitions/Coin"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"DepositData": {
"type": "object",
"required": [
"deposit_info",
"encryption_key",
"identity_key"
],
"properties": {
"deposit_info": {
"type": "string"
},
"encryption_key": {
"type": "string"
},
"identity_key": {
"type": "string"
}
},
"additionalProperties": false
},
"SpendCredentialData": {
"type": "object",
"required": [
"blinded_serial_number",
"funds",
"gateway_cosmos_address"
],
"properties": {
"blinded_serial_number": {
"type": "string"
},
"funds": {
"$ref": "#/definitions/Coin"
},
"gateway_cosmos_address": {
"type": "string"
}
},
"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"
}
}
},
"query": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "QueryMsg",
"oneOf": [
{
"type": "object",
"required": [
"get_spent_credential"
],
"properties": {
"get_spent_credential": {
"type": "object",
"required": [
"blinded_serial_number"
],
"properties": {
"blinded_serial_number": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_all_spent_credentials"
],
"properties": {
"get_all_spent_credentials": {
"type": "object",
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
]
},
"migrate": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "MigrateMsg",
"type": "object",
"additionalProperties": false
},
"sudo": null,
"responses": {
"get_all_spent_credentials": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PagedSpendCredentialResponse",
"type": "object",
"required": [
"per_page",
"spend_credentials"
],
"properties": {
"per_page": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"spend_credentials": {
"type": "array",
"items": {
"$ref": "#/definitions/SpendCredential"
}
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"SpendCredential": {
"type": "object",
"required": [
"blinded_serial_number",
"funds",
"gateway_cosmos_address",
"status"
],
"properties": {
"blinded_serial_number": {
"type": "string"
},
"funds": {
"$ref": "#/definitions/Coin"
},
"gateway_cosmos_address": {
"$ref": "#/definitions/Addr"
},
"status": {
"$ref": "#/definitions/SpendCredentialStatus"
}
},
"additionalProperties": false
},
"SpendCredentialStatus": {
"type": "string",
"enum": [
"in_progress",
"spent"
]
},
"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"
}
}
},
"get_spent_credential": {
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "SpendCredentialResponse",
"type": "object",
"properties": {
"spend_credential": {
"anyOf": [
{
"$ref": "#/definitions/SpendCredential"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"SpendCredential": {
"type": "object",
"required": [
"blinded_serial_number",
"funds",
"gateway_cosmos_address",
"status"
],
"properties": {
"blinded_serial_number": {
"type": "string"
},
"funds": {
"$ref": "#/definitions/Coin"
},
"gateway_cosmos_address": {
"$ref": "#/definitions/Addr"
},
"status": {
"$ref": "#/definitions/SpendCredentialStatus"
}
},
"additionalProperties": false
},
"SpendCredentialStatus": {
"type": "string",
"enum": [
"in_progress",
"spent"
]
},
"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,130 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ExecuteMsg",
"oneOf": [
{
"type": "object",
"required": [
"deposit_funds"
],
"properties": {
"deposit_funds": {
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"$ref": "#/definitions/DepositData"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"spend_credential"
],
"properties": {
"spend_credential": {
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"$ref": "#/definitions/SpendCredentialData"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"release_funds"
],
"properties": {
"release_funds": {
"type": "object",
"required": [
"funds"
],
"properties": {
"funds": {
"$ref": "#/definitions/Coin"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
],
"definitions": {
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"DepositData": {
"type": "object",
"required": [
"deposit_info",
"encryption_key",
"identity_key"
],
"properties": {
"deposit_info": {
"type": "string"
},
"encryption_key": {
"type": "string"
},
"identity_key": {
"type": "string"
}
},
"additionalProperties": false
},
"SpendCredentialData": {
"type": "object",
"required": [
"blinded_serial_number",
"funds",
"gateway_cosmos_address"
],
"properties": {
"blinded_serial_number": {
"type": "string"
},
"funds": {
"$ref": "#/definitions/Coin"
},
"gateway_cosmos_address": {
"type": "string"
}
},
"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,22 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "InstantiateMsg",
"type": "object",
"required": [
"mix_denom",
"multisig_addr",
"pool_addr"
],
"properties": {
"mix_denom": {
"type": "string"
},
"multisig_addr": {
"type": "string"
},
"pool_addr": {
"type": "string"
}
},
"additionalProperties": false
}
@@ -1,6 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "MigrateMsg",
"type": "object",
"additionalProperties": false
}
@@ -1,56 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "QueryMsg",
"oneOf": [
{
"type": "object",
"required": [
"get_spent_credential"
],
"properties": {
"get_spent_credential": {
"type": "object",
"required": [
"blinded_serial_number"
],
"properties": {
"blinded_serial_number": {
"type": "string"
}
},
"additionalProperties": false
}
},
"additionalProperties": false
},
{
"type": "object",
"required": [
"get_all_spent_credentials"
],
"properties": {
"get_all_spent_credentials": {
"type": "object",
"properties": {
"limit": {
"type": [
"integer",
"null"
],
"format": "uint32",
"minimum": 0.0
},
"start_after": {
"type": [
"string",
"null"
]
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
]
}
@@ -1,86 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "PagedSpendCredentialResponse",
"type": "object",
"required": [
"per_page",
"spend_credentials"
],
"properties": {
"per_page": {
"type": "integer",
"format": "uint",
"minimum": 0.0
},
"spend_credentials": {
"type": "array",
"items": {
"$ref": "#/definitions/SpendCredential"
}
},
"start_next_after": {
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
"type": [
"string",
"null"
]
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"SpendCredential": {
"type": "object",
"required": [
"blinded_serial_number",
"funds",
"gateway_cosmos_address",
"status"
],
"properties": {
"blinded_serial_number": {
"type": "string"
},
"funds": {
"$ref": "#/definitions/Coin"
},
"gateway_cosmos_address": {
"$ref": "#/definitions/Addr"
},
"status": {
"$ref": "#/definitions/SpendCredentialStatus"
}
},
"additionalProperties": false
},
"SpendCredentialStatus": {
"type": "string",
"enum": [
"in_progress",
"spent"
]
},
"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,74 +0,0 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "SpendCredentialResponse",
"type": "object",
"properties": {
"spend_credential": {
"anyOf": [
{
"$ref": "#/definitions/SpendCredential"
},
{
"type": "null"
}
]
}
},
"additionalProperties": false,
"definitions": {
"Addr": {
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
"type": "string"
},
"Coin": {
"type": "object",
"required": [
"amount",
"denom"
],
"properties": {
"amount": {
"$ref": "#/definitions/Uint128"
},
"denom": {
"type": "string"
}
}
},
"SpendCredential": {
"type": "object",
"required": [
"blinded_serial_number",
"funds",
"gateway_cosmos_address",
"status"
],
"properties": {
"blinded_serial_number": {
"type": "string"
},
"funds": {
"$ref": "#/definitions/Coin"
},
"gateway_cosmos_address": {
"$ref": "#/definitions/Addr"
},
"status": {
"$ref": "#/definitions/SpendCredentialStatus"
}
},
"additionalProperties": false
},
"SpendCredentialStatus": {
"type": "string",
"enum": [
"in_progress",
"spent"
]
},
"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,16 +0,0 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_schema::write_api;
use nym_coconut_bandwidth_contract_common::msg::{
ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg,
};
fn main() {
write_api! {
instantiate: InstantiateMsg,
query: QueryMsg,
execute: ExecuteMsg,
migrate: MigrateMsg,
}
}
-110
View File
@@ -1,110 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{
entry_point, to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult,
};
use nym_coconut_bandwidth_contract_common::msg::{
ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg,
};
use crate::error::ContractError;
use crate::queries::{query_all_spent_credentials_paged, query_spent_credential};
use crate::state::{Config, ADMIN, CONFIG};
use crate::transactions;
/// Instantiate the contract.
///
/// `deps` contains Storage, API and Querier
/// `msg` is the contract initialization message, sort of like a constructor call.
#[entry_point]
pub fn instantiate(
mut deps: DepsMut<'_>,
_env: Env,
_info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Response, ContractError> {
let multisig_addr = deps.api.addr_validate(&msg.multisig_addr)?;
let pool_addr = deps.api.addr_validate(&msg.pool_addr)?;
let mix_denom = msg.mix_denom;
ADMIN.set(deps.branch(), Some(multisig_addr.clone()))?;
let cfg = Config {
multisig_addr,
pool_addr,
mix_denom,
};
CONFIG.save(deps.storage, &cfg)?;
Ok(Response::default())
}
/// Handle an incoming message
#[entry_point]
pub fn execute(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::DepositFunds { data } => transactions::deposit_funds(deps, env, info, data),
ExecuteMsg::SpendCredential { data } => {
transactions::spend_credential(deps, env, info, data)
}
ExecuteMsg::ReleaseFunds { funds } => transactions::release_funds(deps, env, info, funds),
}
}
#[entry_point]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
match msg {
QueryMsg::GetAllSpentCredentials { limit, start_after } => to_binary(
&query_all_spent_credentials_paged(deps, start_after, limit)?,
),
QueryMsg::GetSpentCredential {
blinded_serial_number,
} => to_binary(&query_spent_credential(deps, blinded_serial_number)?),
}
}
#[entry_point]
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
Ok(Default::default())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::tests::fixtures::TEST_MIX_DENOM;
use crate::support::tests::helpers::*;
use cosmwasm_std::coins;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
#[test]
fn initialize_contract() {
let mut deps = mock_dependencies();
let env = mock_env();
let msg = InstantiateMsg {
multisig_addr: String::from(MULTISIG_CONTRACT),
pool_addr: String::from(POOL_CONTRACT),
mix_denom: TEST_MIX_DENOM.to_string(),
};
let info = mock_info("creator", &[]);
let res = instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
assert_eq!(0, res.messages.len());
// Contract balance should be 0
assert_eq!(
coins(0, TEST_MIX_DENOM),
vec![deps
.as_ref()
.querier
.query_balance(env.contract.address, TEST_MIX_DENOM)
.unwrap()]
);
}
}
-34
View File
@@ -1,34 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::StdError;
use cw_controllers::AdminError;
use thiserror::Error;
/// Custom errors for contract failure conditions.
///
/// Add any other custom errors you like here.
/// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details.
#[derive(Error, Debug, PartialEq)]
pub enum ContractError {
#[error(transparent)]
Std(#[from] StdError),
#[error("Received multiple coin types")]
MultipleDenoms,
#[error("No coin was sent for voucher")]
NoCoin,
#[error("Wrong coin denomination, you must send {mix_denom}")]
WrongDenom { mix_denom: String },
#[error("There aren't enough funds in the contract")]
NotEnoughFunds,
#[error("Credential already spent or in process of spending")]
DuplicateBlindedSerialNumber,
#[error(transparent)]
Admin(#[from] AdminError),
}
-10
View File
@@ -1,10 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod contract;
pub mod error;
mod queries;
mod state;
mod storage;
mod support;
mod transactions;
-178
View File
@@ -1,178 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Deps, Order, StdResult};
use cw_storage_plus::Bound;
use nym_coconut_bandwidth_contract_common::spend_credential::{
PagedSpendCredentialResponse, SpendCredential, SpendCredentialResponse,
};
use crate::storage::{self, SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT, SPEND_CREDENTIAL_PAGE_MAX_LIMIT};
pub(crate) fn query_all_spent_credentials_paged(
deps: Deps<'_>,
start_after: Option<String>,
limit: Option<u32>,
) -> StdResult<PagedSpendCredentialResponse> {
let limit = limit
.unwrap_or(SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT)
.min(SPEND_CREDENTIAL_PAGE_MAX_LIMIT) as usize;
let start = start_after.as_deref().map(Bound::exclusive);
let nodes = storage::spent_credentials()
.range(deps.storage, start, None, Order::Ascending)
.take(limit)
.map(|res| res.map(|item| item.1))
.collect::<StdResult<Vec<SpendCredential>>>()?;
let start_next_after = nodes
.last()
.map(|spend_credential| spend_credential.blinded_serial_number().to_string());
Ok(PagedSpendCredentialResponse::new(
nodes,
limit,
start_next_after,
))
}
pub(crate) fn query_spent_credential(
deps: Deps<'_>,
blinded_serial_number: String,
) -> StdResult<SpendCredentialResponse> {
let spend_credential =
storage::spent_credentials().may_load(deps.storage, &blinded_serial_number)?;
Ok(SpendCredentialResponse::new(spend_credential))
}
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::support::tests::fixtures::spend_credential_data_fixture;
use crate::support::tests::helpers::init_contract;
use crate::transactions::spend_credential;
use cosmwasm_std::testing::{mock_env, mock_info};
#[test]
fn spent_credentials_empty_on_init() {
let deps = init_contract();
let response =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(2)).unwrap();
assert_eq!(0, response.spend_credentials.len());
}
#[test]
fn spent_credentials_paged_retrieval_obeys_limits() {
let mut deps = init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
let limit = 2;
for n in 0..1000 {
let data = spend_credential_data_fixture(&format!("blinded_serial_number{}", n));
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
}
let page1 =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(limit)).unwrap();
assert_eq!(limit, page1.spend_credentials.len() as u32);
}
#[test]
fn spent_credentials_paged_retrieval_has_default_limit() {
let mut deps = init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
for n in 0..1000 {
let data = spend_credential_data_fixture(&format!("blinded_serial_number{}", n));
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
}
// query without explicitly setting a limit
let page1 = query_all_spent_credentials_paged(deps.as_ref(), None, None).unwrap();
assert_eq!(
SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT,
page1.spend_credentials.len() as u32
);
}
#[test]
fn spent_credentials_paged_retrieval_has_max_limit() {
let mut deps = init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
for n in 0..1000 {
let data = spend_credential_data_fixture(&format!("blinded_serial_number{}", n));
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
}
// query with a crazily high limit in an attempt to use too many resources
let crazy_limit = 1000 * SPEND_CREDENTIAL_PAGE_MAX_LIMIT;
let page1 =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(crazy_limit))
.unwrap();
// we default to a decent sized upper bound instead
let expected_limit = SPEND_CREDENTIAL_PAGE_MAX_LIMIT;
assert_eq!(expected_limit, page1.spend_credentials.len() as u32);
}
#[test]
fn spent_credentials_pagination_works() {
let mut deps = init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
let data = spend_credential_data_fixture("blinded_serial_number1");
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
let per_page = 2;
let page1 =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
// page should have 1 result on it
assert_eq!(1, page1.spend_credentials.len());
// save another
let data = spend_credential_data_fixture("blinded_serial_number2");
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
// page1 should have 2 results on it
let page1 =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
assert_eq!(2, page1.spend_credentials.len());
let data = spend_credential_data_fixture("blinded_serial_number3");
spend_credential(deps.as_mut(), env.clone(), info.clone(), data).unwrap();
// page1 still has 2 results
let page1 =
query_all_spent_credentials_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
assert_eq!(2, page1.spend_credentials.len());
// retrieving the next page should start after the last key on this page
let start_after = page1.start_next_after.unwrap();
let page2 = query_all_spent_credentials_paged(
deps.as_ref(),
Option::from(start_after.clone()),
Option::from(per_page),
)
.unwrap();
assert_eq!(1, page2.spend_credentials.len());
let data = spend_credential_data_fixture("blinded_serial_number4");
spend_credential(deps.as_mut(), env, info, data).unwrap();
let page2 = query_all_spent_credentials_paged(
deps.as_ref(),
Option::from(start_after),
Option::from(per_page),
)
.unwrap();
// now we have 2 pages, with 2 results on the second page
assert_eq!(2, page2.spend_credentials.len());
}
}
-18
View File
@@ -1,18 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::Addr;
use cw_controllers::Admin;
use cw_storage_plus::Item;
use serde::{Deserialize, Serialize};
pub const ADMIN: Admin = Admin::new("admin");
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
pub struct Config {
pub multisig_addr: Addr,
pub pool_addr: Addr,
pub mix_denom: String,
}
pub const CONFIG: Item<Config> = Item::new("config");
-106
View File
@@ -1,106 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cw_storage_plus::{Index, IndexList, IndexedMap, UniqueIndex};
use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredential;
// storage prefixes
const SPEND_CREDENTIAL_PK_NAMESPACE: &str = "sc";
const SPEND_CREDENTIAL_BLINDED_SERIAL_NO_IDX_NAMESPACE: &str = "scn";
// paged retrieval limits for all queries and transactions
pub(crate) const SPEND_CREDENTIAL_PAGE_MAX_LIMIT: u32 = 75;
pub(crate) const SPEND_CREDENTIAL_PAGE_DEFAULT_LIMIT: u32 = 50;
pub(crate) struct SpendCredentialIndex<'a> {
pub(crate) blinded_serial_number: UniqueIndex<'a, String, SpendCredential>,
}
// IndexList is just boilerplate code for fetching a struct's indexes
// note that from my understanding this will be converted into a macro at some point in the future
impl IndexList<SpendCredential> for SpendCredentialIndex<'_> {
fn get_indexes(&'_ self) -> Box<dyn Iterator<Item = &'_ dyn Index<SpendCredential>> + '_> {
let v: Vec<&dyn Index<SpendCredential>> = vec![&self.blinded_serial_number];
Box::new(v.into_iter())
}
}
// spent_credentials() is the storage access function.
pub(crate) fn spent_credentials<'a>(
) -> IndexedMap<'a, &'a str, SpendCredential, SpendCredentialIndex<'a>> {
let indexes = SpendCredentialIndex {
blinded_serial_number: UniqueIndex::new(
|d| d.blinded_serial_number().to_string(),
SPEND_CREDENTIAL_BLINDED_SERIAL_NO_IDX_NAMESPACE,
),
};
IndexedMap::new(SPEND_CREDENTIAL_PK_NAMESPACE, indexes)
}
// currently not used outside tests
#[cfg(test)]
mod tests {
use super::super::storage;
use crate::storage::SpendCredential;
use crate::support::tests::fixtures;
use crate::support::tests::fixtures::TEST_MIX_DENOM;
use cosmwasm_std::testing::MockStorage;
use cosmwasm_std::Addr;
use cosmwasm_std::Coin;
use nym_coconut_bandwidth_contract_common::spend_credential::SpendCredentialStatus;
#[test]
fn spend_credential_single_read_retrieval() {
let mut storage = MockStorage::new();
let blind_serial_number1 = "number1";
let blind_serial_number2 = "number2";
let spend1 = fixtures::spend_credential_fixture(blind_serial_number1);
let spend2 = fixtures::spend_credential_fixture(blind_serial_number2);
storage::spent_credentials()
.save(&mut storage, blind_serial_number1, &spend1)
.unwrap();
storage::spent_credentials()
.save(&mut storage, blind_serial_number2, &spend2)
.unwrap();
let res1 = storage::spent_credentials()
.load(&storage, blind_serial_number1)
.unwrap();
let res2 = storage::spent_credentials()
.load(&storage, blind_serial_number2)
.unwrap();
assert_eq!(spend1, res1);
assert_eq!(spend2, res2);
}
#[test]
fn mark_as_spent_credential() {
let mut mock_storage = MockStorage::new();
let funds = Coin::new(100, TEST_MIX_DENOM);
let blind_serial_number = "blind_serial_number";
let gateway_cosmos_address: Addr = Addr::unchecked("gateway_cosmos_address");
let res = storage::spent_credentials()
.may_load(&mock_storage, blind_serial_number)
.unwrap();
assert!(res.is_none());
let mut spend_credential = SpendCredential::new(
funds,
blind_serial_number.to_string(),
gateway_cosmos_address,
);
spend_credential.mark_as_spent();
storage::spent_credentials()
.save(&mut mock_storage, blind_serial_number, &spend_credential)
.unwrap();
let ret = storage::spent_credentials()
.load(&mock_storage, blind_serial_number)
.unwrap();
assert_eq!(ret, spend_credential);
assert_eq!(ret.status(), SpendCredentialStatus::Spent);
}
}
@@ -1,5 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(test)]
pub mod tests;
@@ -1,25 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Addr, Coin};
use nym_coconut_bandwidth_contract_common::spend_credential::{
SpendCredential, SpendCredentialData,
};
pub const TEST_MIX_DENOM: &str = "unym";
pub fn spend_credential_fixture(blinded_serial_number: &str) -> SpendCredential {
SpendCredential::new(
Coin::new(100, TEST_MIX_DENOM),
blinded_serial_number.to_string(),
Addr::unchecked("gateway_owner_addr"),
)
}
pub fn spend_credential_data_fixture(blinded_serial_number: &str) -> SpendCredentialData {
SpendCredentialData::new(
Coin::new(100, TEST_MIX_DENOM),
blinded_serial_number.to_string(),
"gateway_owner_addr".to_string(),
)
}
@@ -1,25 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
pub const POOL_CONTRACT: &str = "mix pool contract address";
use crate::contract::instantiate;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
use cosmwasm_std::{Empty, MemoryStorage, OwnedDeps};
use nym_coconut_bandwidth_contract_common::msg::InstantiateMsg;
use super::fixtures::TEST_MIX_DENOM;
pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
let mut deps = mock_dependencies();
let msg = InstantiateMsg {
multisig_addr: String::from(MULTISIG_CONTRACT),
pool_addr: String::from(POOL_CONTRACT),
mix_denom: TEST_MIX_DENOM.to_string(),
};
let env = mock_env();
let info = mock_info("creator", &[]);
instantiate(deps.as_mut(), env, info, msg).unwrap();
deps
}
@@ -1,5 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod fixtures;
pub mod helpers;
@@ -1,369 +0,0 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{BankMsg, Coin, DepsMut, Env, Event, MessageInfo, Response};
use nym_coconut_bandwidth_contract_common::spend_credential::{
to_cosmos_msg, SpendCredential, SpendCredentialData,
};
use crate::error::ContractError;
use crate::state::{ADMIN, CONFIG};
use crate::storage;
use nym_coconut_bandwidth_contract_common::deposit::DepositData;
use nym_coconut_bandwidth_contract_common::events::{
DEPOSITED_FUNDS_EVENT_TYPE, DEPOSIT_ENCRYPTION_KEY, DEPOSIT_IDENTITY_KEY, DEPOSIT_INFO,
DEPOSIT_VALUE,
};
pub(crate) fn deposit_funds(
deps: DepsMut<'_>,
_env: Env,
info: MessageInfo,
data: DepositData,
) -> Result<Response, ContractError> {
if info.funds.is_empty() {
return Err(ContractError::NoCoin);
}
if info.funds.len() > 1 {
return Err(ContractError::MultipleDenoms);
}
let mix_denom = CONFIG.load(deps.storage)?.mix_denom;
if info.funds[0].denom != mix_denom {
return Err(ContractError::WrongDenom { mix_denom });
}
let voucher_value = info.funds.last().unwrap();
let event = Event::new(DEPOSITED_FUNDS_EVENT_TYPE)
.add_attribute(DEPOSIT_VALUE, voucher_value.amount)
.add_attribute(DEPOSIT_INFO, data.deposit_info())
.add_attribute(DEPOSIT_IDENTITY_KEY, data.identity_key())
.add_attribute(DEPOSIT_ENCRYPTION_KEY, data.encryption_key());
Ok(Response::new().add_event(event))
}
pub(crate) fn spend_credential(
deps: DepsMut<'_>,
env: Env,
_info: MessageInfo,
data: SpendCredentialData,
) -> Result<Response, ContractError> {
let mix_denom = CONFIG.load(deps.storage)?.mix_denom;
if data.funds().denom != mix_denom {
return Err(ContractError::WrongDenom { mix_denom });
}
if storage::spent_credentials().has(deps.storage, data.blinded_serial_number()) {
return Err(ContractError::DuplicateBlindedSerialNumber);
}
let cfg = CONFIG.load(deps.storage)?;
let gateway_cosmos_address = deps.api.addr_validate(data.gateway_cosmos_address())?;
storage::spent_credentials().save(
deps.storage,
data.blinded_serial_number(),
&SpendCredential::new(
data.funds().to_owned(),
data.blinded_serial_number().to_owned(),
gateway_cosmos_address,
),
)?;
let msg = to_cosmos_msg(
data.funds().clone(),
data.blinded_serial_number().to_string(),
env.contract.address.into_string(),
cfg.multisig_addr.into_string(),
)?;
Ok(Response::new().add_message(msg))
}
pub(crate) fn release_funds(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
funds: Coin,
) -> Result<Response, ContractError> {
let mix_denom = CONFIG.load(deps.storage)?.mix_denom;
if funds.denom != mix_denom {
return Err(ContractError::WrongDenom { mix_denom });
}
let current_balance = deps
.querier
.query_balance(env.contract.address, mix_denom)?;
if funds.amount > current_balance.amount {
return Err(ContractError::NotEnoughFunds);
}
ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
let cfg = CONFIG.load(deps.storage)?;
let return_tokens = BankMsg::Send {
to_address: cfg.pool_addr.into(),
amount: vec![funds],
};
let response = Response::new().add_message(return_tokens);
Ok(response)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::tests::fixtures::spend_credential_data_fixture;
use crate::support::tests::helpers::{self, MULTISIG_CONTRACT, POOL_CONTRACT};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{from_binary, CosmosMsg, WasmMsg};
use cw_controllers::AdminError;
use nym_coconut_bandwidth_contract_common::msg::ExecuteMsg;
use nym_multisig_contract_common::msg::ExecuteMsg as MultisigExecuteMsg;
#[test]
fn invalid_deposit() {
let mut deps = helpers::init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
let deposit_info = String::from("Deposit info");
let verification_key = String::from("Verification key");
let encryption_key = String::from("Encryption key");
let data = DepositData::new(deposit_info, verification_key, encryption_key);
assert_eq!(
deposit_funds(deps.as_mut(), env.clone(), info, data.clone()),
Err(ContractError::NoCoin)
);
let coin = Coin::new(1000000, crate::support::tests::fixtures::TEST_MIX_DENOM);
let second_coin = Coin::new(1000000, "some_denom");
let info = mock_info("requester", &[coin, second_coin.clone()]);
assert_eq!(
deposit_funds(deps.as_mut(), env.clone(), info, data.clone()),
Err(ContractError::MultipleDenoms)
);
let info = mock_info("requester", &[second_coin]);
assert_eq!(
deposit_funds(deps.as_mut(), env, info, data),
Err(ContractError::WrongDenom {
mix_denom: crate::support::tests::fixtures::TEST_MIX_DENOM.to_string()
})
);
}
#[test]
fn valid_deposit() {
let mut deps = helpers::init_contract();
let env = mock_env();
let deposit_info = String::from("Deposit info");
let verification_key = String::from("Verification key");
let encryption_key = String::from("Encryption key");
let deposit_value = 424242;
let data = DepositData::new(
deposit_info.clone(),
verification_key.clone(),
encryption_key.clone(),
);
let coin = Coin::new(
deposit_value,
crate::support::tests::fixtures::TEST_MIX_DENOM,
);
let info = mock_info("requester", &[coin]);
let tx = deposit_funds(deps.as_mut(), env, info, data).unwrap();
let events: Vec<_> = tx
.events
.iter()
.filter(|event| event.ty == DEPOSITED_FUNDS_EVENT_TYPE)
.collect();
assert_eq!(events.len(), 1);
let event = events[0];
assert_eq!(event.attributes.len(), 4);
let deposit_attr = event
.attributes
.iter()
.find(|attr| attr.key == DEPOSIT_VALUE)
.unwrap();
assert_eq!(deposit_attr.value, deposit_value.to_string());
let info_attr = event
.attributes
.iter()
.find(|attr| attr.key == DEPOSIT_INFO)
.unwrap();
assert_eq!(info_attr.value, deposit_info);
let verification_key_attr = event
.attributes
.iter()
.find(|attr| attr.key == DEPOSIT_IDENTITY_KEY)
.unwrap();
assert_eq!(verification_key_attr.value, verification_key);
let encryption_key_attr = event
.attributes
.iter()
.find(|attr| attr.key == DEPOSIT_ENCRYPTION_KEY)
.unwrap();
assert_eq!(encryption_key_attr.value, encryption_key);
}
#[test]
fn invalid_release() {
let mut deps = helpers::init_contract();
let env = mock_env();
let invalid_admin = "invalid admin";
let funds = Coin::new(1, crate::support::tests::fixtures::TEST_MIX_DENOM);
let err = release_funds(
deps.as_mut(),
env.clone(),
mock_info(invalid_admin, &[]),
Coin::new(1, "invalid denom"),
)
.unwrap_err();
assert_eq!(
err,
ContractError::WrongDenom {
mix_denom: crate::support::tests::fixtures::TEST_MIX_DENOM.to_string()
}
);
let err = release_funds(
deps.as_mut(),
env.clone(),
mock_info(invalid_admin, &[]),
funds.clone(),
)
.unwrap_err();
assert_eq!(err, ContractError::NotEnoughFunds);
deps.querier
.update_balance(env.contract.address.clone(), vec![funds.clone()]);
let err =
release_funds(deps.as_mut(), env, mock_info(invalid_admin, &[]), funds).unwrap_err();
assert_eq!(err, ContractError::Admin(AdminError::NotAdmin {}));
}
#[test]
fn valid_release() {
let mut deps = helpers::init_contract();
let env = mock_env();
let coin = Coin::new(1, crate::support::tests::fixtures::TEST_MIX_DENOM);
deps.querier
.update_balance(env.contract.address.clone(), vec![coin.clone()]);
let res = release_funds(
deps.as_mut(),
env,
mock_info(MULTISIG_CONTRACT, &[]),
coin.clone(),
)
.unwrap();
assert_eq!(
res.messages[0].msg,
CosmosMsg::Bank(BankMsg::Send {
to_address: String::from(POOL_CONTRACT),
amount: vec![coin]
})
);
}
#[test]
fn valid_spend() {
let mut deps = helpers::init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
let data = spend_credential_data_fixture("blinded_serial_number");
let res = spend_credential(deps.as_mut(), env.clone(), info, data.clone()).unwrap();
if let CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
msg,
funds,
}) = &res.messages[0].msg
{
assert_eq!(contract_addr, MULTISIG_CONTRACT);
assert!(funds.is_empty());
let multisig_msg: MultisigExecuteMsg = from_binary(msg).unwrap();
if let MultisigExecuteMsg::Propose {
title: _,
description,
msgs,
latest,
} = multisig_msg
{
assert_eq!(description, data.blinded_serial_number().to_string());
assert!(latest.is_none());
if let CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr,
msg,
funds,
}) = &msgs[0]
{
assert_eq!(*contract_addr, env.contract.address.into_string());
assert!(funds.is_empty());
let release_funds_req: ExecuteMsg = from_binary(msg).unwrap();
if let ExecuteMsg::ReleaseFunds { funds } = release_funds_req {
assert_eq!(funds, *data.funds());
} else {
panic!("Could not extract release funds message from proposal");
}
}
} else {
panic!("Could not extract proposal from binary blob");
}
} else {
panic!("Wasm execute message not found");
}
}
#[test]
fn invalid_spend_attempts() {
let mut deps = helpers::init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
let invalid_data = SpendCredentialData::new(
Coin::new(1, "invalid_denom".to_string()),
String::new(),
String::new(),
);
let ret = spend_credential(deps.as_mut(), env.clone(), info.clone(), invalid_data);
assert_eq!(
ret.unwrap_err(),
ContractError::WrongDenom {
mix_denom: crate::support::tests::fixtures::TEST_MIX_DENOM.to_string()
}
);
let invalid_data = SpendCredentialData::new(
Coin::new(1, crate::support::tests::fixtures::TEST_MIX_DENOM),
String::new(),
"Blinded Serial Number".to_string(),
);
let ret = spend_credential(deps.as_mut(), env.clone(), info.clone(), invalid_data);
assert_eq!(
ret.unwrap_err().to_string(),
"Generic error: Invalid input: address not normalized".to_string()
);
let invalid_data = spend_credential_data_fixture("blined_serial_number");
spend_credential(
deps.as_mut(),
env.clone(),
info.clone(),
invalid_data.clone(),
)
.unwrap();
let ret = spend_credential(deps.as_mut(), env, info, invalid_data);
assert_eq!(
ret.unwrap_err(),
ContractError::DuplicateBlindedSerialNumber
);
}
}
+6 -3
View File
@@ -1,7 +1,10 @@
[package]
name = "nym-coconut-dkg"
version = "0.1.0"
edition = "2021"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -18,15 +21,15 @@ nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts
cosmwasm-schema = { workspace = true, optional = true }
cosmwasm-std = { workspace = true }
cosmwasm-storage = { workspace = true }
cw-storage-plus = { workspace = true }
cw-controllers = { workspace = true }
cw2 = { workspace = true }
cw4 = { workspace = true }
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { workspace = true }
[dev-dependencies]
easy-addr = { path = "../../common/cosmwasm-smart-contracts/easy_addr" }
cw-multi-test = { workspace = true }
cw4-group = { path = "../multisig/cw4-group" }
nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" }
@@ -1357,9 +1357,7 @@
},
"dealing_submission_status": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/DealingStatus"
}
"additionalProperties": false
},
"epoch_id": {
"type": "integer",
@@ -1397,9 +1395,7 @@
"properties": {
"chunk_submission_status": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/ChunkSubmissionStatus"
}
"additionalProperties": false
},
"fully_submitted": {
"type": "boolean"
@@ -1744,9 +1740,7 @@
"properties": {
"chunk_submission_status": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/ChunkSubmissionStatus"
}
"additionalProperties": false
},
"fully_submitted": {
"type": "boolean"
@@ -1841,9 +1835,7 @@
},
"submitted_chunks": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/SubmittedChunk"
}
"additionalProperties": false
}
},
"additionalProperties": false
@@ -17,9 +17,7 @@
},
"dealing_submission_status": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/DealingStatus"
}
"additionalProperties": false
},
"epoch_id": {
"type": "integer",
@@ -57,9 +55,7 @@
"properties": {
"chunk_submission_status": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/ChunkSubmissionStatus"
}
"additionalProperties": false
},
"fully_submitted": {
"type": "boolean"
@@ -56,9 +56,7 @@
"properties": {
"chunk_submission_status": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/ChunkSubmissionStatus"
}
"additionalProperties": false
},
"fully_submitted": {
"type": "boolean"
@@ -80,9 +80,7 @@
},
"submitted_chunks": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/SubmittedChunk"
}
"additionalProperties": false
}
},
"additionalProperties": false
+30 -24
View File
@@ -26,7 +26,7 @@ use crate::verification_key_shares::queries::{query_vk_share, query_vk_shares_pa
use crate::verification_key_shares::transactions::try_commit_verification_key_share;
use crate::verification_key_shares::transactions::try_verify_verification_key_share;
use cosmwasm_std::{
entry_point, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
entry_point, to_json_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
};
use cw4::Cw4Contract;
use nym_coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
@@ -129,50 +129,52 @@ pub fn execute(
#[entry_point]
pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
let response = match msg {
QueryMsg::GetState {} => to_binary(&query_state(deps.storage)?)?,
QueryMsg::GetCurrentEpochState {} => to_binary(&query_current_epoch(deps.storage)?)?,
QueryMsg::CanAdvanceState {} => to_binary(&query_can_advance_state(deps.storage, env)?)?,
QueryMsg::GetState {} => to_json_binary(&query_state(deps.storage)?)?,
QueryMsg::GetCurrentEpochState {} => to_json_binary(&query_current_epoch(deps.storage)?)?,
QueryMsg::CanAdvanceState {} => {
to_json_binary(&query_can_advance_state(deps.storage, env)?)?
}
QueryMsg::GetCurrentEpochThreshold {} => {
to_binary(&query_current_epoch_threshold(deps.storage)?)?
to_json_binary(&query_current_epoch_threshold(deps.storage)?)?
}
QueryMsg::GetEpochThreshold { epoch_id } => {
to_binary(&query_epoch_threshold(deps.storage, epoch_id)?)?
to_json_binary(&query_epoch_threshold(deps.storage, epoch_id)?)?
}
QueryMsg::GetRegisteredDealer {
dealer_address,
epoch_id,
} => to_binary(&query_registered_dealer_details(
} => to_json_binary(&query_registered_dealer_details(
deps,
dealer_address,
epoch_id,
)?)?,
QueryMsg::GetDealerDetails { dealer_address } => {
to_binary(&query_dealer_details(deps, dealer_address)?)?
to_json_binary(&query_dealer_details(deps, dealer_address)?)?
}
QueryMsg::GetCurrentDealers { limit, start_after } => {
to_binary(&query_current_dealers_paged(deps, start_after, limit)?)?
to_json_binary(&query_current_dealers_paged(deps, start_after, limit)?)?
}
QueryMsg::GetDealerIndices { limit, start_after } => {
to_binary(&query_dealers_indices_paged(deps, start_after, limit)?)?
to_json_binary(&query_dealers_indices_paged(deps, start_after, limit)?)?
}
QueryMsg::GetDealingsMetadata {
epoch_id,
dealer,
dealing_index,
} => to_binary(&query_dealing_metadata(
} => to_json_binary(&query_dealing_metadata(
deps,
epoch_id,
dealer,
dealing_index,
)?)?,
QueryMsg::GetDealerDealingsStatus { epoch_id, dealer } => {
to_binary(&query_dealer_dealings_status(deps, epoch_id, dealer)?)?
to_json_binary(&query_dealer_dealings_status(deps, epoch_id, dealer)?)?
}
QueryMsg::GetDealingStatus {
epoch_id,
dealer,
dealing_index,
} => to_binary(&query_dealing_status(
} => to_json_binary(&query_dealing_status(
deps,
epoch_id,
dealer,
@@ -183,7 +185,7 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
dealer,
dealing_index,
chunk_index,
} => to_binary(&query_dealing_chunk_status(
} => to_json_binary(&query_dealing_chunk_status(
deps,
epoch_id,
dealer,
@@ -195,7 +197,7 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
dealer,
dealing_index,
chunk_index,
} => to_binary(&query_dealing_chunk(
} => to_json_binary(&query_dealing_chunk(
deps,
epoch_id,
dealer,
@@ -203,14 +205,16 @@ pub fn query(deps: Deps<'_>, env: Env, msg: QueryMsg) -> Result<QueryResponse, C
chunk_index,
)?)?,
QueryMsg::GetVerificationKey { owner, epoch_id } => {
to_binary(&query_vk_share(deps, owner, epoch_id)?)?
to_json_binary(&query_vk_share(deps, owner, epoch_id)?)?
}
QueryMsg::GetVerificationKeys {
epoch_id,
limit,
start_after,
} => to_binary(&query_vk_shares_paged(deps, epoch_id, start_after, limit)?)?,
QueryMsg::GetCW2ContractVersion {} => to_binary(&cw2::get_contract_version(deps.storage)?)?,
} => to_json_binary(&query_vk_shares_paged(deps, epoch_id, start_after, limit)?)?,
QueryMsg::GetCW2ContractVersion {} => {
to_json_binary(&cw2::get_contract_version(deps.storage)?)?
}
};
Ok(response)
@@ -236,7 +240,7 @@ mod tests {
use super::*;
use crate::support::tests::fixtures::TEST_MIX_DENOM;
use crate::support::tests::helpers::{ADMIN_ADDRESS, MULTISIG_CONTRACT};
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env, MockApi};
use cosmwasm_std::{coins, Addr};
use cw4::Member;
use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor};
@@ -311,13 +315,13 @@ mod tests {
let mut deps = mock_dependencies();
let env = mock_env();
let msg = InstantiateMsg {
group_addr: "group_addr".to_string(),
multisig_addr: "multisig_addr".to_string(),
group_addr: deps.api.addr_make("group_addr").to_string(),
multisig_addr: deps.api.addr_make("multisig_addr").to_string(),
time_configuration: None,
mix_denom: "nym".to_string(),
key_size: 5,
};
let info = mock_info("creator", &[]);
let info = message_info(&deps.api.addr_make("creator"), &[]);
let res = instantiate(deps.as_mut(), env, info, msg);
assert!(res.is_ok())
@@ -326,9 +330,11 @@ mod tests {
#[test]
fn execute_add_dealer() {
let init_funds = coins(100, TEST_MIX_DENOM);
let api = MockApi::default();
const MEMBER_SIZE: usize = 100;
let members: [Addr; MEMBER_SIZE] =
std::array::from_fn(|idx| Addr::unchecked(format!("member{}", idx)));
std::array::from_fn(|idx| api.addr_make(&format!("member{}", idx)));
let mut app = AppBuilder::new().build(|router, _, storage| {
router
@@ -378,7 +384,7 @@ mod tests {
assert_eq!(ContractError::AlreadyADealer, err.downcast().unwrap());
}
let unauthorized_member = Addr::unchecked("not_a_member");
let unauthorized_member = MockApi::default().addr_make("not_a_member");
let err = app
.execute_contract(
unauthorized_member,
+31 -22
View File
@@ -131,21 +131,30 @@ pub(crate) mod tests {
use crate::dealers::storage::{DEALERS_PAGE_DEFAULT_LIMIT, DEALERS_PAGE_MAX_LIMIT};
use crate::support::tests::fixtures::dealer_details_fixture;
use crate::support::tests::helpers::{init_contract, insert_dealer};
use cosmwasm_std::DepsMut;
use cosmwasm_std::testing::{MockApi, MockQuerier};
use cosmwasm_std::{Empty, MemoryStorage, OwnedDeps};
fn fill_dealers(mut deps: DepsMut<'_>, epoch_id: EpochId, size: usize) {
fn fill_dealers(
deps: &mut OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>>,
epoch_id: EpochId,
size: usize,
) {
for assigned_index in 0..size {
let dealer_details = dealer_details_fixture(assigned_index as u64);
insert_dealer(deps.branch(), epoch_id, &dealer_details);
let dealer_details = dealer_details_fixture(&deps.api, assigned_index as u64);
insert_dealer(deps.as_mut(), epoch_id, &dealer_details);
}
}
fn remove_dealers(deps: DepsMut<'_>, epoch_id: EpochId, size: usize) {
fn remove_dealers(
deps: &mut OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>>,
epoch_id: EpochId,
size: usize,
) {
for assigned_index in 0..size {
let dealer_details = dealer_details_fixture(assigned_index as u64);
DEALERS_INDICES.remove(deps.storage, &dealer_details.address);
let dealer_details = dealer_details_fixture(&deps.api, assigned_index as u64);
DEALERS_INDICES.remove(deps.as_mut().storage, &dealer_details.address);
EPOCH_DEALERS_MAP.remove(deps.storage, (epoch_id, &dealer_details.address));
EPOCH_DEALERS_MAP.remove(deps.as_mut().storage, (epoch_id, &dealer_details.address));
}
}
@@ -162,26 +171,26 @@ pub(crate) mod tests {
let mut deps = init_contract();
let limit = 2;
fill_dealers(deps.as_mut(), 0, 1000);
fill_dealers(&mut deps, 0, 1000);
let page1 = query_current_dealers_paged(deps.as_ref(), None, Option::from(limit)).unwrap();
assert_eq!(limit, page1.dealers.len() as u32);
remove_dealers(deps.as_mut(), 0, 1000);
remove_dealers(&mut deps, 0, 1000);
}
#[test]
fn dealers_paged_retrieval_has_default_limit() {
let mut deps = init_contract();
fill_dealers(deps.as_mut(), 0, 1000);
fill_dealers(&mut deps, 0, 1000);
// query without explicitly setting a limit
let page1 = query_current_dealers_paged(deps.as_ref(), None, None).unwrap();
assert_eq!(DEALERS_PAGE_DEFAULT_LIMIT, page1.dealers.len() as u32);
remove_dealers(deps.as_mut(), 0, 1000);
remove_dealers(&mut deps, 0, 1000);
}
#[test]
@@ -191,7 +200,7 @@ pub(crate) mod tests {
// query with a crazily high limit in an attempt to use too many resources
let crazy_limit = 1000 * DEALERS_PAGE_MAX_LIMIT;
fill_dealers(deps.as_mut(), 0, 1000);
fill_dealers(&mut deps, 0, 1000);
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap();
@@ -200,7 +209,7 @@ pub(crate) mod tests {
let expected_limit = DEALERS_PAGE_MAX_LIMIT;
assert_eq!(expected_limit, page1.dealers.len() as u32);
remove_dealers(deps.as_mut(), 0, 1000);
remove_dealers(&mut deps, 0, 1000);
}
#[test]
@@ -209,22 +218,22 @@ pub(crate) mod tests {
let per_page = 2;
fill_dealers(deps.as_mut(), 0, 1);
fill_dealers(&mut deps, 0, 1);
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
// page should have 1 result on it
assert_eq!(1, page1.dealers.len());
remove_dealers(deps.as_mut(), 0, 1);
remove_dealers(&mut deps, 0, 1);
fill_dealers(deps.as_mut(), 0, 2);
fill_dealers(&mut deps, 0, 2);
// page1 should have 2 results on it
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
assert_eq!(2, page1.dealers.len());
remove_dealers(deps.as_mut(), 0, 2);
remove_dealers(&mut deps, 0, 2);
fill_dealers(deps.as_mut(), 0, 3);
fill_dealers(&mut deps, 0, 3);
// page1 still has 2 results
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
@@ -240,9 +249,9 @@ pub(crate) mod tests {
.unwrap();
assert_eq!(1, page2.dealers.len());
remove_dealers(deps.as_mut(), 0, 3);
remove_dealers(&mut deps, 0, 3);
fill_dealers(deps.as_mut(), 0, 4);
fill_dealers(&mut deps, 0, 4);
let page1 =
query_current_dealers_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
let start_after = page1.start_next_after.unwrap();
@@ -255,6 +264,6 @@ pub(crate) mod tests {
// now we have 2 pages, with 2 results on the second page
assert_eq!(2, page2.dealers.len());
remove_dealers(deps.as_mut(), 0, 4);
remove_dealers(&mut deps, 0, 4);
}
}
@@ -88,7 +88,7 @@ pub(crate) mod tests {
use crate::epoch_state::transactions::{try_advance_epoch_state, try_initiate_dkg};
use crate::support::tests::helpers;
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::testing::{message_info, mock_env};
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::types::TimeConfiguration;
@@ -96,10 +96,15 @@ pub(crate) mod tests {
fn invalid_state() {
let mut deps = helpers::init_contract();
let mut env = mock_env();
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
try_initiate_dkg(
deps.as_mut(),
env.clone(),
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
)
.unwrap();
let owner = Addr::unchecked("owner");
let info = mock_info(owner.as_str(), &[]);
let owner = deps.api.addr_make("owner");
let info = message_info(&owner, &[]);
let bte_key_with_proof = String::from("bte_key_with_proof");
let identity = String::from("identity");
let announce_address = String::from("localhost:8000");
+11 -12
View File
@@ -168,29 +168,26 @@ pub(crate) mod tests {
let mut deps = init_contract();
let bad_address = "FOOMP".to_string();
let good_address = deps.api.addr_make("foo");
assert!(query_dealing_chunk(deps.as_ref(), 0, bad_address, 0, 0).is_err());
let empty = query_dealing_chunk(deps.as_ref(), 0, "foo".to_string(), 0, 0).unwrap();
let empty = query_dealing_chunk(deps.as_ref(), 0, good_address.to_string(), 0, 0).unwrap();
assert_eq!(empty.epoch_id, 0);
assert_eq!(empty.dealing_index, 0);
assert_eq!(empty.chunk_index, 0);
assert_eq!(empty.dealer, Addr::unchecked("foo"));
assert_eq!(empty.dealer, good_address);
assert!(empty.chunk.is_none());
// insert the dealing chunk
let dealing = partial_dealing_fixture();
StoredDealing::save(
deps.as_mut().storage,
0,
&Addr::unchecked("foo"),
dealing.clone(),
);
StoredDealing::save(deps.as_mut().storage, 0, &good_address, dealing.clone());
let retrieved = query_dealing_chunk(deps.as_ref(), 0, "foo".to_string(), 0, 0).unwrap();
let retrieved =
query_dealing_chunk(deps.as_ref(), 0, good_address.to_string(), 0, 0).unwrap();
assert_eq!(retrieved.epoch_id, 0);
assert_eq!(retrieved.dealing_index, dealing.dealing_index);
assert_eq!(retrieved.chunk_index, dealing.chunk_index);
assert_eq!(retrieved.dealer, Addr::unchecked("foo"));
assert_eq!(retrieved.dealer, good_address);
assert_eq!(retrieved.chunk.unwrap(), dealing.data);
}
@@ -201,10 +198,12 @@ pub(crate) mod tests {
let bad_address = "FOOMP".to_string();
assert!(query_dealing_status(deps.as_ref(), 0, bad_address, 0).is_err());
let empty = query_dealing_status(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
let empty =
query_dealing_status(deps.as_ref(), 0, deps.api.addr_make("foo").to_string(), 0)
.unwrap();
assert_eq!(empty.epoch_id, 0);
assert_eq!(empty.dealing_index, 0);
assert_eq!(empty.dealer, Addr::unchecked("foo"));
assert_eq!(empty.dealer, deps.api.addr_make("foo"));
assert!(!empty.status.fully_submitted);
assert!(!empty.status.has_metadata);
assert!(empty.status.chunk_submission_status.is_empty());
+22 -24
View File
@@ -77,17 +77,10 @@ impl StoredDealing {
#[cfg(test)]
fn prefix(
prefix: (EpochId, Dealer, DealingIndex),
) -> cw_storage_plus::Prefix<ChunkIndex, PartialContractDealingData, ChunkIndex> {
) -> cw_storage_plus::Prefix<ChunkIndex, PartialContractDealingData> {
use cw_storage_plus::Prefixer;
cw_storage_plus::Prefix::with_deserialization_functions(
Self::NAMESPACE,
&prefix.prefix(),
&[],
// explicitly panic to make sure we're never attempting to call an unexpected deserializer on our data
|_, _, kv| Self::deserialize_dealing_record(kv),
|_, _, _| panic!("attempted to call custom de_fn_v"),
)
cw_storage_plus::Prefix::new(Self::NAMESPACE, &prefix.prefix())
}
// prefix-range related should we need it
@@ -98,8 +91,16 @@ impl StoredDealing {
start: Option<cw_storage_plus::Bound<ChunkIndex>>,
) -> impl Iterator<Item = cosmwasm_std::StdResult<PartialContractDealing>> + 'a {
let dealing_index = prefix.2;
Self::prefix(prefix)
.range(storage, start, None, cosmwasm_std::Order::Ascending)
let prefix = Self::prefix(prefix);
cw_storage_plus::range_with_prefix(
storage,
&prefix,
start.map(|b| b.to_raw_bound()),
None,
cosmwasm_std::Order::Ascending,
)
.map(Self::deserialize_dealing_record)
.map(move |maybe_record| {
maybe_record.map(|(chunk_index, data)| PartialContractDealing {
dealing_index,
@@ -186,20 +187,17 @@ impl StoredDealing {
type StorageKey<'a> = (EpochId, Dealer<'a>, (DealingIndex, ChunkIndex));
let empty_prefix: cw_storage_plus::Prefix<
StorageKey,
PartialContractDealingData,
StorageKey,
> = cw_storage_plus::Prefix::with_deserialization_functions(
Self::NAMESPACE,
&[],
&[],
|_, _, kv| StorageKey::from_vec(kv.0).map(|kt| (kt, ContractSafeBytes(kv.1))),
|_, _, _| unimplemented!(),
);
let empty_prefix: cw_storage_plus::Prefix<StorageKey, PartialContractDealingData> =
cw_storage_plus::Prefix::new(Self::NAMESPACE, &[]);
empty_prefix
.range(storage, None, None, cosmwasm_std::Order::Ascending)
cw_storage_plus::range_with_prefix(
storage,
&empty_prefix,
None,
None,
cosmwasm_std::Order::Ascending,
)
.map(|kv| StorageKey::from_vec(kv.0).map(|kt| (kt, ContractSafeBytes(kv.1))))
.collect::<cosmwasm_std::StdResult<_>>()
.unwrap()
}
@@ -210,7 +210,7 @@ pub(crate) mod tests {
use crate::support::tests::fixtures::{dealing_metadata_fixture, partial_dealing_fixture};
use crate::support::tests::helpers;
use crate::support::tests::helpers::{add_current_dealer, re_register_dealer, ADMIN_ADDRESS};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::testing::{message_info, mock_env};
use nym_coconut_dkg_common::dealer::DealerDetails;
use nym_coconut_dkg_common::types::{ContractSafeBytes, TimeConfiguration};
@@ -218,10 +218,15 @@ pub(crate) mod tests {
fn invalid_commit_dealing_chunk() {
let mut deps = helpers::init_contract();
let mut env = mock_env();
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
try_initiate_dkg(
deps.as_mut(),
env.clone(),
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
)
.unwrap();
let owner = Addr::unchecked("owner1");
let info = mock_info(owner.as_str(), &[]);
let owner = deps.api.addr_make("owner1");
let info = message_info(&owner, &[]);
let chunk = partial_dealing_fixture();
// no dealing metadata
@@ -57,7 +57,8 @@ pub(crate) mod test {
use super::*;
use crate::epoch_state::transactions::try_initiate_dkg;
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::testing::{message_info, mock_env};
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::types::TimeConfiguration;
#[test]
@@ -68,7 +69,12 @@ pub(crate) mod test {
assert_eq!(epoch.deadline, None);
let env = mock_env();
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
try_initiate_dkg(
deps.as_mut(),
env.clone(),
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
)
.unwrap();
let epoch = query_current_epoch(deps.as_mut().storage).unwrap();
assert_eq!(
@@ -4,7 +4,7 @@
use cw_storage_plus::{Item, Map};
use nym_coconut_dkg_common::types::{Epoch, EpochId};
pub(crate) const CURRENT_EPOCH: Item<'_, Epoch> = Item::new("current_epoch");
pub(crate) const CURRENT_EPOCH: Item<Epoch> = Item::new("current_epoch");
pub const THRESHOLD: Item<u64> = Item::new("threshold");
@@ -95,8 +95,8 @@ mod tests {
use crate::error::ContractError::EarlyEpochStateAdvancement;
use crate::state::storage::STATE;
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::{StdResult, Storage};
use cosmwasm_std::testing::{message_info, mock_env};
use cosmwasm_std::{Addr, StdResult, Storage};
use nym_coconut_dkg_common::types::TimeConfiguration;
#[test]
@@ -359,7 +359,12 @@ mod tests {
ContractError::WaitingInitialisation
);
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
try_initiate_dkg(
deps.as_mut(),
env.clone(),
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
)
.unwrap();
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
assert_eq!(
@@ -584,7 +589,12 @@ mod tests {
fn verify_threshold() {
let mut deps = init_contract();
let mut env = mock_env();
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
try_initiate_dkg(
deps.as_mut(),
env.clone(),
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
)
.unwrap();
assert!(THRESHOLD.may_load(deps.as_mut().storage).unwrap().is_none());
@@ -90,7 +90,8 @@ pub(crate) fn try_trigger_resharing(
pub(crate) mod tests {
use super::*;
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::testing::{message_info, mock_env};
use cosmwasm_std::Addr;
use cw_controllers::AdminError;
#[test]
@@ -101,16 +102,25 @@ pub(crate) mod tests {
let initial_epoch_info = CURRENT_EPOCH.load(&deps.storage).unwrap();
assert!(initial_epoch_info.deadline.is_none());
let not_admin = deps.api.addr_make("not an admin");
// can only be executed by the admin
let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info("not an admin", &[]))
let res = try_initiate_dkg(deps.as_mut(), env.clone(), message_info(&not_admin, &[]))
.unwrap_err();
assert_eq!(ContractError::Admin(AdminError::NotAdmin {}), res);
let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[]));
let res = try_initiate_dkg(
deps.as_mut(),
env.clone(),
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
);
assert!(res.is_ok());
// can't be initialised more than once
let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[]))
let res = try_initiate_dkg(
deps.as_mut(),
env.clone(),
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
)
.unwrap_err();
assert_eq!(ContractError::AlreadyInitialised, res);
@@ -1,6 +1,7 @@
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::testing::MockApi;
use cosmwasm_std::Addr;
use nym_coconut_dkg_common::dealer::DealerDetails;
use nym_coconut_dkg_common::dealing::{DealingChunkInfo, PartialContractDealing};
@@ -40,9 +41,9 @@ pub fn dealing_metadata_fixture() -> Vec<DealingChunkInfo> {
}]
}
pub fn dealer_details_fixture(assigned_index: u64) -> DealerDetails {
pub fn dealer_details_fixture(api: &MockApi, assigned_index: u64) -> DealerDetails {
DealerDetails {
address: Addr::unchecked(format!("owner{}", assigned_index)),
address: api.addr_make(&format!("owner{}", assigned_index)),
bte_public_key_with_proof: "".to_string(),
ed25519_identity: "".to_string(),
announce_address: "".to_string(),
@@ -4,12 +4,13 @@
use crate::contract::instantiate;
use crate::dealers::storage::{DEALERS_INDICES, EPOCH_DEALERS_MAP};
use crate::epoch_state::storage::CURRENT_EPOCH;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env, MockApi, MockQuerier};
use cosmwasm_std::{
from_binary, to_binary, Addr, ContractResult, DepsMut, Empty, MemoryStorage, OwnedDeps,
from_json, to_json_binary, Addr, ContractResult, DepsMut, Empty, MemoryStorage, OwnedDeps,
QuerierResult, SystemResult, WasmQuery,
};
use cw4::{Cw4QueryMsg, Member, MemberListResponse, MemberResponse};
use easy_addr::addr;
use nym_coconut_dkg_common::dealer::DealerRegistrationDetails;
use nym_coconut_dkg_common::dealing::DEFAULT_DEALINGS;
use nym_coconut_dkg_common::msg::InstantiateMsg;
@@ -18,9 +19,9 @@ use std::sync::Mutex;
use super::fixtures::TEST_MIX_DENOM;
pub const ADMIN_ADDRESS: &str = "admin address";
pub const GROUP_CONTRACT: &str = "group contract address";
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
pub const ADMIN_ADDRESS: &str = addr!("admin address");
pub const GROUP_CONTRACT: &str = addr!("group contract address");
pub const MULTISIG_CONTRACT: &str = addr!("multisig contract address");
// wtf, why is this a thing?
pub(crate) static GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(Vec::new());
@@ -79,7 +80,7 @@ fn querier_handler(query: &WasmQuery) -> QuerierResult {
if contract_addr != GROUP_CONTRACT {
panic!("Not supported");
}
match from_binary(msg) {
match from_json(msg) {
Ok(Cw4QueryMsg::Member { addr, at_height }) => {
let weight = GROUP_MEMBERS.lock().unwrap().iter().find_map(|(m, h)| {
if m.addr == addr {
@@ -93,7 +94,7 @@ fn querier_handler(query: &WasmQuery) -> QuerierResult {
None
}
});
to_binary(&MemberResponse { weight }).unwrap()
to_json_binary(&MemberResponse { weight }).unwrap()
}
Ok(Cw4QueryMsg::ListMembers { .. }) => {
let members = GROUP_MEMBERS
@@ -102,7 +103,7 @@ fn querier_handler(query: &WasmQuery) -> QuerierResult {
.iter()
.map(|m| m.0.clone())
.collect();
to_binary(&MemberListResponse { members }).unwrap()
to_json_binary(&MemberListResponse { members }).unwrap()
}
_ => panic!("Not supported"),
}
@@ -123,7 +124,7 @@ pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>>
key_size: DEFAULT_DEALINGS as u32,
};
let env = mock_env();
let info = mock_info(ADMIN_ADDRESS, &[]);
let info = message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]);
instantiate(deps.as_mut(), env, info, msg).unwrap();
deps
}
@@ -158,8 +158,17 @@ pub(crate) mod tests {
fn vk_shares_pagination_works() {
let mut deps = init_contract();
let owner = format!("owner{}", 1);
let vk_share = vk_share_fixture(&owner, 0);
let mut owners = [
deps.api.addr_make("owner1"),
deps.api.addr_make("owner2"),
deps.api.addr_make("owner3"),
deps.api.addr_make("owner4"),
];
// sort them due to how values are saved
owners.sort();
let owner = owners[0].clone();
let vk_share = vk_share_fixture(owner.as_ref(), 0);
let sender = Addr::unchecked(owner);
vk_shares()
.save(&mut deps.storage, (&sender, 0), &vk_share)
@@ -172,8 +181,8 @@ pub(crate) mod tests {
assert_eq!(1, page1.shares.len());
// save another
let owner = format!("owner{}", 2);
let vk_share = vk_share_fixture(&owner, 0);
let owner = owners[1].clone();
let vk_share = vk_share_fixture(owner.as_str(), 0);
let sender = Addr::unchecked(owner);
vk_shares()
.save(&mut deps.storage, (&sender, 0), &vk_share)
@@ -183,8 +192,8 @@ pub(crate) mod tests {
let page1 = query_vk_shares_paged(deps.as_ref(), 0, None, Option::from(per_page)).unwrap();
assert_eq!(2, page1.shares.len());
let owner = format!("owner{}", 3);
let vk_share = vk_share_fixture(&owner, 0);
let owner = owners[2].clone();
let vk_share = vk_share_fixture(owner.as_str(), 0);
let sender = Addr::unchecked(owner);
vk_shares()
.save(&mut deps.storage, (&sender, 0), &vk_share)
@@ -206,8 +215,8 @@ pub(crate) mod tests {
assert_eq!(1, page2.shares.len());
let owner = format!("owner{}", 4);
let vk_share = vk_share_fixture(&owner, 0);
let owner = owners[3].clone();
let vk_share = vk_share_fixture(owner.as_str(), 0);
let sender = Addr::unchecked(owner);
vk_shares()
.save(&mut deps.storage, (&sender, 0), &vk_share)
@@ -23,7 +23,7 @@ impl IndexList<ContractVKShare> for VkShareIndex<'_> {
}
}
pub(crate) fn vk_shares<'a>() -> IndexedMap<'a, VKShareKey<'a>, ContractVKShare, VkShareIndex<'a>> {
pub(crate) fn vk_shares<'a>() -> IndexedMap<VKShareKey<'a>, ContractVKShare, VkShareIndex<'a>> {
let indexes = VkShareIndex {
epoch_id: MultiIndex::new(
|_pk, d| d.epoch_id,
@@ -106,7 +106,7 @@ mod tests {
use crate::support::tests::helpers::{
add_current_dealer, add_fixture_dealer, ADMIN_ADDRESS, MULTISIG_CONTRACT,
};
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::testing::{message_info, mock_env};
use cosmwasm_std::Addr;
use cw_controllers::AdminError;
use nym_coconut_dkg_common::dealer::DealerDetails;
@@ -116,9 +116,14 @@ mod tests {
fn current_epoch_id() {
let mut deps = helpers::init_contract();
let mut env = mock_env();
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
try_initiate_dkg(
deps.as_mut(),
env.clone(),
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
)
.unwrap();
let info = mock_info("requester", &[]);
let info = message_info(&deps.api.addr_make("requester"), &[]);
let share = "share".to_string();
add_fixture_dealer(deps.as_mut());
@@ -132,7 +137,7 @@ mod tests {
.time
.plus_seconds(TimeConfiguration::default().dealing_exchange_time_secs);
try_advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
let dealer = Addr::unchecked("requester");
let dealer = deps.api.addr_make("requester");
let announce_address = String::from("localhost");
let dealer_details = DealerDetails {
address: dealer.clone(),
@@ -163,9 +168,14 @@ mod tests {
fn commit_vk_share() {
let mut deps = helpers::init_contract();
let mut env = mock_env();
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
try_initiate_dkg(
deps.as_mut(),
env.clone(),
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
)
.unwrap();
let info = mock_info("requester", &[]);
let info = message_info(&deps.api.addr_make("requester"), &[]);
let share = "share".to_string();
let ret = try_commit_verification_key_share(
@@ -205,7 +215,7 @@ mod tests {
.unwrap_err();
assert_eq!(ret, ContractError::NotADealer { epoch_id: 0 });
let dealer = Addr::unchecked("requester");
let dealer = deps.api.addr_make("requester");
let dealer_details = DealerDetails {
address: dealer.clone(),
bte_public_key_with_proof: String::new(),
@@ -238,11 +248,16 @@ mod tests {
fn invalid_verify_vk_share() {
let mut deps = helpers::init_contract();
let mut env = mock_env();
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
try_initiate_dkg(
deps.as_mut(),
env.clone(),
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
)
.unwrap();
let info = mock_info("requester", &[]);
let owner = "owner".to_string();
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
let info = message_info(&deps.api.addr_make("requester"), &[]);
let owner = deps.api.addr_make("owner").to_string();
let multisig_info = message_info(&Addr::unchecked(MULTISIG_CONTRACT), &[]);
let ret =
try_verify_verification_key_share(deps.as_mut(), info.clone(), owner.clone(), false)
@@ -297,12 +312,17 @@ mod tests {
fn verify_vk_share() {
let mut deps = helpers::init_contract();
let mut env = mock_env();
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
try_initiate_dkg(
deps.as_mut(),
env.clone(),
message_info(&Addr::unchecked(ADMIN_ADDRESS), &[]),
)
.unwrap();
let owner = "owner".to_string();
let info = mock_info(owner.as_ref(), &[]);
let owner = deps.api.addr_make("owner");
let info = message_info(&owner, &[]);
let share = "share".to_string();
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
let multisig_info = message_info(&Addr::unchecked(MULTISIG_CONTRACT), &[]);
add_fixture_dealer(deps.as_mut());
env.block.time = env
@@ -338,6 +358,7 @@ mod tests {
.plus_seconds(TimeConfiguration::default().verification_key_validation_time_secs);
try_advance_epoch_state(deps.as_mut(), env).unwrap();
try_verify_verification_key_share(deps.as_mut(), multisig_info, owner, false).unwrap();
try_verify_verification_key_share(deps.as_mut(), multisig_info, owner.to_string(), false)
.unwrap();
}
}
-39
View File
@@ -1,39 +0,0 @@
[package]
name = "coconut-test"
version = "0.1.0"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nym-coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
nym-coconut-dkg-common = { path = "../../common/cosmwasm-smart-contracts/coconut-dkg" }
nym-multisig-contract-common = { path = "../../common/cosmwasm-smart-contracts/multisig-contract" }
nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" }
cosmwasm-std = { workspace = true }
cosmwasm-storage = { workspace = true }
cw3 = { workspace = true }
cw4 = { workspace = true }
cw-storage-plus = { workspace = true }
cw-controllers = { workspace = true }
cw-utils = { workspace = true }
subtle-encoding = { version = "0.5", features = ["bech32-preview"] }
bs58 = "0.4.0"
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = { workspace = true }
nym-coconut-bandwidth = { path = "../coconut-bandwidth" }
nym-coconut-dkg = { path = "../coconut-dkg" }
cw-multi-test = { workspace = true }
cw3-flex-multisig = { path = "../multisig/cw3-flex-multisig" }
cw4-group = { path = "../multisig/cw4-group" }
rand_chacha = "0.3"
[[test]]
name = "coconut-test"
path = "src/tests.rs"
@@ -1,96 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::helpers::*;
use cosmwasm_std::{coins, Addr};
use cw_controllers::AdminError;
use cw_multi_test::Executor;
use nym_coconut_bandwidth::error::ContractError;
use nym_coconut_bandwidth_contract_common::{
deposit::DepositData,
msg::{ExecuteMsg, InstantiateMsg},
};
const TEST_MIX_DENOM: &str = "unym";
#[test]
fn deposit_and_release() {
let init_funds = coins(10, TEST_MIX_DENOM);
let deposit_funds = coins(1, TEST_MIX_DENOM);
let release_funds = coins(2, TEST_MIX_DENOM);
let mut app = mock_app(&init_funds);
let multisig_addr = String::from(MULTISIG_CONTRACT);
let pool_addr = String::from(POOL_CONTRACT);
let random_addr = String::from(RANDOM_ADDRESS);
let code_id = app.store_code(contract_bandwidth());
let msg = InstantiateMsg {
multisig_addr: multisig_addr.clone(),
pool_addr: pool_addr.clone(),
mix_denom: TEST_MIX_DENOM.to_string(),
};
let contract_addr = app
.instantiate_contract(
code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"bandwidth",
None,
)
.unwrap();
let msg = ExecuteMsg::DepositFunds {
data: DepositData::new(
String::from("info"),
String::from("id"),
String::from("enc"),
),
};
app.execute_contract(
Addr::unchecked(OWNER),
contract_addr.clone(),
&msg,
&deposit_funds,
)
.unwrap();
// try to release more then it's in the contract
let msg = ExecuteMsg::ReleaseFunds {
funds: release_funds[0].clone(),
};
let err = app
.execute_contract(
Addr::unchecked(multisig_addr.clone()),
contract_addr.clone(),
&msg,
&[],
)
.unwrap_err();
assert_eq!(ContractError::NotEnoughFunds, err.downcast().unwrap());
// try to call release from non-admin
let msg = ExecuteMsg::ReleaseFunds {
funds: deposit_funds[0].clone(),
};
let err = app
.execute_contract(
Addr::unchecked(random_addr),
contract_addr.clone(),
&msg,
&[],
)
.unwrap_err();
assert_eq!(
ContractError::Admin(AdminError::NotAdmin {}),
err.downcast().unwrap()
);
let msg = ExecuteMsg::ReleaseFunds {
funds: deposit_funds[0].clone(),
};
app.execute_contract(Addr::unchecked(multisig_addr), contract_addr, &msg, &[])
.unwrap();
let pool_bal = app.wrap().query_balance(pool_addr, TEST_MIX_DENOM).unwrap();
assert_eq!(pool_bal, deposit_funds[0]);
}
-69
View File
@@ -1,69 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Addr, Coin, Empty};
use cw_multi_test::{App, AppBuilder, Contract, ContractWrapper};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub const OWNER: &str = "admin0001";
pub const MEMBER1: &str = "member1";
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
pub const POOL_CONTRACT: &str = "mix pool contract address";
pub const RANDOM_ADDRESS: &str = "random address";
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {
pub coconut_bandwidth_address: String,
pub coconut_dkg_address: String,
}
pub fn mock_app(init_funds: &[Coin]) -> App {
AppBuilder::new().build(|router, _, storage| {
router
.bank
.init_balance(storage, &Addr::unchecked(OWNER), init_funds.to_vec())
.unwrap();
router
.bank
.init_balance(storage, &Addr::unchecked(MEMBER1), init_funds.to_vec())
.unwrap();
})
}
pub fn contract_dkg() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
nym_coconut_dkg::contract::execute,
nym_coconut_dkg::contract::instantiate,
nym_coconut_dkg::contract::query,
);
Box::new(contract)
}
pub fn contract_bandwidth() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
nym_coconut_bandwidth::contract::execute,
nym_coconut_bandwidth::contract::instantiate,
nym_coconut_bandwidth::contract::query,
);
Box::new(contract)
}
pub fn contract_multisig() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
cw3_flex_multisig::contract::execute,
cw3_flex_multisig::contract::instantiate,
cw3_flex_multisig::contract::query,
)
.with_migrate(cw3_flex_multisig::contract::migrate);
Box::new(contract)
}
pub fn contract_group() -> Box<dyn Contract<Empty>> {
let contract = ContractWrapper::new(
cw4_group::contract::execute,
cw4_group::contract::instantiate,
cw4_group::contract::query,
);
Box::new(contract)
}
@@ -1,165 +0,0 @@
use crate::helpers::*;
use cosmwasm_std::{coins, Addr, Coin, Decimal};
use cw_multi_test::Executor;
use cw_utils::{Duration, Threshold};
use nym_coconut_bandwidth::error::ContractError;
use nym_coconut_bandwidth_contract_common::{
msg::{
ExecuteMsg as CoconutBandwidthExecuteMsg, InstantiateMsg as CoconutBandwidthInstantiateMsg,
},
spend_credential::SpendCredentialData,
};
use nym_group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg;
use nym_multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg;
pub const TEST_COIN_DENOM: &str = "unym";
pub const TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS: &str =
"n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
pub const TEST_COCONUT_DKG_CONTRACT_ADDRESS: &str = "n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0";
#[test]
fn spend_credential_creates_proposal() {
let init_funds = coins(10, TEST_COIN_DENOM);
let mut app = mock_app(&init_funds);
let pool_addr = String::from(POOL_CONTRACT);
let group_code_id = app.store_code(contract_group());
let msg = GroupInstantiateMsg {
admin: Some(OWNER.to_string()),
members: vec![],
};
let group_contract_addr = app
.instantiate_contract(
group_code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"group",
None,
)
.unwrap();
let multisig_code_id = app.store_code(contract_multisig());
let msg = MultisigInstantiateMsg {
group_addr: group_contract_addr.into_string(),
threshold: Threshold::AbsolutePercentage {
percentage: Decimal::from_ratio(2u128, 3u128),
},
executor: None,
proposal_deposit: None,
max_voting_period: Duration::Height(1000),
coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(),
coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(),
};
let multisig_contract_addr = app
.instantiate_contract(
multisig_code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"multisig",
Some(OWNER.to_string()),
)
.unwrap();
let coconut_bandwidth_code_id = app.store_code(contract_bandwidth());
let msg = CoconutBandwidthInstantiateMsg {
multisig_addr: multisig_contract_addr.to_string(),
pool_addr,
mix_denom: TEST_COIN_DENOM.to_string(),
};
let coconut_bandwidth_contract_addr = app
.instantiate_contract(
coconut_bandwidth_code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"coconut bandwidth",
None,
)
.unwrap();
let msg = MigrateMsg {
coconut_bandwidth_address: coconut_bandwidth_contract_addr.to_string(),
coconut_dkg_address: "dkg-address".to_string(),
};
app.migrate_contract(
Addr::unchecked(OWNER),
multisig_contract_addr,
&msg,
multisig_code_id,
)
.unwrap();
let msg = CoconutBandwidthExecuteMsg::SpendCredential {
data: SpendCredentialData::new(
Coin::new(1, TEST_COIN_DENOM),
String::from("blinded_serial_number"),
String::from("gateway_cosmos_address"),
),
};
let res = app
.execute_contract(
Addr::unchecked(OWNER),
coconut_bandwidth_contract_addr.clone(),
&msg,
&[],
)
.unwrap();
let proposal_id = res
.events
.into_iter()
.find(|e| &e.ty == "wasm")
.unwrap()
.attributes
.into_iter()
.find(|attr| &attr.key == "proposal_id")
.unwrap()
.value
.parse::<u64>()
.unwrap();
assert_eq!(1, proposal_id);
// Trying with the same blinded serial number will detect the double spend attempt
let err = app
.execute_contract(
Addr::unchecked(OWNER),
coconut_bandwidth_contract_addr.clone(),
&msg,
&[],
)
.unwrap_err();
assert_eq!(
ContractError::DuplicateBlindedSerialNumber,
err.downcast().unwrap()
);
let msg = CoconutBandwidthExecuteMsg::SpendCredential {
data: SpendCredentialData::new(
Coin::new(1, TEST_COIN_DENOM),
String::from("blinded_serial_number2"),
String::from("gateway_cosmos_address"),
),
};
let res = app
.execute_contract(
Addr::unchecked(OWNER),
coconut_bandwidth_contract_addr,
&msg,
&[],
)
.unwrap();
let proposal_id = res
.events
.into_iter()
.find(|e| &e.ty == "wasm")
.unwrap()
.attributes
.into_iter()
.find(|attr| &attr.key == "proposal_id")
.unwrap()
.value
.parse::<u64>()
.unwrap();
assert_eq!(2, proposal_id);
}
@@ -1,224 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::helpers::{
contract_dkg, contract_group, contract_multisig, mock_app, MigrateMsg, MEMBER1, OWNER,
};
use crate::spend_credential_creates_proposal::{
TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS, TEST_COCONUT_DKG_CONTRACT_ADDRESS, TEST_COIN_DENOM,
};
use cosmwasm_std::{coins, Addr, Decimal};
use cw4::Member;
use cw_multi_test::Executor;
use cw_utils::{Duration, Threshold};
use nym_coconut_dkg_common::msg::ExecuteMsg::{
AdvanceEpochState, CommitVerificationKeyShare, InitiateDkg, RegisterDealer,
};
use nym_coconut_dkg_common::msg::InstantiateMsg as DkgInstantiateMsg;
use nym_coconut_dkg_common::msg::QueryMsg::GetVerificationKeys;
use nym_coconut_dkg_common::verification_key::PagedVKSharesResponse;
use nym_group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg;
use nym_multisig_contract_common::msg::ExecuteMsg::{Execute, Vote};
use nym_multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg;
#[test]
fn dkg_proposal() {
let init_funds = coins(10000000000, TEST_COIN_DENOM);
let mut app = mock_app(&init_funds);
let member1 = Member {
addr: MEMBER1.to_string(),
weight: 10,
};
let group_code_id = app.store_code(contract_group());
let msg = GroupInstantiateMsg {
admin: Some(OWNER.to_string()),
members: vec![member1],
};
let group_contract_addr = app
.instantiate_contract(
group_code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"group",
None,
)
.unwrap();
let multisig_code_id = app.store_code(contract_multisig());
let msg = MultisigInstantiateMsg {
group_addr: group_contract_addr.to_string(),
threshold: Threshold::AbsolutePercentage {
percentage: Decimal::from_ratio(1u128, 1u128),
},
executor: None,
proposal_deposit: None,
max_voting_period: Duration::Time(1000),
coconut_bandwidth_contract_address: TEST_COCONUT_BANDWIDTH_CONTRACT_ADDRESS.to_string(),
coconut_dkg_contract_address: TEST_COCONUT_DKG_CONTRACT_ADDRESS.to_string(),
};
let multisig_contract_addr = app
.instantiate_contract(
multisig_code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"multisig",
Some(OWNER.to_string()),
)
.unwrap();
let coconut_dkg_code_id = app.store_code(contract_dkg());
let msg = DkgInstantiateMsg {
group_addr: group_contract_addr.to_string(),
multisig_addr: multisig_contract_addr.to_string(),
time_configuration: None,
mix_denom: TEST_COIN_DENOM.to_string(),
key_size: 5,
};
let coconut_dkg_contract_addr = app
.instantiate_contract(
coconut_dkg_code_id,
Addr::unchecked(OWNER),
&msg,
&[],
"coconut dkg",
None,
)
.unwrap();
let msg = MigrateMsg {
coconut_bandwidth_address: coconut_dkg_contract_addr.to_string(),
coconut_dkg_address: coconut_dkg_contract_addr.to_string(),
};
app.migrate_contract(
Addr::unchecked(OWNER),
multisig_contract_addr.clone(),
&msg,
multisig_code_id,
)
.unwrap();
app.execute_contract(
Addr::unchecked(OWNER),
coconut_dkg_contract_addr.clone(),
&InitiateDkg {},
&[],
)
.unwrap();
app.execute_contract(
Addr::unchecked(MEMBER1),
coconut_dkg_contract_addr.clone(),
&RegisterDealer {
bte_key_with_proof: "bte_key_with_proof".to_string(),
identity_key: "identity".to_string(),
announce_address: "127.0.0.1:8000".to_string(),
resharing: false,
},
&[],
)
.unwrap();
for _ in 0..2 {
app.update_block(|block| block.time = block.time.plus_seconds(1000));
app.execute_contract(
Addr::unchecked(OWNER),
coconut_dkg_contract_addr.clone(),
&AdvanceEpochState {},
&[],
)
.unwrap();
}
// Proposal needs to be later then the member became part of the group
app.update_block(|block| block.height += 1);
let msg = CommitVerificationKeyShare {
share: "share".to_string(),
resharing: false,
};
let res = app
.execute_contract(
Addr::unchecked(MEMBER1),
coconut_dkg_contract_addr.clone(),
&msg,
&[],
)
.unwrap();
let proposal_id = res
.events
.into_iter()
.find(|e| &e.ty == "wasm")
.unwrap()
.attributes
.into_iter()
.find(|attr| &attr.key == "proposal_id")
.unwrap()
.value
.parse::<u64>()
.unwrap();
let mut res: PagedVKSharesResponse = app
.wrap()
.query_wasm_smart(
coconut_dkg_contract_addr.clone(),
&GetVerificationKeys {
epoch_id: 0,
limit: None,
start_after: None,
},
)
.unwrap();
let share = res.shares.pop().unwrap();
assert_eq!(share.share, "share".to_string());
assert_eq!(share.announce_address, "127.0.0.1:8000".to_string());
assert_eq!(share.node_index, 1);
assert_eq!(share.owner, Addr::unchecked(MEMBER1));
assert!(!share.verified);
app.execute_contract(
Addr::unchecked(MEMBER1),
multisig_contract_addr.clone(),
&Vote {
proposal_id,
vote: cw3::Vote::Yes,
},
&[],
)
.unwrap();
for _ in 0..2 {
app.update_block(|block| block.time = block.time.plus_seconds(1000));
app.execute_contract(
Addr::unchecked(OWNER),
coconut_dkg_contract_addr.clone(),
&AdvanceEpochState {},
&[],
)
.unwrap();
}
app.execute_contract(
Addr::unchecked(MEMBER1),
multisig_contract_addr,
&Execute { proposal_id },
&[],
)
.unwrap();
let res: PagedVKSharesResponse = app
.wrap()
.query_wasm_smart(
coconut_dkg_contract_addr,
&GetVerificationKeys {
epoch_id: 0,
limit: None,
start_after: None,
},
)
.unwrap();
assert!(res.shares[0].verified);
}
-433
View File
@@ -1,433 +0,0 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::helpers::{contract_bandwidth, contract_dkg, contract_group, contract_multisig};
use cosmwasm_std::{coins, Addr};
use cw3::{Cw3Contract, ProposalListResponse, Status, Vote};
use cw4::{Cw4Contract, Member};
use cw_multi_test::{App, AppBuilder, Executor};
use cw_utils::{Duration, Threshold};
use nym_coconut_bandwidth_contract_common::msg::InstantiateMsg as BandwidthInstantiateMsg;
use nym_coconut_dkg_common::dealing::{chunk_dealing, DealingChunkInfo, MAX_DEALING_CHUNK_SIZE};
use nym_coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg;
use nym_coconut_dkg_common::msg::InstantiateMsg as DkgInstantiateMsg;
use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
use nym_coconut_dkg_common::types::{Epoch, State};
use nym_group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg;
use nym_multisig_contract_common::msg::InstantiateMsg as MultisigInstantiateMsg;
use nym_multisig_contract_common::msg::MigrateMsg as MultisigMigrateMsg;
use rand_chacha::rand_core::{RngCore, SeedableRng};
use rand_chacha::ChaCha20Rng;
use subtle_encoding::bech32;
pub const PREFIX: &str = "n";
pub const TEST_DENOM: &str = "unym";
pub const BANDWIDTH_POOL: &str = "pool";
pub const BLOCK_TIME_SECS: u64 = 6;
pub fn test_rng() -> ChaCha20Rng {
let dummy_seed = [42u8; 32];
ChaCha20Rng::from_seed(dummy_seed)
}
pub fn random_address(rng: &mut ChaCha20Rng) -> Addr {
let mut bytes = [0u8; 32];
rng.fill_bytes(&mut bytes);
Addr::unchecked(bech32::encode(PREFIX, bytes))
}
#[allow(dead_code)]
pub struct TestSetup {
pub app: App,
pub rng: ChaCha20Rng,
pub global_admin: Addr,
pub multisig_contract: Cw3Contract,
pub group_contract: Cw4Contract,
pub dkg_contract: Addr,
pub bandwidth_contract: Addr,
}
#[allow(dead_code)]
impl TestSetup {
pub fn new() -> Self {
let mut rng = test_rng();
let global_admin = random_address(&mut rng);
let mut app = AppBuilder::new().build(|router, _, storage| {
router
.bank
.init_balance(storage, &global_admin, coins(1000000000000000, TEST_DENOM))
.unwrap();
});
let group_id = app.store_code(contract_group());
let multisig_id = app.store_code(contract_multisig());
let bandwidth_id = app.store_code(contract_bandwidth());
let dkg_id = app.store_code(contract_dkg());
// 1. init group contract
let group_contract = app
.instantiate_contract(
group_id,
global_admin.clone(),
&GroupInstantiateMsg {
admin: Some(global_admin.to_string()),
members: vec![],
},
&[],
"group contract",
Some(global_admin.to_string()),
)
.unwrap();
// 2. init multisig contract
let multisig_contract = app
.instantiate_contract(
multisig_id,
global_admin.clone(),
&MultisigInstantiateMsg {
group_addr: group_contract.to_string(),
coconut_bandwidth_contract_address: group_contract.to_string(),
coconut_dkg_contract_address: group_contract.to_string(),
threshold: Threshold::AbsolutePercentage {
percentage: "0.67".parse().unwrap(),
},
max_voting_period: Duration::Time(3600),
executor: None,
proposal_deposit: None,
},
&[],
"multisig contract",
Some(global_admin.to_string()),
)
.unwrap();
// 3. init bandwidth contract
let bandwidth_contract = app
.instantiate_contract(
bandwidth_id,
global_admin.clone(),
&BandwidthInstantiateMsg {
multisig_addr: multisig_contract.to_string(),
pool_addr: BANDWIDTH_POOL.to_string(),
mix_denom: TEST_DENOM.to_string(),
},
&[],
"bandwidth contract",
Some(global_admin.to_string()),
)
.unwrap();
// 4. init dkg contract
let dkg_contract = app
.instantiate_contract(
dkg_id,
global_admin.clone(),
&DkgInstantiateMsg {
group_addr: group_contract.to_string(),
multisig_addr: multisig_contract.to_string(),
time_configuration: None,
mix_denom: TEST_DENOM.to_string(),
key_size: 5,
},
&[],
"dkg contract",
Some(global_admin.to_string()),
)
.unwrap();
// 5.migrate multisig contract with addresses of bandwidth and dkg contracts
app.migrate_contract(
global_admin.clone(),
multisig_contract.clone(),
&MultisigMigrateMsg {
coconut_bandwidth_address: bandwidth_contract.to_string(),
coconut_dkg_address: dkg_contract.to_string(),
},
multisig_id,
)
.unwrap();
TestSetup {
app,
rng,
global_admin,
multisig_contract: Cw3Contract(multisig_contract),
group_contract: Cw4Contract(group_contract),
dkg_contract,
bandwidth_contract,
}
}
pub fn random_address(&mut self) -> Addr {
random_address(&mut self.rng)
}
pub fn next_block(&mut self) {
self.app.update_block(|block| {
block.height += 1;
block.time = block.time.plus_seconds(BLOCK_TIME_SECS);
})
}
// if we ever want to expand those tests, those queries should be moved to contract specific structs
// (kinda like what cw4 has)
pub fn dkg_state(&self) -> State {
self.app
.wrap()
.query_wasm_smart(&self.dkg_contract, &DkgQueryMsg::GetState {})
.unwrap()
}
pub fn epoch(&self) -> Epoch {
self.app
.wrap()
.query_wasm_smart(&self.dkg_contract, &DkgQueryMsg::GetCurrentEpochState {})
.unwrap()
}
// TODO: this will not go beyond first page
pub fn all_proposals(&self) -> ProposalListResponse {
self.app
.wrap()
.query_wasm_smart(
&self.multisig_contract.0,
&nym_multisig_contract_common::msg::QueryMsg::ListProposals {
start_after: None,
limit: None,
},
)
.unwrap()
}
pub fn admin(&self) -> Addr {
self.global_admin.clone()
}
pub fn remove_group_member(&mut self, addr: &Addr) {
self.app
.execute_contract(
self.admin(),
self.group_contract.addr(),
&nym_group_contract_common::msg::ExecuteMsg::UpdateMembers {
remove: vec![addr.to_string()],
add: vec![],
},
&[],
)
.unwrap();
}
pub fn add_mock_group_member(&mut self, weight: Option<u64>) -> Addr {
let member_addr = self.random_address();
let weight = weight.unwrap_or(10);
self.app
.execute_contract(
self.admin(),
self.group_contract.addr(),
&nym_group_contract_common::msg::ExecuteMsg::UpdateMembers {
remove: vec![],
add: vec![Member {
addr: member_addr.to_string(),
weight,
}],
},
&[],
)
.unwrap();
member_addr
}
pub fn begin_dkg(&mut self) {
self.app
.execute_contract(
self.admin(),
self.dkg_contract.clone(),
&DkgExecuteMsg::InitiateDkg {},
&[],
)
.unwrap();
}
pub fn skip_to_dkg_state_end(&mut self) {
let epoch = self.epoch();
self.app.update_block(|block| {
block.height += 42;
if let Some(finish_timestamp) = epoch.deadline {
block.time = finish_timestamp.plus_seconds(BLOCK_TIME_SECS);
} else {
block.time = block.time.plus_seconds(BLOCK_TIME_SECS)
}
});
}
pub fn advance_dkg_epoch(&mut self) {
self.skip_to_dkg_state_end();
self.unchecked_advance_dkg_epoch();
}
pub fn unchecked_advance_dkg_epoch(&mut self) {
self.app
.execute_contract(
self.admin(),
self.dkg_contract.clone(),
&DkgExecuteMsg::AdvanceEpochState {},
&[],
)
.unwrap();
}
pub fn submit_dummy_dkg_keys(&mut self, member: &Addr, resharing: bool) {
let mut bte_key_with_proof = [0u8; 32];
self.rng.fill_bytes(&mut bte_key_with_proof);
let bte_key_with_proof = bs58::encode(&bte_key_with_proof).into_string();
let mut identity_key = [0u8; 32];
self.rng.fill_bytes(&mut identity_key);
let identity_key = bs58::encode(&identity_key).into_string();
let mut announce_address = [0u8; 16];
self.rng.fill_bytes(&mut announce_address);
let announce_address = bs58::encode(&announce_address).into_string();
self.app
.execute_contract(
member.clone(),
self.dkg_contract.clone(),
&DkgExecuteMsg::RegisterDealer {
bte_key_with_proof,
identity_key,
announce_address,
resharing,
},
&[],
)
.unwrap();
}
pub fn submit_dummy_dealings(&mut self, member: &Addr, resharing: bool) {
let dealings = self.dkg_state().key_size;
for dealing_index in 0..dealings {
let mut dealing_bytes = vec![0u8; 5000];
self.rng.fill_bytes(&mut dealing_bytes);
let chunks = DealingChunkInfo::construct(dealing_bytes.len(), MAX_DEALING_CHUNK_SIZE);
self.app
.execute_contract(
member.clone(),
self.dkg_contract.clone(),
&DkgExecuteMsg::CommitDealingsMetadata {
dealing_index,
chunks,
resharing,
},
&[],
)
.unwrap();
self.next_block();
let chunks = chunk_dealing(dealing_index, dealing_bytes, MAX_DEALING_CHUNK_SIZE);
for (_, chunk) in chunks {
self.app
.execute_contract(
member.clone(),
self.dkg_contract.clone(),
&DkgExecuteMsg::CommitDealingsChunk { chunk },
&[],
)
.unwrap();
self.next_block();
}
}
}
pub fn submit_dummy_vk_key(&mut self, member: &Addr, resharing: bool) {
let mut derived_vk = vec![0u8; 256];
self.rng.fill_bytes(&mut derived_vk);
let share = bs58::encode(&derived_vk).into_string();
self.app
.execute_contract(
member.clone(),
self.dkg_contract.clone(),
&DkgExecuteMsg::CommitVerificationKeyShare { share, resharing },
&[],
)
.unwrap();
}
pub fn validate_dummy_keys(&mut self, member: &Addr) {
for proposal in self.all_proposals().proposals {
if proposal.status == Status::Open {
self.app
.execute_contract(
member.clone(),
self.multisig_contract.addr(),
&nym_multisig_contract_common::msg::ExecuteMsg::Vote {
proposal_id: proposal.id,
vote: Vote::Yes,
},
&[],
)
.unwrap();
}
}
}
pub fn finalize_dummy_dkg(&mut self) {
for proposal in self.all_proposals().proposals {
assert_eq!(proposal.status, Status::Passed);
self.app
.execute_contract(
self.admin(),
self.multisig_contract.addr(),
&nym_multisig_contract_common::msg::ExecuteMsg::Execute {
proposal_id: proposal.id,
},
&[],
)
.unwrap();
}
}
pub fn full_dummy_dkg(&mut self, members: Vec<Addr>, resharing: bool) {
for member in &members {
self.submit_dummy_dkg_keys(member, resharing);
self.next_block();
}
self.advance_dkg_epoch();
for member in &members {
self.submit_dummy_dealings(member, resharing);
self.next_block();
}
self.advance_dkg_epoch();
for member in &members {
self.submit_dummy_vk_key(member, resharing);
self.next_block();
}
self.advance_dkg_epoch();
for member in &members {
self.validate_dummy_keys(member);
self.next_block();
}
self.advance_dkg_epoch();
self.finalize_dummy_dkg();
self.next_block();
self.advance_dkg_epoch();
}
}
-8
View File
@@ -1,8 +0,0 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod deposit_and_release;
mod helpers;
mod spend_credential_creates_proposal;
mod submit_vk_creates_proposal;
mod test_wrapper;
+2 -1
View File
@@ -23,7 +23,7 @@ sylvia = { workspace = true }
cw-storage-plus = { workspace = true }
thiserror = { workspace = true }
cw-controllers = { workspace = true }
cosmwasm-storage = { workspace = true }
cw2 = { workspace = true }
cw3 = { workspace = true }
cw4 = { workspace = true }
@@ -40,6 +40,7 @@ anyhow = { workspace = true }
sylvia = { workspace = true, features = ["mt"] }
nym-crypto = { path = "../../common/crypto", features = ["rand", "asymmetric"] }
rand_chacha = "0.3"
cw-multi-test = { workspace = true }
[features]
schema-gen = ["nym-ecash-contract-common/schema"]
+5 -2
View File
@@ -41,7 +41,8 @@
"denom": {
"type": "string"
}
}
},
"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); ```",
@@ -230,7 +231,8 @@
"denom": {
"type": "string"
}
}
},
"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); ```",
@@ -658,6 +660,7 @@
"type": "string"
}
},
"additionalProperties": false,
"definitions": {
"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); ```",
+2 -1
View File
@@ -179,7 +179,8 @@
"denom": {
"type": "string"
}
}
},
"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); ```",
+2 -1
View File
@@ -37,7 +37,8 @@
"denom": {
"type": "string"
}
}
},
"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); ```",
@@ -14,6 +14,7 @@
"type": "string"
}
},
"additionalProperties": false,
"definitions": {
"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); ```",
+5 -5
View File
@@ -6,19 +6,19 @@ use crate::helpers::{
create_batch_redemption_proposal, create_blacklist_proposal, Config, ProposalId,
};
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{to_binary, Addr, Coin, Decimal, Deps, Storage, SubMsg, Uint128};
use cosmwasm_std::{to_json_binary, Addr, Coin, Decimal, Deps, Storage, SubMsg, Uint128};
use cw3::ProposalResponse;
use nym_ecash_contract_common::EcashContractError;
use nym_multisig_contract_common::msg::QueryMsg as MultisigQueryMsg;
use nym_network_defaults::TICKETBOOK_SIZE;
use sylvia::types::ExecCtx;
use sylvia::ctx::ExecCtx;
#[cw_serde]
pub(crate) struct Invariants {
pub(crate) ticket_book_size: u64,
}
impl NymEcashContract<'_> {
impl NymEcashContract {
pub(crate) fn get_ticketbook_size(
&self,
storage: &dyn Storage,
@@ -47,7 +47,7 @@ impl NymEcashContract<'_> {
let book_ratio = Decimal::from_ratio(tickets, ticketbook_size);
// return = ticketbook_price * (tickets / ticketbook_size)
let return_amount = book_ratio * deposit_amount;
let return_amount = deposit_amount.mul_floor(book_ratio);
Ok(Coin {
denom: config.deposit_amount.denom.clone(),
@@ -111,7 +111,7 @@ impl NymEcashContract<'_> {
let proposal_response: ProposalResponse = deps.querier.query(
&cosmwasm_std::QueryRequest::Wasm(cosmwasm_std::WasmQuery::Smart {
contract_addr: multisig_addr.to_string(),
msg: to_binary(&msg)?,
msg: to_json_binary(&msg)?,
}),
)?;
Ok(proposal_response)
+37 -30
View File
@@ -25,7 +25,7 @@ use nym_ecash_contract_common::events::{
};
use nym_ecash_contract_common::EcashContractError;
use nym_network_defaults::TICKETBOOK_SIZE;
use sylvia::types::{ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx, ReplyCtx};
use sylvia::ctx::{ExecCtx, InstantiateCtx, MigrateCtx, QueryCtx};
use sylvia::{contract, entry_points};
mod helpers;
@@ -34,22 +34,22 @@ mod queued_migrations;
#[cfg(test)]
mod test;
pub struct NymEcashContract<'a> {
pub(crate) contract_admin: Admin<'a>,
pub(crate) multisig: Admin<'a>,
pub(crate) config: Item<'a, Config>,
pub(crate) pool_counters: Item<'a, PoolCounters>,
pub(crate) expected_invariants: Item<'a, Invariants>,
pub struct NymEcashContract {
pub(crate) contract_admin: Admin,
pub(crate) multisig: Admin,
pub(crate) config: Item<Config>,
pub(crate) pool_counters: Item<PoolCounters>,
pub(crate) expected_invariants: Item<Invariants>,
pub(crate) blacklist: Map<'a, BlacklistKey, Blacklisting>,
pub(crate) blacklist: Map<BlacklistKey, Blacklisting>,
pub(crate) deposits: DepositStorage<'a>,
pub(crate) deposits: DepositStorage,
}
#[entry_points]
#[contract]
#[error(EcashContractError)]
impl NymEcashContract<'_> {
#[sv::error(EcashContractError)]
impl NymEcashContract {
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Self {
@@ -63,7 +63,7 @@ impl NymEcashContract<'_> {
}
}
#[msg(instantiate)]
#[sv::msg(instantiate)]
pub fn instantiate(
&self,
mut ctx: InstantiateCtx,
@@ -119,7 +119,7 @@ impl NymEcashContract<'_> {
/*==================
======QUERIES=======
==================*/
#[msg(query)]
#[sv::msg(query)]
pub fn get_blacklist_paged(
&self,
ctx: QueryCtx,
@@ -150,7 +150,7 @@ impl NymEcashContract<'_> {
))
}
#[msg(query)]
#[sv::msg(query)]
pub fn get_blacklisted_account(
&self,
ctx: QueryCtx,
@@ -160,14 +160,14 @@ impl NymEcashContract<'_> {
Ok(BlacklistedAccountResponse::new(account))
}
#[msg(query)]
#[sv::msg(query)]
pub fn get_required_deposit_amount(&self, ctx: QueryCtx) -> StdResult<Coin> {
let deposit_amount = self.config.load(ctx.deps.storage)?.deposit_amount;
Ok(deposit_amount)
}
#[msg(query)]
#[sv::msg(query)]
pub fn get_deposit(
&self,
ctx: QueryCtx,
@@ -179,7 +179,7 @@ impl NymEcashContract<'_> {
})
}
#[msg(query)]
#[sv::msg(query)]
pub fn get_latest_deposit(
&self,
ctx: QueryCtx,
@@ -198,7 +198,7 @@ impl NymEcashContract<'_> {
})
}
#[msg(query)]
#[sv::msg(query)]
pub fn get_deposits_paged(
&self,
ctx: QueryCtx,
@@ -230,7 +230,7 @@ impl NymEcashContract<'_> {
======EXECUTIONS=======
=====================*/
#[msg(exec)]
#[sv::msg(exec)]
pub fn deposit_ticket_book_funds(
&self,
ctx: ExecCtx,
@@ -266,7 +266,7 @@ impl NymEcashContract<'_> {
.set_data(deposit_id.to_be_bytes()))
}
#[msg(exec)]
#[sv::msg(exec)]
pub fn request_redemption(
&self,
ctx: ExecCtx,
@@ -285,7 +285,7 @@ impl NymEcashContract<'_> {
Ok(Response::new().add_submessage(msg))
}
#[msg(exec)]
#[sv::msg(exec)]
pub fn redeem_tickets(
&self,
ctx: ExecCtx,
@@ -319,7 +319,7 @@ impl NymEcashContract<'_> {
}))
}
#[msg(exec)]
#[sv::msg(exec)]
pub fn update_admin(
&self,
ctx: ExecCtx,
@@ -333,7 +333,7 @@ impl NymEcashContract<'_> {
.execute_update_admin(ctx.deps, ctx.info, Some(new_admin))?)
}
#[msg(exec)]
#[sv::msg(exec)]
pub fn update_deposit_value(
&self,
ctx: ExecCtx,
@@ -352,7 +352,7 @@ impl NymEcashContract<'_> {
Ok(Response::new().add_attribute("updated_deposit", deposit_str))
}
#[msg(exec)]
#[sv::msg(exec)]
pub fn propose_to_blacklist(
&self,
ctx: ExecCtx,
@@ -381,7 +381,7 @@ impl NymEcashContract<'_> {
// }
}
#[msg(exec)]
#[sv::msg(exec)]
pub fn add_to_blacklist(
&self,
ctx: ExecCtx,
@@ -405,8 +405,13 @@ impl NymEcashContract<'_> {
/*=====================
=========REPLY=========
=====================*/
#[msg(reply)]
pub fn reply(&self, ctx: ReplyCtx, msg: Reply) -> Result<Response, EcashContractError> {
#[sv::msg(reply)]
#[allow(deprecated)]
pub fn reply(
&self,
ctx: sylvia::types::ReplyCtx,
msg: Reply,
) -> Result<Response, EcashContractError> {
match msg.id {
n if n == BLACKLIST_PROPOSAL_REPLY_ID => self.handle_blacklist_proposal_reply(ctx, msg),
n if n == REDEMPTION_PROPOSAL_REPLY_ID => {
@@ -416,9 +421,10 @@ impl NymEcashContract<'_> {
}
}
#[allow(deprecated)]
fn handle_blacklist_proposal_reply(
&self,
ctx: ReplyCtx,
ctx: sylvia::types::ReplyCtx,
msg: Reply,
) -> Result<Response, EcashContractError> {
let proposal_id = msg.multisig_proposal_id()?;
@@ -435,9 +441,10 @@ impl NymEcashContract<'_> {
Ok(Response::new().add_attribute(PROPOSAL_ID_ATTRIBUTE_NAME, proposal_id.to_string()))
}
#[allow(deprecated)]
fn handle_redemption_proposal_reply(
&self,
_ctx: ReplyCtx,
_ctx: sylvia::types::ReplyCtx,
msg: Reply,
) -> Result<Response, EcashContractError> {
let proposal_id = msg.multisig_proposal_id()?;
@@ -451,7 +458,7 @@ impl NymEcashContract<'_> {
/*=====================
=======MIGRATION=======
=====================*/
#[msg(migrate)]
#[sv::msg(migrate)]
pub fn migrate(&self, ctx: MigrateCtx) -> Result<Response, EcashContractError> {
set_build_information!(ctx.deps.storage)?;
cw2::ensure_from_older_version(ctx.deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
+9 -8
View File
@@ -2,15 +2,15 @@
// SPDX-License-Identifier: Apache-2.0
use crate::contract::NymEcashContract;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env, MockApi, MockQuerier};
use cosmwasm_std::{coin, Addr, Empty, Env, MemoryStorage, OwnedDeps};
use sylvia::types::{InstantiateCtx, QueryCtx};
use sylvia::ctx::{InstantiateCtx, QueryCtx};
pub const TEST_DENOM: &str = "unym";
#[allow(dead_code)]
pub struct TestSetup {
pub contract: NymEcashContract<'static>,
pub contract: NymEcashContract,
pub deps: OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>>,
pub env: Env,
@@ -23,12 +23,13 @@ impl TestSetup {
pub fn init() -> TestSetup {
let mut deps = mock_dependencies();
let env = mock_env();
let admin = mock_info("admin", &[]);
let init_ctx = InstantiateCtx::from((deps.as_mut(), env.clone(), admin));
let multisig_contract = "multisig";
let group_contract = "group";
let holding = "holding";
let admin = message_info(&deps.api.addr_make("admin"), &[]);
let multisig_contract = deps.api.addr_make("multisig");
let group_contract = deps.api.addr_make("group");
let holding = deps.api.addr_make("holding");
let init_ctx = InstantiateCtx::from((deps.as_mut(), env.clone(), admin));
let contract = NymEcashContract::new();
contract
+18 -15
View File
@@ -2,16 +2,17 @@
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{Order, StdResult, Storage};
use cw_storage_plus::{Bound, Item, Key, Path, Prefix, PrimaryKey};
use cw_storage_plus::{range_with_prefix, Bound, Item, Key, Path, Prefix, PrimaryKey};
use nym_ecash_contract_common::deposit::DepositId;
use nym_ecash_contract_common::{deposit::Deposit, EcashContractError};
use std::ops::Deref;
pub(crate) struct DepositStorage<'a> {
pub(crate) deposit_id_counter: Item<'a, DepositId>,
pub(crate) struct DepositStorage {
pub(crate) deposit_id_counter: Item<DepositId>,
pub(crate) deposits: StoredDeposits,
}
impl<'a> DepositStorage<'a> {
impl DepositStorage {
pub const fn new() -> Self {
DepositStorage {
deposit_id_counter: Item::new("deposit_ids"),
@@ -65,14 +66,23 @@ impl<'a> DepositStorage<'a> {
Ok(Some(Deposit::try_from_bytes(&deposit_bytes)?))
}
pub fn range(
pub fn range<'a>(
&'a self,
store: &'a dyn Storage,
min: Option<Bound<'a, DepositId>>,
max: Option<Bound<'a, DepositId>>,
order: Order,
) -> impl Iterator<Item = StdResult<(DepositId, Deposit)>> + 'a {
self.deposits.no_prefix().range(store, min, max, order)
let prefix = self.deposits.no_prefix();
let mapped = range_with_prefix(
store,
prefix.deref(),
min.map(|b| b.to_raw_bound()),
max.map(|b| b.to_raw_bound()),
order,
)
.map(StoredDeposits::deserialize_deposit_record);
Box::new(mapped)
}
}
@@ -89,15 +99,8 @@ impl StoredDeposits {
Ok((id, Deposit::try_from_bytes(&deposit_bytes)?))
}
fn no_prefix(&self) -> Prefix<DepositId, Deposit, DepositId> {
cw_storage_plus::Prefix::with_deserialization_functions(
Self::NAMESPACE,
&[],
&[],
// explicitly panic to make sure we're never attempting to call an unexpected deserializer on our data
|_, _, kv| Self::deserialize_deposit_record(kv),
|_, _, _| panic!("attempted to call custom de_fn_v"),
)
fn no_prefix(&self) -> Prefix<DepositId, Deposit> {
Prefix::new(Self::NAMESPACE, &[])
}
fn storage_key(deposit_id: u32) -> Path<Vec<u8>> {
+6 -5
View File
@@ -3,7 +3,8 @@
use crate::constants::{BLACKLIST_PROPOSAL_REPLY_ID, REDEMPTION_PROPOSAL_REPLY_ID};
use cosmwasm_std::{
to_binary, Addr, Coin, CosmosMsg, Reply, StdError, StdResult, SubMsg, SubMsgResult, WasmMsg,
to_json_binary, Addr, Coin, CosmosMsg, Reply, StdError, StdResult, SubMsg, SubMsgResult,
WasmMsg,
};
use cw4::Cw4Contract;
use nym_contracts_common::events::try_find_attribute;
@@ -46,7 +47,7 @@ pub(crate) fn create_batch_redemption_proposal(
let release_funds_req = ExecuteMsg::RedeemTickets { n, gw };
let release_funds_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: ecash_bandwidth_address,
msg: to_binary(&release_funds_req)?,
msg: to_json_binary(&release_funds_req)?,
funds: vec![],
});
let req = MultisigExecuteMsg::Propose {
@@ -57,7 +58,7 @@ pub(crate) fn create_batch_redemption_proposal(
};
let msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: multisig_addr,
msg: to_binary(&req)?,
msg: to_json_binary(&req)?,
funds: vec![],
});
@@ -76,7 +77,7 @@ pub(crate) fn create_blacklist_proposal(
};
let blacklist_req_msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: ecash_bandwidth_address,
msg: to_binary(&blacklist_req)?,
msg: to_json_binary(&blacklist_req)?,
funds: vec![],
});
let req = MultisigExecuteMsg::Propose {
@@ -87,7 +88,7 @@ pub(crate) fn create_blacklist_proposal(
};
let msg = CosmosMsg::Wasm(WasmMsg::Execute {
contract_addr: multisig_addr,
msg: to_binary(&req)?,
msg: to_json_binary(&req)?,
funds: vec![],
});
+17 -15
View File
@@ -1,16 +1,18 @@
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{coin, Addr, Coin};
use cosmwasm_std::testing::MockApi;
use cosmwasm_std::{coin, Coin};
use cw_multi_test::IntoBech32;
use cw_utils::PaymentError;
use nym_ecash_contract_common::EcashContractError;
use sylvia::{cw_multi_test::App as MtApp, multitest::App};
use crate::contract::multitest_utils::CodeId;
use crate::contract::sv::mt::{CodeId, NymEcashContractProxy};
#[test]
fn invalid_deposit() {
let owner = "owner";
let owner = "owner".into_bech32();
let denom = "unym";
let mtapp = MtApp::new(|router, _, storage| {
@@ -18,10 +20,10 @@ fn invalid_deposit() {
.bank
.init_balance(
storage,
&Addr::unchecked(owner),
&owner,
vec![
Coin::new(10000000, denom),
Coin::new(10000000, "some_denom"),
Coin::new(10000000u32, denom),
Coin::new(10000000u32, "some_denom"),
],
)
.unwrap()
@@ -32,12 +34,12 @@ fn invalid_deposit() {
let contract = code_id
.instantiate(
"holding_acount".to_string(),
"multisig_addr".to_string(),
"group_addr".to_string(),
MockApi::default().addr_make("holding_acount").to_string(),
MockApi::default().addr_make("multisig_addr").to_string(),
MockApi::default().addr_make("group_addr").to_string(),
coin(75000000, denom),
)
.call(owner)
.call(&owner)
.unwrap();
let verification_key = "Verification key";
@@ -45,19 +47,19 @@ fn invalid_deposit() {
assert_eq!(
contract
.deposit_ticket_book_funds(verification_key.to_string(),)
.call(owner)
.call(&owner)
.unwrap_err(),
EcashContractError::InvalidDeposit(PaymentError::NoFunds {})
);
let coin = Coin::new(1000000, denom.to_string());
let second_coin = Coin::new(1000000, "some_denom");
let coin = Coin::new(1000000u32, denom.to_string());
let second_coin = Coin::new(1000000u32, "some_denom");
assert_eq!(
contract
.deposit_ticket_book_funds(verification_key.to_string(),)
.with_funds(&[coin, second_coin.clone()])
.call(owner)
.call(&owner)
.unwrap_err(),
EcashContractError::InvalidDeposit(PaymentError::MultipleDenoms {})
);
@@ -66,7 +68,7 @@ fn invalid_deposit() {
contract
.deposit_ticket_book_funds(verification_key.to_string(),)
.with_funds(&[second_coin])
.call(owner)
.call(&owner)
.unwrap_err(),
EcashContractError::InvalidDeposit(PaymentError::MissingDenom(denom.to_string()))
);
+14 -23
View File
@@ -3,18 +3,18 @@
use crate::contract::NymEcashContract;
use crate::helpers::Config;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
use cosmwasm_std::testing::{message_info, mock_dependencies, mock_env, MockApi, MockQuerier};
use cosmwasm_std::{coin, Addr, Deps, Empty, Env, MemoryStorage, MessageInfo, OwnedDeps};
use rand_chacha::rand_core::SeedableRng;
use rand_chacha::ChaCha20Rng;
use sylvia::types::{ExecCtx, InstantiateCtx, QueryCtx};
use sylvia::ctx::{ExecCtx, InstantiateCtx, QueryCtx};
pub fn test_rng() -> ChaCha20Rng {
let dummy_seed = [42u8; 32];
ChaCha20Rng::from_seed(dummy_seed)
}
const CONTRACT: NymEcashContract<'static> = NymEcashContract::new();
const CONTRACT: NymEcashContract = NymEcashContract::new();
const DENOM: &str = "unym";
@@ -33,18 +33,15 @@ impl TestSetupSimple {
pub fn new() -> Self {
let mut deps = mock_dependencies();
let env = mock_env();
let owner = Addr::unchecked("owner");
let init_ctx = InstantiateCtx {
deps: deps.as_mut(),
env: env.clone(),
info: mock_info(owner.as_str(), &[]),
};
let owner = deps.api.addr_make("owner");
let rng = test_rng();
let holding_account = Addr::unchecked("holding_account");
let multisig_contract = Addr::unchecked("multisig_contract");
let group_contract = Addr::unchecked("group_contract");
let holding_account = deps.api.addr_make("holding_account");
let multisig_contract = deps.api.addr_make("multisig_contract");
let group_contract = deps.api.addr_make("group_contract");
let init_ctx =
InstantiateCtx::from((deps.as_mut(), env.clone(), message_info(&owner, &[])));
CONTRACT
.instantiate(
@@ -73,23 +70,17 @@ impl TestSetupSimple {
.get(self.deps.as_ref())
.unwrap()
.unwrap();
mock_info(admin.as_str(), &[])
message_info(&admin, &[])
}
pub fn execute_ctx(&mut self, sender: MessageInfo) -> ExecCtx {
ExecCtx {
env: self.env.clone(),
deps: self.deps.as_mut(),
info: sender,
}
let env = self.env.clone();
ExecCtx::from((self.deps.as_mut(), env, sender))
}
#[allow(dead_code)]
pub fn query_ctx(&self) -> QueryCtx {
QueryCtx {
env: self.env.clone(),
deps: self.deps.as_ref(),
}
QueryCtx::from((self.deps.as_ref(), self.env.clone()))
}
pub fn contract(&self) -> NymEcashContract {
@@ -1,7 +1,10 @@
[package]
name = "mixnet-vesting-integration-tests"
version = "0.1.0"
edition = "2021"
edition = { workspace = true }
authors = { workspace = true }
license = { workspace = true }
repository = { workspace = true }
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -10,7 +13,6 @@ publish = false
# cosmwasm dependencies
cosmwasm-std = { workspace = true }
cosmwasm-storage = { workspace = true }
cw-multi-test = { workspace = true }
# contracts dependencies
+2 -1
View File
@@ -32,7 +32,7 @@ nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts
cosmwasm-schema = { workspace = true, optional = true }
cosmwasm-std = { workspace = true }
cosmwasm-storage = { workspace = true }
cosmwasm-derive = { workspace = true }
cw-controllers = { workspace = true }
cw2 = { workspace = true }
@@ -49,6 +49,7 @@ anyhow.workspace = true
rand_chacha = "0.3"
rand = "0.8.5"
nym-crypto = { path = "../../common/crypto", features = ["asymmetric", "rand"] }
easy-addr = { path = "../../common/cosmwasm-smart-contracts/easy_addr" }
[features]
default = []
@@ -109,7 +109,8 @@
"format": "uint64",
"minimum": 0.0
}
}
},
"additionalProperties": false
},
"InitialRewardingParams": {
"type": "object",
@@ -1303,7 +1304,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"ConfigScoreParamsUpdate": {
"type": "object",
@@ -3558,7 +3560,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -3675,7 +3678,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -3794,7 +3798,8 @@
}
]
}
}
},
"additionalProperties": false
},
"MixNodeDetails": {
"description": "Full details associated with given mixnode.",
@@ -3948,7 +3953,8 @@
"format": "uint32",
"minimum": 0.0
}
}
},
"additionalProperties": false
},
"Percent": {
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
@@ -4093,7 +4099,8 @@
"format": "uint64",
"minimum": 0.0
}
}
},
"additionalProperties": false
},
"Interval": {
"type": "object",
@@ -4132,7 +4139,8 @@
"format": "uint32",
"minimum": 0.0
}
}
},
"additionalProperties": false
}
}
},
@@ -4290,7 +4298,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -4418,7 +4427,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -4687,7 +4697,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -4776,7 +4787,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -4832,7 +4844,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Gateway": {
"description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.",
@@ -4992,7 +5005,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Gateway": {
"description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.",
@@ -5154,7 +5168,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"MixNode": {
"description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.",
@@ -5269,7 +5284,8 @@
}
]
}
}
},
"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); ```",
@@ -5329,7 +5345,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -5448,7 +5465,8 @@
}
]
}
}
},
"additionalProperties": false
},
"MixNodeDetails": {
"description": "Full details associated with given mixnode.",
@@ -5602,7 +5620,8 @@
"format": "uint32",
"minimum": 0.0
}
}
},
"additionalProperties": false
},
"Percent": {
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
@@ -5664,7 +5683,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -5783,7 +5803,8 @@
}
]
}
}
},
"additionalProperties": false
},
"MixNodeDetails": {
"description": "Full details associated with given mixnode.",
@@ -5937,7 +5958,8 @@
"format": "uint32",
"minimum": 0.0
}
}
},
"additionalProperties": false
},
"Percent": {
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
@@ -5995,7 +6017,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -6151,7 +6174,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -6266,7 +6290,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -6494,7 +6519,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"NymNode": {
"description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.",
@@ -6630,7 +6656,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -6927,7 +6954,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -7337,7 +7365,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -7638,7 +7667,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Gateway": {
"description": "Information provided by the node operator during bonding that are used to allow other entities to use the services of this node.",
@@ -7796,7 +7826,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -7915,7 +7946,8 @@
}
]
}
}
},
"additionalProperties": false
},
"MixNodeDetails": {
"description": "Full details associated with given mixnode.",
@@ -8069,7 +8101,8 @@
"format": "uint32",
"minimum": 0.0
}
}
},
"additionalProperties": false
},
"Percent": {
"description": "Percent represents a value between 0 and 100% (i.e. between 0.0 and 1.0)",
@@ -8133,7 +8166,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -8453,7 +8487,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -8538,7 +8573,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"PendingEpochEventData": {
"description": "Details of a particular pending epoch event.",
@@ -8964,7 +9000,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"PendingEpochEvent": {
"description": "A request made at some point in the current epoch that's going to get resolved once the epoch rolls over.",
@@ -9373,7 +9410,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -9744,7 +9782,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -10160,7 +10199,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -10239,7 +10279,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"Decimal": {
"description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)",
@@ -10723,7 +10764,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"ConfigScoreParams": {
"type": "object",
@@ -10990,7 +11032,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"ConfigScoreParams": {
"type": "object",
+2 -1
View File
@@ -1022,7 +1022,8 @@
"denom": {
"type": "string"
}
}
},
"additionalProperties": false
},
"ConfigScoreParamsUpdate": {
"type": "object",

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