Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f9ac1286b | |||
| 961775417f | |||
| 044d94ad02 | |||
| 35d7525d1e | |||
| b393405db8 | |||
| 6974f3d785 | |||
| 85d96deded | |||
| 4fe7ce0f12 | |||
| 5886361dc5 | |||
| 40c26b1326 | |||
| 46a482cfcb | |||
| 761b6c2cac | |||
| 0afdd7bc82 | |||
| 7b9fe3dc09 | |||
| d734174f6e | |||
| 6187d94b68 | |||
| a7dca2f07c | |||
| 2ed4449e0b | |||
| 605176551b | |||
| ac3830e677 | |||
| c00e4655f4 | |||
| 8fa84a28e6 | |||
| 94d3bf087a | |||
| 7a9b989db9 | |||
| 9f1b89616a | |||
| 89315f0c2a | |||
| 858fafb1a5 | |||
| 169f8f2c1c | |||
| 8782fd7bb8 | |||
| 146c3bd358 | |||
| e68ebdc2b8 | |||
| 397b03267a | |||
| 2a021b46ac | |||
| c43cb657c6 | |||
| 6f66b377e2 | |||
| 5ea67a9376 | |||
| f566dffc5b | |||
| 05f8beedad | |||
| 2fff051e28 | |||
| 44bd70c546 | |||
| 702354d127 | |||
| 56b1010d16 | |||
| 0632517f5d | |||
| bfcc5e9b41 | |||
| 337aacd442 | |||
| da5b7302b5 | |||
| 7a53e86b40 | |||
| 654dd07d19 | |||
| ef0765face | |||
| 3685b4681c | |||
| 47e2af2caa | |||
| 5be555d79f | |||
| 8cc2b3167e | |||
| 4d95955961 | |||
| e36ae4091f | |||
| b566147f2f | |||
| 12242bb3c6 | |||
| 0a0b0e80f4 | |||
| 3c3a34ec0f | |||
| e9b442e634 | |||
| ca02e2bce1 | |||
| 985ab43fe9 | |||
| f4dad37b14 | |||
| 08a57fa8df | |||
| 98e88e2f11 | |||
| 2a589b049c | |||
| 9bcd56e254 | |||
| 025ba2ec5f | |||
| 1a1d11c447 | |||
| 958bc2ae9a | |||
| cfb9f3d356 | |||
| ea386b6145 | |||
| d0fa1792e2 | |||
| 9b2d224e54 | |||
| 3f504d7500 | |||
| 67701290d3 | |||
| 22541f5a79 | |||
| bd8f666405 | |||
| a3c1541660 | |||
| 6c1d14a4bc | |||
| defd148d73 | |||
| 162ff71814 | |||
| 5c864cb055 | |||
| cfc13671a4 | |||
| 668a255e0d | |||
| 31d8352621 | |||
| 4f6fe88b4c | |||
| 397ef8723d | |||
| 2c2223947c | |||
| e1c0638f1e | |||
| 13fa2119fc | |||
| 8f24e8f208 | |||
| 37dd20ded1 | |||
| b4b32bb907 | |||
| c1718154cb | |||
| 3126053cbe | |||
| 54266fd5df |
Generated
+154
-37
@@ -1649,6 +1649,16 @@ dependencies = [
|
||||
"tendermint-proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmos-sdk-proto"
|
||||
version = "0.20.0"
|
||||
source = "git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features#41ed4631e146268b0300033c8bbb993d79a49f58"
|
||||
dependencies = [
|
||||
"prost 0.12.1",
|
||||
"prost-types 0.12.1",
|
||||
"tendermint-proto",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmrs"
|
||||
version = "0.15.0"
|
||||
@@ -1656,7 +1666,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47126f5364df9387b9d8559dcef62e99010e1d4098f39eb3f7ee4b5c254e40ea"
|
||||
dependencies = [
|
||||
"bip32",
|
||||
"cosmos-sdk-proto",
|
||||
"cosmos-sdk-proto 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ecdsa 0.16.8",
|
||||
"eyre",
|
||||
"k256",
|
||||
"rand_core 0.6.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"signature 2.1.0",
|
||||
"subtle-encoding",
|
||||
"tendermint",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cosmrs"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features#41ed4631e146268b0300033c8bbb993d79a49f58"
|
||||
dependencies = [
|
||||
"bip32",
|
||||
"cosmos-sdk-proto 0.20.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
|
||||
"ecdsa 0.16.8",
|
||||
"eyre",
|
||||
"k256",
|
||||
@@ -2212,6 +2241,16 @@ dependencies = [
|
||||
"darling_macro 0.14.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
|
||||
dependencies = [
|
||||
"darling_core 0.20.3",
|
||||
"darling_macro 0.20.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.13.4"
|
||||
@@ -2240,6 +2279,20 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.13.4"
|
||||
@@ -2262,6 +2315,17 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||
dependencies = [
|
||||
"darling_core 0.20.3",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.5.3"
|
||||
@@ -2771,26 +2835,6 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-iterator"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45a0ac4aeb3a18f92eaf09c6bb9b3ac30ff61ca95514fc58cbead1c9a6bf5401"
|
||||
dependencies = [
|
||||
"enum-iterator-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-iterator-derive"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
@@ -5870,6 +5914,15 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-api"
|
||||
version = "1.1.34"
|
||||
@@ -5930,6 +5983,8 @@ dependencies = [
|
||||
"pin-project",
|
||||
"rand 0.7.3",
|
||||
"rand 0.8.5",
|
||||
"rand_chacha 0.2.2",
|
||||
"rand_chacha 0.3.1",
|
||||
"reqwest",
|
||||
"rocket",
|
||||
"rocket_cors",
|
||||
@@ -5938,6 +5993,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha2 0.9.9",
|
||||
"sqlx",
|
||||
"tap",
|
||||
"tempfile",
|
||||
@@ -5956,7 +6012,7 @@ name = "nym-api-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bs58 0.4.0",
|
||||
"cosmrs",
|
||||
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
|
||||
"cosmwasm-std",
|
||||
"getset",
|
||||
"nym-coconut-interface",
|
||||
@@ -6014,7 +6070,7 @@ name = "nym-bity-integration"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cosmrs",
|
||||
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
|
||||
"eyre",
|
||||
"k256",
|
||||
"nym-cli-commands",
|
||||
@@ -6059,7 +6115,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"clap 4.4.7",
|
||||
"comfy-table",
|
||||
"cosmrs",
|
||||
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"handlebars",
|
||||
@@ -6256,6 +6312,8 @@ dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw4",
|
||||
"nym-contracts-common",
|
||||
"nym-multisig-contract-common",
|
||||
]
|
||||
@@ -6328,7 +6386,7 @@ name = "nym-credentials"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bls12_381",
|
||||
"cosmrs",
|
||||
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
|
||||
"log",
|
||||
"nym-api-requests",
|
||||
"nym-coconut-interface",
|
||||
@@ -6687,7 +6745,6 @@ dependencies = [
|
||||
"nym-types",
|
||||
"nym-validator-client",
|
||||
"opentelemetry",
|
||||
"pretty_env_logger",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -7416,7 +7473,7 @@ name = "nym-types"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"base64 0.21.4",
|
||||
"cosmrs",
|
||||
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
|
||||
"cosmwasm-std",
|
||||
"eyre",
|
||||
"hmac 0.12.1",
|
||||
@@ -7450,7 +7507,7 @@ dependencies = [
|
||||
"bip32",
|
||||
"bip39",
|
||||
"colored",
|
||||
"cosmrs",
|
||||
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
|
||||
"cosmwasm-std",
|
||||
"cw-controllers",
|
||||
"cw-utils",
|
||||
@@ -7492,6 +7549,40 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-validator-rewarder"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bip39",
|
||||
"clap 4.4.7",
|
||||
"cosmwasm-std",
|
||||
"futures",
|
||||
"humantime 2.1.0",
|
||||
"humantime-serde",
|
||||
"nym-bin-common",
|
||||
"nym-coconut",
|
||||
"nym-coconut-bandwidth-contract-common",
|
||||
"nym-coconut-dkg-common",
|
||||
"nym-config",
|
||||
"nym-credentials",
|
||||
"nym-crypto",
|
||||
"nym-network-defaults",
|
||||
"nym-task",
|
||||
"nym-validator-client",
|
||||
"nyxd-scraper",
|
||||
"serde",
|
||||
"serde_with",
|
||||
"sha2 0.10.8",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-vesting-contract-common"
|
||||
version = "0.7.0"
|
||||
@@ -7510,7 +7601,7 @@ dependencies = [
|
||||
name = "nym-wallet-types"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"cosmrs",
|
||||
"cosmrs 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cosmwasm-std",
|
||||
"hex-literal",
|
||||
"nym-config",
|
||||
@@ -7594,7 +7685,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"const_format",
|
||||
"cosmrs",
|
||||
"cosmrs 0.15.0 (git+https://github.com/jstuczyn/cosmos-rust?branch=nym-temp/all-validator-features)",
|
||||
"eyre",
|
||||
"futures",
|
||||
"nym-bin-common",
|
||||
@@ -9818,6 +9909,35 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23"
|
||||
dependencies = [
|
||||
"base64 0.21.4",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.0.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_with_macros",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with_macros"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788"
|
||||
dependencies = [
|
||||
"darling 0.20.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.25"
|
||||
@@ -10702,6 +10822,8 @@ dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
@@ -11652,18 +11774,13 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "7.4.3"
|
||||
version = "8.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "447f9238a4553957277b3ee09d80babeae0811f1b3baefb093de1c0448437a37"
|
||||
checksum = "1290fd64cc4e7d3c9b07d7f333ce0ce0007253e32870e632624835cc80b83939"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if",
|
||||
"enum-iterator",
|
||||
"getset",
|
||||
"git2",
|
||||
"rustc_version 0.4.0",
|
||||
"rustversion",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
|
||||
+6
-1
@@ -102,6 +102,7 @@ members = [
|
||||
"nym-node",
|
||||
"nym-node/nym-node-requests",
|
||||
"nym-outfox",
|
||||
"nym-validator-rewarder",
|
||||
"tools/internal/ssl-inject",
|
||||
"tools/internal/sdk-version-bump",
|
||||
"tools/nym-cli",
|
||||
@@ -124,6 +125,7 @@ default-members = [
|
||||
"nym-api",
|
||||
"tools/nymvisor",
|
||||
"explorer-api",
|
||||
"nym-validator-rewarder",
|
||||
]
|
||||
|
||||
exclude = ["explorer", "contracts", "nym-wallet", "nym-connect/mobile/src-tauri", "nym-connect/desktop", "nym-vpn/ui/src-tauri", "cpu-cycles"]
|
||||
@@ -201,7 +203,10 @@ cw-controllers = { version = "=1.1.0" }
|
||||
|
||||
# cosmrs-related
|
||||
bip32 = "0.5.1"
|
||||
cosmrs = "=0.15.0"
|
||||
|
||||
# temporarily using a fork again (yay.) because we need staking and slashing support
|
||||
cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch ="nym-temp/all-validator-features" }
|
||||
#cosmrs = { git = "https://github.com/jstuczyn/cosmos-rust", branch = "nym-temp/all-validator-features" } # unfortuntely we need a fork by yours truly to get the staking support
|
||||
tendermint = "0.34" # same version as used by cosmrs
|
||||
tendermint-rpc = "0.34" # same version as used by cosmrs
|
||||
prost = "0.12"
|
||||
|
||||
@@ -1667,9 +1667,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -5800,9 +5800,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"dev": true
|
||||
},
|
||||
"forwarded": {
|
||||
|
||||
@@ -35,9 +35,10 @@ opentelemetry = { version = "0.19.0", optional = true, features = ["rt-tokio"] }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
vergen = { version = "=7.4.3", default-features = false, features = [
|
||||
vergen = { version = "=8.2.6", default-features = false, features = [
|
||||
"build",
|
||||
"git",
|
||||
"gitcl",
|
||||
"rustc",
|
||||
"cargo",
|
||||
] }
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use vergen::{vergen, Config};
|
||||
use vergen::EmitBuilder;
|
||||
|
||||
fn main() {
|
||||
let mut config = Config::default();
|
||||
if std::env::var("DOCS_RS").is_ok() {
|
||||
// If we don't have access to git information, such as in a docs.rs build, don't error
|
||||
*config.git_mut().skip_if_error_mut() = true;
|
||||
}
|
||||
vergen(config).expect("failed to extract build metadata");
|
||||
EmitBuilder::builder()
|
||||
.all_build()
|
||||
.all_git()
|
||||
.all_rustc()
|
||||
.all_cargo()
|
||||
.emit()
|
||||
.expect("failed to extract build metadata");
|
||||
}
|
||||
|
||||
@@ -40,9 +40,9 @@ pub struct BinaryBuildInformation {
|
||||
/// Provides the rustc channel that was used for the build, for example `nightly`.
|
||||
pub rustc_channel: &'static str,
|
||||
|
||||
// VERGEN_CARGO_PROFILE
|
||||
/// Provides the cargo profile that was used for the build, for example `debug`.
|
||||
pub cargo_profile: &'static str,
|
||||
// VERGEN_CARGO_DEBUG
|
||||
/// Provides the cargo debug mode that was used for the build.
|
||||
pub cargo_debug: &'static str,
|
||||
}
|
||||
|
||||
impl BinaryBuildInformation {
|
||||
@@ -57,7 +57,7 @@ impl BinaryBuildInformation {
|
||||
commit_branch: env!("VERGEN_GIT_BRANCH"),
|
||||
rustc_version: env!("VERGEN_RUSTC_SEMVER"),
|
||||
rustc_channel: env!("VERGEN_RUSTC_CHANNEL"),
|
||||
cargo_profile: env!("VERGEN_CARGO_PROFILE"),
|
||||
cargo_debug: env!("VERGEN_CARGO_DEBUG"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ impl BinaryBuildInformation {
|
||||
commit_branch: self.commit_branch.to_owned(),
|
||||
rustc_version: self.rustc_version.to_owned(),
|
||||
rustc_channel: self.rustc_channel.to_owned(),
|
||||
cargo_profile: self.cargo_profile.to_owned(),
|
||||
cargo_debug: self.cargo_debug.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +115,9 @@ pub struct BinaryBuildInformationOwned {
|
||||
/// Provides the rustc channel that was used for the build, for example `nightly`.
|
||||
pub rustc_channel: String,
|
||||
|
||||
// VERGEN_CARGO_PROFILE
|
||||
/// Provides the cargo profile that was used for the build, for example `debug`.
|
||||
pub cargo_profile: String,
|
||||
// VERGEN_CARGO_DEBUG
|
||||
/// Provides the cargo debug mode that was used for the build.
|
||||
pub cargo_debug: String,
|
||||
}
|
||||
|
||||
impl Display for BinaryBuildInformationOwned {
|
||||
@@ -151,8 +151,8 @@ impl Display for BinaryBuildInformationOwned {
|
||||
self.rustc_version,
|
||||
"rustc Channel:",
|
||||
self.rustc_channel,
|
||||
"cargo Profile:",
|
||||
self.cargo_profile,
|
||||
"cargo Debug:",
|
||||
self.cargo_debug,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,14 @@ pub struct Config {
|
||||
nyxd_config: nyxd::Config,
|
||||
}
|
||||
|
||||
impl TryFrom<NymNetworkDetails> for Config {
|
||||
type Error = ValidatorClientError;
|
||||
|
||||
fn try_from(value: NymNetworkDetails) -> Result<Self, Self::Error> {
|
||||
Config::try_from_nym_network_details(&value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn try_from_nym_network_details(
|
||||
details: &NymNetworkDetails,
|
||||
|
||||
@@ -5,21 +5,24 @@ use crate::nym_api::error::NymAPIError;
|
||||
use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
||||
use async_trait::async_trait;
|
||||
use http_api_client::{ApiClient, NO_PARAMS};
|
||||
use nym_api_requests::coconut::models::{
|
||||
EpochCredentialsResponse, IssuedCredentialResponse, IssuedCredentialsResponse,
|
||||
pub use nym_api_requests::{
|
||||
coconut::{
|
||||
models::{
|
||||
EpochCredentialsResponse, IssuedCredential, IssuedCredentialBody,
|
||||
IssuedCredentialResponse, IssuedCredentialsResponse,
|
||||
},
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CredentialsRequestBody,
|
||||
VerifyCredentialBody, VerifyCredentialResponse,
|
||||
},
|
||||
models::{
|
||||
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
|
||||
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
|
||||
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse, UptimeResponse,
|
||||
},
|
||||
};
|
||||
use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CredentialsRequestBody, VerifyCredentialBody,
|
||||
VerifyCredentialResponse,
|
||||
};
|
||||
use nym_api_requests::models::{
|
||||
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
GatewayStatusReportResponse, GatewayUptimeHistoryResponse, InclusionProbabilityResponse,
|
||||
MixNodeBondAnnotated, MixnodeCoreStatusResponse, MixnodeStatusReportResponse,
|
||||
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse, UptimeResponse,
|
||||
};
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
pub use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
|
||||
use nym_name_service_common::response::NamesListResponse;
|
||||
|
||||
@@ -8,6 +8,8 @@ use cosmwasm_std::{Fraction, Uint128};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::ops::Div;
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Default, Debug, PartialEq, Eq)]
|
||||
pub struct MismatchedDenoms;
|
||||
@@ -126,6 +128,37 @@ impl From<CosmWasmCoin> for Coin {
|
||||
}
|
||||
}
|
||||
|
||||
// unfortunately cosmwasm didn't re-export this correct so we just redefine its
|
||||
#[derive(Error, Debug, PartialEq, Eq)]
|
||||
pub enum CoinFromStrError {
|
||||
#[error("Missing denominator")]
|
||||
MissingDenom,
|
||||
#[error("Missing amount or non-digit characters in amount")]
|
||||
MissingAmount,
|
||||
#[error("Invalid amount: {0}")]
|
||||
InvalidAmount(#[from] std::num::ParseIntError),
|
||||
}
|
||||
|
||||
impl FromStr for Coin {
|
||||
type Err = CoinFromStrError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let pos = s
|
||||
.find(|c: char| !c.is_ascii_digit())
|
||||
.ok_or(CoinFromStrError::MissingDenom)?;
|
||||
let (amount, denom) = s.split_at(pos);
|
||||
|
||||
if amount.is_empty() {
|
||||
return Err(CoinFromStrError::MissingAmount);
|
||||
}
|
||||
|
||||
Ok(Coin {
|
||||
amount: amount.parse::<u128>()?,
|
||||
denom: denom.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CoinConverter {
|
||||
type Target;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::collect_paged;
|
||||
@@ -7,14 +7,22 @@ use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::CosmWasmClient;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use nym_coconut_dkg_common::dealer::{
|
||||
ContractDealing, DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse,
|
||||
};
|
||||
use nym_coconut_dkg_common::msg::QueryMsg as DkgQueryMsg;
|
||||
use nym_coconut_dkg_common::types::{DealerDetails, Epoch, EpochId, InitialReplacementData};
|
||||
use nym_coconut_dkg_common::verification_key::{ContractVKShare, PagedVKSharesResponse};
|
||||
use nym_coconut_dkg_common::types::ChunkIndex;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub use nym_coconut_dkg_common::{
|
||||
dealer::{DealerDetailsResponse, PagedDealerResponse},
|
||||
dealing::{
|
||||
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
|
||||
DealingMetadataResponse, DealingStatusResponse,
|
||||
},
|
||||
msg::QueryMsg as DkgQueryMsg,
|
||||
types::{
|
||||
DealerDetails, DealingIndex, Epoch, EpochId, EpochState, InitialReplacementData, State,
|
||||
},
|
||||
verification_key::{ContractVKShare, PagedVKSharesResponse, VkShareResponse},
|
||||
};
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait DkgQueryClient {
|
||||
@@ -22,10 +30,16 @@ pub trait DkgQueryClient {
|
||||
where
|
||||
for<'a> T: Deserialize<'a>;
|
||||
|
||||
async fn get_state(&self) -> Result<State, NyxdError> {
|
||||
let request = DkgQueryMsg::GetState {};
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_current_epoch(&self) -> Result<Epoch, NyxdError> {
|
||||
let request = DkgQueryMsg::GetCurrentEpochState {};
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_current_epoch_threshold(&self) -> Result<Option<u64>, NyxdError> {
|
||||
let request = DkgQueryMsg::GetCurrentEpochThreshold {};
|
||||
self.query_dkg_contract(request).await
|
||||
@@ -64,17 +78,86 @@ pub trait DkgQueryClient {
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_dealings_paged(
|
||||
async fn get_dealings_metadata(
|
||||
&self,
|
||||
idx: u64,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> Result<PagedDealingsResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetDealing {
|
||||
idx,
|
||||
limit,
|
||||
start_after,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<DealingMetadataResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetDealingsMetadata {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
};
|
||||
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_dealer_dealings_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
) -> Result<DealerDealingsStatusResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetDealerDealingsStatus { epoch_id, dealer };
|
||||
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_dealing_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<DealingStatusResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetDealingStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
};
|
||||
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_dealing_chunk_status(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> Result<DealingChunkStatusResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetDealingChunkStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
};
|
||||
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_dealing_chunk(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> Result<DealingChunkResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetDealingChunk {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
};
|
||||
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_vk_share(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
owner: String,
|
||||
) -> Result<VkShareResponse, NyxdError> {
|
||||
let request = DkgQueryMsg::GetVerificationKey { epoch_id, owner };
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
@@ -91,6 +174,11 @@ pub trait DkgQueryClient {
|
||||
};
|
||||
self.query_dkg_contract(request).await
|
||||
}
|
||||
|
||||
async fn get_contract_cw2_version(&self) -> Result<cw2::ContractVersion, NyxdError> {
|
||||
self.query_dkg_contract(DkgQueryMsg::GetCW2ContractVersion {})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
// extension trait to the query client to deal with the paged queries
|
||||
@@ -106,10 +194,6 @@ pub trait PagedDkgQueryClient: DkgQueryClient {
|
||||
collect_paged!(self, get_past_dealers_paged, dealers)
|
||||
}
|
||||
|
||||
async fn get_all_epoch_dealings(&self, idx: u64) -> Result<Vec<ContractDealing>, NyxdError> {
|
||||
collect_paged!(self, get_dealings_paged, dealings, idx)
|
||||
}
|
||||
|
||||
async fn get_all_verification_key_shares(
|
||||
&self,
|
||||
epoch_id: EpochId,
|
||||
@@ -143,6 +227,7 @@ where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::nyxd::contract_traits::tests::IgnoreValue;
|
||||
use nym_coconut_dkg_common::msg::QueryMsg;
|
||||
|
||||
// it's enough that this compiles and clippy is happy about it
|
||||
#[allow(dead_code)]
|
||||
@@ -151,6 +236,7 @@ mod tests {
|
||||
msg: DkgQueryMsg,
|
||||
) {
|
||||
match msg {
|
||||
DkgQueryMsg::GetState {} => client.get_state().ignore(),
|
||||
DkgQueryMsg::GetCurrentEpochState {} => client.get_current_epoch().ignore(),
|
||||
DkgQueryMsg::GetCurrentEpochThreshold {} => {
|
||||
client.get_current_epoch_threshold().ignore()
|
||||
@@ -165,11 +251,42 @@ mod tests {
|
||||
DkgQueryMsg::GetPastDealers { limit, start_after } => {
|
||||
client.get_past_dealers_paged(start_after, limit).ignore()
|
||||
}
|
||||
DkgQueryMsg::GetDealing {
|
||||
idx,
|
||||
limit,
|
||||
start_after,
|
||||
} => client.get_dealings_paged(idx, start_after, limit).ignore(),
|
||||
DkgQueryMsg::GetDealingStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
} => client
|
||||
.get_dealing_status(epoch_id, dealer, dealing_index)
|
||||
.ignore(),
|
||||
DkgQueryMsg::GetDealingsMetadata {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
} => client
|
||||
.get_dealings_metadata(epoch_id, dealer, dealing_index)
|
||||
.ignore(),
|
||||
QueryMsg::GetDealerDealingsStatus { epoch_id, dealer } => {
|
||||
client.get_dealer_dealings_status(epoch_id, dealer).ignore()
|
||||
}
|
||||
DkgQueryMsg::GetDealingChunkStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
} => client
|
||||
.get_dealing_chunk_status(epoch_id, dealer, dealing_index, chunk_index)
|
||||
.ignore(),
|
||||
DkgQueryMsg::GetDealingChunk {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
} => client
|
||||
.get_dealing_chunk(epoch_id, dealer, dealing_index, chunk_index)
|
||||
.ignore(),
|
||||
DkgQueryMsg::GetVerificationKey { epoch_id, owner } => {
|
||||
client.get_vk_share(epoch_id, owner).ignore()
|
||||
}
|
||||
DkgQueryMsg::GetVerificationKeys {
|
||||
epoch_id,
|
||||
limit,
|
||||
@@ -177,6 +294,7 @@ mod tests {
|
||||
} => client
|
||||
.get_vk_shares_paged(epoch_id, start_after, limit)
|
||||
.ignore(),
|
||||
DkgQueryMsg::GetCW2ContractVersion {} => client.get_contract_cw2_version().ignore(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+52
-20
@@ -8,11 +8,11 @@ use crate::nyxd::{Coin, Fee, SigningCosmWasmClient};
|
||||
use crate::signing::signer::OfflineSigner;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::AccountId;
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_coconut_dkg_common::dealing::{DealingChunkInfo, PartialContractDealing};
|
||||
use nym_coconut_dkg_common::msg::ExecuteMsg as DkgExecuteMsg;
|
||||
use nym_coconut_dkg_common::types::EncodedBTEPublicKeyWithProof;
|
||||
use nym_coconut_dkg_common::types::{DealingIndex, EncodedBTEPublicKeyWithProof};
|
||||
use nym_coconut_dkg_common::verification_key::VerificationKeyShare;
|
||||
use nym_contracts_common::dealings::ContractSafeBytes;
|
||||
use nym_contracts_common::IdentityKey;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
@@ -25,6 +25,13 @@ pub trait DkgSigningClient {
|
||||
funds: Vec<Coin>,
|
||||
) -> Result<ExecuteResult, NyxdError>;
|
||||
|
||||
async fn initiate_dkg(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::InitiateDkg {};
|
||||
|
||||
self.execute_dkg_contract(fee, req, "initiating the DKG".to_string(), vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn advance_dkg_epoch_state(&self, fee: Option<Fee>) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::AdvanceEpochState {};
|
||||
|
||||
@@ -42,12 +49,14 @@ pub trait DkgSigningClient {
|
||||
async fn register_dealer(
|
||||
&self,
|
||||
bte_key: EncodedBTEPublicKeyWithProof,
|
||||
identity_key: IdentityKey,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof: bte_key,
|
||||
identity_key,
|
||||
announce_address,
|
||||
resharing,
|
||||
};
|
||||
@@ -56,18 +65,32 @@ pub trait DkgSigningClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn submit_dealing_bytes(
|
||||
async fn submit_dealing_metadata(
|
||||
&self,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
dealing_index: DealingIndex,
|
||||
chunks: Vec<DealingChunkInfo>,
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::CommitDealing {
|
||||
dealing_bytes,
|
||||
let req = DkgExecuteMsg::CommitDealingsMetadata {
|
||||
dealing_index,
|
||||
chunks,
|
||||
resharing,
|
||||
};
|
||||
|
||||
self.execute_dkg_contract(fee, req, "dealing commitment".to_string(), vec![])
|
||||
self.execute_dkg_contract(fee, req, "dealing metadata commitment".to_string(), vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
async fn submit_dealing_chunk(
|
||||
&self,
|
||||
chunk: PartialContractDealing,
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = DkgExecuteMsg::CommitDealingsChunk { chunk, resharing };
|
||||
|
||||
self.execute_dkg_contract(fee, req, "dealing chunk commitment".to_string(), vec![])
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -94,9 +117,10 @@ pub trait DkgSigningClient {
|
||||
resharing: bool,
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
// the call to unchecked is fine as we're converting from pre-validated `AccountId`
|
||||
let owner = Addr::unchecked(owner.to_string());
|
||||
let req = DkgExecuteMsg::VerifyVerificationKeyShare { owner, resharing };
|
||||
let req = DkgExecuteMsg::VerifyVerificationKeyShare {
|
||||
owner: owner.to_string(),
|
||||
resharing,
|
||||
};
|
||||
|
||||
self.execute_dkg_contract(
|
||||
fee,
|
||||
@@ -146,28 +170,36 @@ mod tests {
|
||||
msg: DkgExecuteMsg,
|
||||
) {
|
||||
match msg {
|
||||
DkgExecuteMsg::InitiateDkg {} => client.initiate_dkg(None).ignore(),
|
||||
DkgExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof,
|
||||
identity_key,
|
||||
announce_address,
|
||||
resharing,
|
||||
} => client
|
||||
.register_dealer(bte_key_with_proof, announce_address, resharing, None)
|
||||
.register_dealer(
|
||||
bte_key_with_proof,
|
||||
identity_key,
|
||||
announce_address,
|
||||
resharing,
|
||||
None,
|
||||
)
|
||||
.ignore(),
|
||||
DkgExecuteMsg::CommitDealing {
|
||||
dealing_bytes,
|
||||
DkgExecuteMsg::CommitDealingsMetadata {
|
||||
dealing_index,
|
||||
chunks,
|
||||
resharing,
|
||||
} => client
|
||||
.submit_dealing_bytes(dealing_bytes, resharing, None)
|
||||
.submit_dealing_metadata(dealing_index, chunks, resharing, None)
|
||||
.ignore(),
|
||||
DkgExecuteMsg::CommitDealingsChunk { chunk, resharing } => {
|
||||
client.submit_dealing_chunk(chunk, resharing, None).ignore()
|
||||
}
|
||||
DkgExecuteMsg::CommitVerificationKeyShare { share, resharing } => client
|
||||
.submit_verification_key_share(share, resharing, None)
|
||||
.ignore(),
|
||||
DkgExecuteMsg::VerifyVerificationKeyShare { owner, resharing } => client
|
||||
.verify_verification_key_share(
|
||||
&owner.into_string().parse().unwrap(),
|
||||
resharing,
|
||||
None,
|
||||
)
|
||||
.verify_verification_key_share(&owner.parse().unwrap(), resharing, None)
|
||||
.ignore(),
|
||||
DkgExecuteMsg::SurpassedThreshold {} => client.surpass_threshold(None).ignore(),
|
||||
DkgExecuteMsg::AdvanceEpochState {} => client.advance_dkg_epoch_state(None).ignore(),
|
||||
|
||||
@@ -8,26 +8,26 @@ use std::str::FromStr;
|
||||
// TODO: all of those could/should be derived via a macro
|
||||
|
||||
// query clients
|
||||
mod coconut_bandwidth_query_client;
|
||||
mod dkg_query_client;
|
||||
mod ephemera_query_client;
|
||||
mod group_query_client;
|
||||
mod mixnet_query_client;
|
||||
mod multisig_query_client;
|
||||
mod name_service_query_client;
|
||||
mod sp_directory_query_client;
|
||||
mod vesting_query_client;
|
||||
pub mod coconut_bandwidth_query_client;
|
||||
pub mod dkg_query_client;
|
||||
pub mod ephemera_query_client;
|
||||
pub mod group_query_client;
|
||||
pub mod mixnet_query_client;
|
||||
pub mod multisig_query_client;
|
||||
pub mod name_service_query_client;
|
||||
pub mod sp_directory_query_client;
|
||||
pub mod vesting_query_client;
|
||||
|
||||
// signing clients
|
||||
mod coconut_bandwidth_signing_client;
|
||||
mod dkg_signing_client;
|
||||
mod ephemera_signing_client;
|
||||
mod group_signing_client;
|
||||
mod mixnet_signing_client;
|
||||
mod multisig_signing_client;
|
||||
mod name_service_signing_client;
|
||||
mod sp_directory_signing_client;
|
||||
mod vesting_signing_client;
|
||||
pub mod coconut_bandwidth_signing_client;
|
||||
pub mod dkg_signing_client;
|
||||
pub mod ephemera_signing_client;
|
||||
pub mod group_signing_client;
|
||||
pub mod mixnet_signing_client;
|
||||
pub mod multisig_signing_client;
|
||||
pub mod name_service_signing_client;
|
||||
pub mod sp_directory_signing_client;
|
||||
pub mod vesting_signing_client;
|
||||
|
||||
// re-export query traits
|
||||
pub use coconut_bandwidth_query_client::{
|
||||
|
||||
+4
-4
@@ -52,10 +52,6 @@ use wasmtimer::tokio::sleep;
|
||||
pub const DEFAULT_BROADCAST_POLLING_RATE: Duration = Duration::from_secs(4);
|
||||
pub const DEFAULT_BROADCAST_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
|
||||
#[cfg(feature = "http-client")]
|
||||
#[async_trait]
|
||||
impl CosmWasmClient for cosmrs::rpc::HttpClient {}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait CosmWasmClient: TendermintRpcClient {
|
||||
@@ -522,3 +518,7 @@ pub trait CosmWasmClient: TendermintRpcClient {
|
||||
res.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<T> CosmWasmClient for T where T: TendermintRpcClient {}
|
||||
|
||||
+1
-1
@@ -425,7 +425,7 @@ where
|
||||
amount: amount.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
.to_any()
|
||||
.map_err(|_| NyxdError::SerializationError("MsgExecuteContract".to_owned()))
|
||||
.map_err(|_| NyxdError::SerializationError("MsgSend".to_owned()))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient};
|
||||
use crate::nyxd::cosmwasm_client::client_traits::SigningCosmWasmClient;
|
||||
use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::{Config, GasPrice, Hash, Height};
|
||||
use crate::rpc::TendermintRpcClient;
|
||||
@@ -26,6 +26,7 @@ use cosmrs::rpc::{HttpClient, HttpClientUrl};
|
||||
pub mod client_traits;
|
||||
mod helpers;
|
||||
pub mod logs;
|
||||
pub mod module_traits;
|
||||
pub mod types;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -329,14 +330,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C, S> CosmWasmClient for MaybeSigningClient<C, S>
|
||||
where
|
||||
C: TendermintRpcClient + Send + Sync,
|
||||
S: Send + Sync,
|
||||
{
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C, S> SigningCosmWasmClient for MaybeSigningClient<C, S>
|
||||
where
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod slashing;
|
||||
pub mod staking;
|
||||
|
||||
pub use staking::query::StakingQueryClient;
|
||||
// pub use slashing::query
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod query;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod query;
|
||||
|
||||
pub use cosmrs::staking::{
|
||||
QueryHistoricalInfoResponse, QueryValidatorResponse, QueryValidatorsResponse, Validator,
|
||||
};
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use super::{QueryHistoricalInfoResponse, QueryValidatorResponse, QueryValidatorsResponse};
|
||||
use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::{CosmWasmClient, PageRequest};
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::proto::cosmos::staking::v1beta1::{
|
||||
QueryHistoricalInfoRequest as ProtoQueryHistoricalInfoRequest,
|
||||
QueryHistoricalInfoResponse as ProtoQueryHistoricalInfoResponse,
|
||||
QueryValidatorRequest as ProtoQueryValidatorRequest,
|
||||
QueryValidatorResponse as ProtoQueryValidatorResponse,
|
||||
QueryValidatorsRequest as ProtoQueryValidatorsRequest,
|
||||
QueryValidatorsResponse as ProtoQueryValidatorsResponse,
|
||||
};
|
||||
use cosmrs::staking::{QueryHistoricalInfoRequest, QueryValidatorRequest, QueryValidatorsRequest};
|
||||
use cosmrs::AccountId;
|
||||
|
||||
// TODO: change trait restriction from `CosmWasmClient` to `TendermintRpcClient`
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
pub trait StakingQueryClient: CosmWasmClient {
|
||||
async fn historical_info(&self, height: i64) -> Result<QueryHistoricalInfoResponse, NyxdError> {
|
||||
let path = Some("/cosmos.staking.v1beta1.Query/HistoricalInfo".to_owned());
|
||||
|
||||
let req = QueryHistoricalInfoRequest { height };
|
||||
|
||||
let res = self
|
||||
.make_abci_query::<ProtoQueryHistoricalInfoRequest, ProtoQueryHistoricalInfoResponse>(
|
||||
path,
|
||||
req.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(res.try_into()?)
|
||||
}
|
||||
|
||||
async fn validator(
|
||||
&self,
|
||||
validator_addr: AccountId,
|
||||
) -> Result<QueryValidatorResponse, NyxdError> {
|
||||
let path = Some("/cosmos.staking.v1beta1.Query/Validator".to_owned());
|
||||
|
||||
let req = QueryValidatorRequest { validator_addr };
|
||||
|
||||
let res = self
|
||||
.make_abci_query::<ProtoQueryValidatorRequest, ProtoQueryValidatorResponse>(
|
||||
path,
|
||||
req.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(res.try_into()?)
|
||||
}
|
||||
|
||||
async fn validators(
|
||||
&self,
|
||||
status: String,
|
||||
pagination: Option<PageRequest>,
|
||||
) -> Result<QueryValidatorsResponse, NyxdError> {
|
||||
let path = Some("/cosmos.staking.v1beta1.Query/Validators".to_owned());
|
||||
|
||||
let req = QueryValidatorsRequest { status, pagination };
|
||||
|
||||
let res = self
|
||||
.make_abci_query::<ProtoQueryValidatorsRequest, ProtoQueryValidatorsResponse>(
|
||||
path,
|
||||
req.into(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(res.try_into()?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
impl<T> StakingQueryClient for T where T: CosmWasmClient {}
|
||||
@@ -29,24 +29,30 @@ use tendermint_rpc::endpoint::*;
|
||||
use tendermint_rpc::{Error as TendermintRpcError, Order};
|
||||
use url::Url;
|
||||
|
||||
pub use crate::nyxd::cosmwasm_client::client_traits::{CosmWasmClient, SigningCosmWasmClient};
|
||||
pub use crate::nyxd::fee::Fee;
|
||||
pub use crate::nyxd::{
|
||||
cosmwasm_client::{
|
||||
client_traits::{CosmWasmClient, SigningCosmWasmClient},
|
||||
module_traits::{self, StakingQueryClient},
|
||||
},
|
||||
fee::Fee,
|
||||
};
|
||||
pub use crate::rpc::TendermintRpcClient;
|
||||
pub use coin::Coin;
|
||||
pub use cosmrs::bank::MsgSend;
|
||||
pub use cosmrs::tendermint::abci::{
|
||||
response::DeliverTx, types::ExecTxResult, Event, EventAttribute,
|
||||
pub use cosmrs::{
|
||||
bank::MsgSend,
|
||||
bip32,
|
||||
crypto::PublicKey,
|
||||
query::{PageRequest, PageResponse},
|
||||
tendermint::{
|
||||
abci::{response::DeliverTx, types::ExecTxResult, Event, EventAttribute},
|
||||
block::Height,
|
||||
hash::{self, Algorithm, Hash},
|
||||
validator::Info as TendermintValidatorInfo,
|
||||
Time as TendermintTime,
|
||||
},
|
||||
tx::{self, Msg},
|
||||
AccountId, Any, Coin as CosmosCoin, Denom, Gas,
|
||||
};
|
||||
pub use cosmrs::tendermint::block::Height;
|
||||
pub use cosmrs::tendermint::hash::{self, Algorithm, Hash};
|
||||
pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
|
||||
pub use cosmrs::tendermint::Time as TendermintTime;
|
||||
pub use cosmrs::tx::Msg;
|
||||
pub use cosmrs::tx::{self};
|
||||
pub use cosmrs::Any;
|
||||
pub use cosmrs::Coin as CosmosCoin;
|
||||
pub use cosmrs::Gas;
|
||||
pub use cosmrs::{bip32, AccountId, Denom};
|
||||
pub use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
pub use cw2;
|
||||
pub use cw3;
|
||||
@@ -56,9 +62,8 @@ pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
|
||||
pub use tendermint_rpc::{
|
||||
endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse},
|
||||
query::Query,
|
||||
Paging,
|
||||
Paging, Request, Response, SimpleRequest,
|
||||
};
|
||||
pub use tendermint_rpc::{Request, Response, SimpleRequest};
|
||||
|
||||
#[cfg(feature = "http-client")]
|
||||
use crate::http_client;
|
||||
@@ -98,6 +103,14 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<NymNetworkDetails> for Config {
|
||||
type Error = NyxdError;
|
||||
|
||||
fn try_from(value: NymNetworkDetails) -> Result<Self, Self::Error> {
|
||||
Config::try_from_nym_network_details(&value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NyxdClient<C, S = NoSigner> {
|
||||
client: MaybeSigningClient<C, S>,
|
||||
@@ -729,7 +742,7 @@ where
|
||||
where
|
||||
H: Into<Height> + Send,
|
||||
{
|
||||
self.client.validators(height, paging).await
|
||||
TendermintRpcClient::validators(&self.client, height, paging).await
|
||||
}
|
||||
|
||||
async fn latest_consensus_params(
|
||||
@@ -804,14 +817,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C, S> CosmWasmClient for NyxdClient<C, S>
|
||||
where
|
||||
C: TendermintRpcClient + Send + Sync,
|
||||
S: Send + Sync,
|
||||
{
|
||||
}
|
||||
|
||||
impl<C, S> OfflineSigner for NyxdClient<C, S>
|
||||
where
|
||||
S: OfflineSigner,
|
||||
|
||||
@@ -14,7 +14,7 @@ pub use nym_coconut::{
|
||||
aggregate_signature_shares, aggregate_verification_keys, blind_sign, hash_to_scalar,
|
||||
prepare_blind_sign, prove_bandwidth_credential, Attribute, Base58, BlindSignRequest,
|
||||
BlindedSignature, Bytable, CoconutError, KeyPair, Parameters, PrivateAttribute,
|
||||
PublicAttribute, Signature, SignatureShare, Theta, VerificationKey,
|
||||
PublicAttribute, SecretKey, Signature, SignatureShare, Theta, VerificationKey,
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Getters, CopyGetters, Clone, PartialEq, Eq)]
|
||||
|
||||
@@ -6,7 +6,7 @@ use log::{debug, info};
|
||||
use std::str::FromStr;
|
||||
|
||||
use nym_coconut_dkg_common::msg::InstantiateMsg;
|
||||
use nym_coconut_dkg_common::types::TimeConfiguration;
|
||||
use nym_coconut_dkg_common::types::{TimeConfiguration, DEFAULT_DEALINGS};
|
||||
use nym_validator_client::nyxd::AccountId;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
@@ -93,6 +93,7 @@ pub async fn generate(args: Args) {
|
||||
multisig_addr: multisig_addr.to_string(),
|
||||
time_configuration: Some(time_configuration),
|
||||
mix_denom,
|
||||
key_size: DEFAULT_DEALINGS as u32,
|
||||
};
|
||||
|
||||
debug!("instantiate_msg: {:?}", instantiate_msg);
|
||||
|
||||
@@ -10,6 +10,8 @@ license.workspace = true
|
||||
cosmwasm-schema = { workspace = true }
|
||||
cosmwasm-std = { workspace = true }
|
||||
cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
cw4 = { workspace = true }
|
||||
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common" }
|
||||
nym-multisig-contract-common = { path = "../multisig-contract" }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, NodeIndex};
|
||||
use crate::types::{EncodedBTEPublicKeyWithProof, NodeIndex};
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
@@ -9,6 +9,7 @@ use cosmwasm_std::Addr;
|
||||
pub struct DealerDetails {
|
||||
pub address: Addr,
|
||||
pub bte_public_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
pub ed25519_identity: String,
|
||||
pub announce_address: String,
|
||||
pub assigned_index: NodeIndex,
|
||||
}
|
||||
@@ -64,38 +65,3 @@ impl PagedDealerResponse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct ContractDealing {
|
||||
pub dealing: ContractSafeBytes,
|
||||
pub dealer: Addr,
|
||||
}
|
||||
|
||||
impl ContractDealing {
|
||||
pub fn new(dealing: ContractSafeBytes, dealer: Addr) -> Self {
|
||||
ContractDealing { dealing, dealer }
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct PagedDealingsResponse {
|
||||
pub dealings: Vec<ContractDealing>,
|
||||
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<Addr>,
|
||||
}
|
||||
|
||||
impl PagedDealingsResponse {
|
||||
pub fn new(
|
||||
dealings: Vec<ContractDealing>,
|
||||
per_page: usize,
|
||||
start_next_after: Option<Addr>,
|
||||
) -> Self {
|
||||
PagedDealingsResponse {
|
||||
dealings,
|
||||
per_page,
|
||||
start_next_after,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::types::{ChunkIndex, DealingIndex, EpochId, PartialContractDealingData};
|
||||
use contracts_common::dealings::ContractSafeBytes;
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Addr;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
/// Defines the maximum size of a dealing chunk. Currently set to 2kB
|
||||
pub const MAX_DEALING_CHUNK_SIZE: usize = 2048;
|
||||
|
||||
/// Defines the maximum size of a full dealing.
|
||||
/// Currently set to 100kB (which is enough for a dealing created for 100 parties)
|
||||
pub const MAX_DEALING_SIZE: usize = 102400;
|
||||
|
||||
pub const MAX_DEALING_CHUNKS: usize = MAX_DEALING_SIZE / MAX_DEALING_CHUNK_SIZE;
|
||||
|
||||
// 2 public attributes, 2 private attributes, 1 fixed for coconut credential
|
||||
pub const DEFAULT_DEALINGS: usize = 2 + 2 + 1;
|
||||
|
||||
pub fn chunk_dealing(
|
||||
dealing_index: DealingIndex,
|
||||
dealing_bytes: Vec<u8>,
|
||||
chunk_size: usize,
|
||||
) -> HashMap<ChunkIndex, PartialContractDealing> {
|
||||
let mut chunks = HashMap::new();
|
||||
for (chunk_index, chunk) in dealing_bytes.chunks(chunk_size).enumerate() {
|
||||
let chunk = PartialContractDealing {
|
||||
dealing_index,
|
||||
chunk_index: chunk_index as ChunkIndex,
|
||||
data: ContractSafeBytes(chunk.to_vec()),
|
||||
};
|
||||
chunks.insert(chunk_index as ChunkIndex, chunk);
|
||||
}
|
||||
|
||||
chunks
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct DealingChunkInfo {
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl DealingChunkInfo {
|
||||
pub fn new(size: usize) -> Self {
|
||||
DealingChunkInfo { size }
|
||||
}
|
||||
|
||||
pub fn construct(dealing_len: usize, chunk_size: usize) -> Vec<Self> {
|
||||
let (full_chunks, overflow) = (dealing_len / chunk_size, dealing_len % chunk_size);
|
||||
|
||||
let mut chunks = Vec::new();
|
||||
for _ in 0..full_chunks {
|
||||
chunks.push(DealingChunkInfo::new(chunk_size));
|
||||
}
|
||||
|
||||
if overflow != 0 {
|
||||
chunks.push(DealingChunkInfo::new(overflow));
|
||||
}
|
||||
|
||||
chunks
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub struct SubmittedChunk {
|
||||
pub info: DealingChunkInfo,
|
||||
|
||||
pub status: ChunkSubmissionStatus,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Default, Copy)]
|
||||
pub struct ChunkSubmissionStatus {
|
||||
// this field is updated by the contract itself to indicate when this particular chunk has been received
|
||||
pub submission_height: Option<u64>,
|
||||
}
|
||||
|
||||
impl ChunkSubmissionStatus {
|
||||
pub fn submitted(&self) -> bool {
|
||||
self.submission_height.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DealingChunkInfo> for SubmittedChunk {
|
||||
fn from(value: DealingChunkInfo) -> Self {
|
||||
SubmittedChunk::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl SubmittedChunk {
|
||||
pub fn new(info: DealingChunkInfo) -> Self {
|
||||
SubmittedChunk {
|
||||
info,
|
||||
status: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn submitted(&self) -> bool {
|
||||
self.status.submitted()
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct DealingMetadata {
|
||||
pub dealing_index: DealingIndex,
|
||||
|
||||
pub submitted_chunks: BTreeMap<ChunkIndex, SubmittedChunk>,
|
||||
}
|
||||
|
||||
impl DealingMetadata {
|
||||
pub fn new(dealing_index: DealingIndex, chunks: Vec<DealingChunkInfo>) -> Self {
|
||||
DealingMetadata {
|
||||
dealing_index,
|
||||
submitted_chunks: chunks
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(id, chunk)| (id as ChunkIndex, chunk.into()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_complete(&self) -> bool {
|
||||
self.submitted_chunks.values().all(|c| c.submitted())
|
||||
}
|
||||
|
||||
pub fn total_size(&self) -> usize {
|
||||
self.submitted_chunks.values().map(|c| c.info.size).sum()
|
||||
}
|
||||
|
||||
pub fn submission_statuses(&self) -> BTreeMap<ChunkIndex, ChunkSubmissionStatus> {
|
||||
self.submitted_chunks
|
||||
.iter()
|
||||
.map(|(id, c)| (*id, c.status))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct PartialContractDealing {
|
||||
pub dealing_index: DealingIndex,
|
||||
pub chunk_index: ChunkIndex,
|
||||
pub data: PartialContractDealingData,
|
||||
}
|
||||
|
||||
impl PartialContractDealing {
|
||||
pub fn new(
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
data: PartialContractDealingData,
|
||||
) -> Self {
|
||||
PartialContractDealing {
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct DealingMetadataResponse {
|
||||
pub epoch_id: EpochId,
|
||||
|
||||
pub dealer: Addr,
|
||||
|
||||
pub dealing_index: DealingIndex,
|
||||
|
||||
pub metadata: Option<DealingMetadata>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct DealingChunkResponse {
|
||||
pub epoch_id: EpochId,
|
||||
|
||||
pub dealer: Addr,
|
||||
|
||||
pub dealing_index: DealingIndex,
|
||||
|
||||
pub chunk_index: ChunkIndex,
|
||||
|
||||
pub chunk: Option<PartialContractDealingData>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct DealingChunkStatusResponse {
|
||||
pub epoch_id: EpochId,
|
||||
|
||||
pub dealer: Addr,
|
||||
|
||||
pub dealing_index: DealingIndex,
|
||||
|
||||
pub chunk_index: ChunkIndex,
|
||||
|
||||
pub status: ChunkSubmissionStatus,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct DealingStatusResponse {
|
||||
pub epoch_id: EpochId,
|
||||
|
||||
pub dealer: Addr,
|
||||
|
||||
pub dealing_index: DealingIndex,
|
||||
|
||||
pub status: DealingStatus,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct DealingStatus {
|
||||
pub has_metadata: bool,
|
||||
|
||||
pub fully_submitted: bool,
|
||||
|
||||
pub chunk_submission_status: BTreeMap<ChunkIndex, ChunkSubmissionStatus>,
|
||||
}
|
||||
|
||||
impl From<Option<DealingMetadata>> for DealingStatus {
|
||||
fn from(metadata: Option<DealingMetadata>) -> Self {
|
||||
DealingStatus {
|
||||
has_metadata: metadata.is_some(),
|
||||
fully_submitted: metadata
|
||||
.as_ref()
|
||||
.map(|m| m.is_complete())
|
||||
.unwrap_or_default(),
|
||||
chunk_submission_status: metadata
|
||||
.map(|m| m.submission_statuses())
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct DealerDealingsStatusResponse {
|
||||
pub epoch_id: EpochId,
|
||||
|
||||
pub dealer: Addr,
|
||||
|
||||
pub all_dealings_fully_submitted: bool,
|
||||
|
||||
pub dealing_submission_status: BTreeMap<DealingIndex, DealingStatus>,
|
||||
}
|
||||
|
||||
impl DealerDealingsStatusResponse {
|
||||
pub fn full_dealings(&self) -> usize {
|
||||
self.dealing_submission_status
|
||||
.values()
|
||||
.filter(|s| s.fully_submitted)
|
||||
.count()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn chunking_dealings() {
|
||||
const CHUNK_SIZE: usize = 512;
|
||||
|
||||
let test_cases = [
|
||||
(CHUNK_SIZE - 10, CHUNK_SIZE, 1),
|
||||
(CHUNK_SIZE, CHUNK_SIZE, 1),
|
||||
(CHUNK_SIZE + 10, CHUNK_SIZE, 2),
|
||||
(CHUNK_SIZE * 2, CHUNK_SIZE, 2),
|
||||
(CHUNK_SIZE * 2 + 1, CHUNK_SIZE, 3),
|
||||
(CHUNK_SIZE * 10 + 42, CHUNK_SIZE, 11),
|
||||
];
|
||||
|
||||
for (dealing_len, chunk_size, expected_chunks) in test_cases {
|
||||
let chunks = DealingChunkInfo::construct(dealing_len, chunk_size);
|
||||
assert_eq!(expected_chunks, chunks.len());
|
||||
assert_eq!(dealing_len, chunks.iter().map(|c| c.size).sum::<usize>());
|
||||
|
||||
let mut expected_last = dealing_len % chunk_size;
|
||||
if expected_last == 0 {
|
||||
expected_last = chunk_size;
|
||||
}
|
||||
assert_eq!(chunks.last().unwrap().size, expected_last);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod dealer;
|
||||
pub mod dealing;
|
||||
pub mod event_attributes;
|
||||
pub mod msg;
|
||||
pub mod types;
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::types::{ContractSafeBytes, EncodedBTEPublicKeyWithProof, EpochId, TimeConfiguration};
|
||||
use crate::dealing::{DealingChunkInfo, PartialContractDealing};
|
||||
use crate::types::{
|
||||
ChunkIndex, DealingIndex, EncodedBTEPublicKeyWithProof, EpochId, TimeConfiguration,
|
||||
};
|
||||
use crate::verification_key::VerificationKeyShare;
|
||||
use contracts_common::IdentityKey;
|
||||
use cosmwasm_schema::cw_serde;
|
||||
use cosmwasm_std::Addr;
|
||||
|
||||
#[cfg(feature = "schema")]
|
||||
use crate::{
|
||||
dealer::{DealerDetailsResponse, PagedDealerResponse, PagedDealingsResponse},
|
||||
types::{Epoch, InitialReplacementData},
|
||||
verification_key::PagedVKSharesResponse,
|
||||
dealer::{DealerDetailsResponse, PagedDealerResponse},
|
||||
dealing::{
|
||||
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
|
||||
DealingMetadataResponse, DealingStatusResponse,
|
||||
},
|
||||
types::{Epoch, InitialReplacementData, State},
|
||||
verification_key::{PagedVKSharesResponse, VkShareResponse},
|
||||
};
|
||||
#[cfg(feature = "schema")]
|
||||
use cosmwasm_schema::QueryResponses;
|
||||
@@ -21,18 +28,31 @@ pub struct InstantiateMsg {
|
||||
pub multisig_addr: String,
|
||||
pub time_configuration: Option<TimeConfiguration>,
|
||||
pub mix_denom: String,
|
||||
|
||||
/// Specifies the number of elements in the derived keys
|
||||
pub key_size: u32,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub enum ExecuteMsg {
|
||||
// we could have just re-used AdvanceEpochState, but imo an explicit message is better
|
||||
InitiateDkg {},
|
||||
|
||||
RegisterDealer {
|
||||
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
identity_key: IdentityKey,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
},
|
||||
|
||||
CommitDealing {
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
CommitDealingsMetadata {
|
||||
dealing_index: DealingIndex,
|
||||
chunks: Vec<DealingChunkInfo>,
|
||||
resharing: bool,
|
||||
},
|
||||
|
||||
CommitDealingsChunk {
|
||||
chunk: PartialContractDealing,
|
||||
resharing: bool,
|
||||
},
|
||||
|
||||
@@ -42,8 +62,7 @@ pub enum ExecuteMsg {
|
||||
},
|
||||
|
||||
VerifyVerificationKeyShare {
|
||||
// TODO: this should be using a String...
|
||||
owner: Addr,
|
||||
owner: String,
|
||||
resharing: bool,
|
||||
},
|
||||
|
||||
@@ -55,6 +74,9 @@ pub enum ExecuteMsg {
|
||||
#[cw_serde]
|
||||
#[cfg_attr(feature = "schema", derive(QueryResponses))]
|
||||
pub enum QueryMsg {
|
||||
#[cfg_attr(feature = "schema", returns(State))]
|
||||
GetState {},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(Epoch))]
|
||||
GetCurrentEpochState {},
|
||||
|
||||
@@ -79,19 +101,53 @@ pub enum QueryMsg {
|
||||
start_after: Option<String>,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(PagedDealingsResponse))]
|
||||
GetDealing {
|
||||
idx: u64,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<String>,
|
||||
#[cfg_attr(feature = "schema", returns(DealingMetadataResponse))]
|
||||
GetDealingsMetadata {
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(DealerDealingsStatusResponse))]
|
||||
GetDealerDealingsStatus { epoch_id: EpochId, dealer: String },
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(DealingStatusResponse))]
|
||||
GetDealingStatus {
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(DealingChunkStatusResponse))]
|
||||
GetDealingChunkStatus {
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(DealingChunkResponse))]
|
||||
GetDealingChunk {
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
},
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(VkShareResponse))]
|
||||
GetVerificationKey { epoch_id: EpochId, owner: String },
|
||||
|
||||
#[cfg_attr(feature = "schema", returns(PagedVKSharesResponse))]
|
||||
GetVerificationKeys {
|
||||
epoch_id: EpochId,
|
||||
limit: Option<u32>,
|
||||
start_after: Option<String>,
|
||||
},
|
||||
|
||||
/// Gets the stored contract version information that's required by the CW2 spec interface for migrations.
|
||||
#[serde(rename = "get_cw2_contract_version")]
|
||||
#[cfg_attr(feature = "schema", returns(cw2::ContractVersion))]
|
||||
GetCW2ContractVersion {},
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
|
||||
@@ -8,14 +8,18 @@ use std::str::FromStr;
|
||||
pub use crate::dealer::{DealerDetails, PagedDealerResponse};
|
||||
pub use contracts_common::dealings::ContractSafeBytes;
|
||||
pub use cosmwasm_std::{Addr, Coin, Timestamp};
|
||||
pub use cw4::Cw4Contract;
|
||||
|
||||
pub type EncodedBTEPublicKeyWithProof = String;
|
||||
pub type EncodedBTEPublicKeyWithProofRef<'a> = &'a str;
|
||||
pub type NodeIndex = u64;
|
||||
pub type EpochId = u64;
|
||||
|
||||
// 2 public attributes, 2 private attributes, 1 fixed for coconut credential
|
||||
pub const TOTAL_DEALINGS: usize = 2 + 2 + 1;
|
||||
pub type DealingIndex = u32;
|
||||
// we really don't need to hold more data than that (even u8 would have been enough),
|
||||
// but explicitly make it different type than `DealingIndex` so type system would detect any
|
||||
// accidental misuses
|
||||
pub type ChunkIndex = u16;
|
||||
pub type PartialContractDealingData = ContractSafeBytes;
|
||||
|
||||
#[cw_serde]
|
||||
pub struct InitialReplacementData {
|
||||
@@ -73,13 +77,23 @@ impl Default for TimeConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct State {
|
||||
pub mix_denom: String,
|
||||
pub multisig_addr: Addr,
|
||||
pub group_addr: Cw4Contract,
|
||||
|
||||
/// Specifies the number of elements in the derived keys
|
||||
pub key_size: u32,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
#[derive(Copy, Default)]
|
||||
pub struct Epoch {
|
||||
pub state: EpochState,
|
||||
pub epoch_id: EpochId,
|
||||
pub time_configuration: TimeConfiguration,
|
||||
pub finish_timestamp: Timestamp,
|
||||
pub finish_timestamp: Option<Timestamp>,
|
||||
}
|
||||
|
||||
impl Epoch {
|
||||
@@ -90,36 +104,40 @@ impl Epoch {
|
||||
current_timestamp: Timestamp,
|
||||
) -> Self {
|
||||
let duration = match state {
|
||||
EpochState::WaitingInitialisation => None,
|
||||
EpochState::PublicKeySubmission { .. } => {
|
||||
time_configuration.public_key_submission_time_secs
|
||||
Some(time_configuration.public_key_submission_time_secs)
|
||||
}
|
||||
EpochState::DealingExchange { .. } => {
|
||||
Some(time_configuration.dealing_exchange_time_secs)
|
||||
}
|
||||
EpochState::DealingExchange { .. } => time_configuration.dealing_exchange_time_secs,
|
||||
EpochState::VerificationKeySubmission { .. } => {
|
||||
time_configuration.verification_key_submission_time_secs
|
||||
Some(time_configuration.verification_key_submission_time_secs)
|
||||
}
|
||||
EpochState::VerificationKeyValidation { .. } => {
|
||||
time_configuration.verification_key_validation_time_secs
|
||||
Some(time_configuration.verification_key_validation_time_secs)
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { .. } => {
|
||||
time_configuration.verification_key_finalization_time_secs
|
||||
Some(time_configuration.verification_key_finalization_time_secs)
|
||||
}
|
||||
EpochState::InProgress => time_configuration.in_progress_time_secs,
|
||||
EpochState::InProgress => Some(time_configuration.in_progress_time_secs),
|
||||
};
|
||||
Epoch {
|
||||
state,
|
||||
epoch_id,
|
||||
time_configuration,
|
||||
finish_timestamp: current_timestamp.plus_seconds(duration),
|
||||
finish_timestamp: duration.map(|d| current_timestamp.plus_seconds(d)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn final_timestamp_secs(&self) -> u64 {
|
||||
let mut finish = self.finish_timestamp.seconds();
|
||||
pub fn final_timestamp_secs(&self) -> Option<u64> {
|
||||
let mut finish = self.finish_timestamp?.seconds();
|
||||
let time_configuration = self.time_configuration;
|
||||
let mut curr_epoch_state = self.state;
|
||||
while let Some(state) = curr_epoch_state.next() {
|
||||
curr_epoch_state = state;
|
||||
let adding = match curr_epoch_state {
|
||||
EpochState::WaitingInitialisation => return None,
|
||||
EpochState::PublicKeySubmission { .. } => {
|
||||
time_configuration.public_key_submission_time_secs
|
||||
}
|
||||
@@ -137,12 +155,13 @@ impl Epoch {
|
||||
};
|
||||
finish += adding;
|
||||
}
|
||||
finish
|
||||
Some(finish)
|
||||
}
|
||||
}
|
||||
|
||||
// currently (it is still extremely likely to change, we might be able to get rid of verification key-related complaints),
|
||||
// the epoch can be in the following states (in order):
|
||||
// 0. WaitingInitialisation -> the contract has been instantiated, but awaits for the admin to kick off the process (group members might still be getting added)
|
||||
// 1. PublicKeySubmission -> potential dealers are submitting their BTE and ed25519 public keys to participate in dealing exchange
|
||||
// 2. DealingExchange -> the actual (off-chain) dealing exchange is happening
|
||||
// 3. ComplaintSubmission -> receivers submitting evidence of other dealers sending malformed data
|
||||
@@ -156,6 +175,7 @@ impl Epoch {
|
||||
#[cw_serde]
|
||||
#[derive(Copy)]
|
||||
pub enum EpochState {
|
||||
WaitingInitialisation,
|
||||
PublicKeySubmission { resharing: bool },
|
||||
DealingExchange { resharing: bool },
|
||||
VerificationKeySubmission { resharing: bool },
|
||||
@@ -166,13 +186,14 @@ pub enum EpochState {
|
||||
|
||||
impl Default for EpochState {
|
||||
fn default() -> Self {
|
||||
Self::PublicKeySubmission { resharing: false }
|
||||
Self::WaitingInitialisation
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EpochState {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
EpochState::WaitingInitialisation => write!(f, "Waiting for initialisation"),
|
||||
EpochState::PublicKeySubmission { resharing } => {
|
||||
write!(f, "PublicKeySubmission (resharing: {resharing})")
|
||||
}
|
||||
@@ -194,8 +215,13 @@ impl Display for EpochState {
|
||||
}
|
||||
|
||||
impl EpochState {
|
||||
pub fn first() -> Self {
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
}
|
||||
|
||||
pub fn next(self) -> Option<Self> {
|
||||
match self {
|
||||
EpochState::WaitingInitialisation => None,
|
||||
EpochState::PublicKeySubmission { resharing } => {
|
||||
Some(EpochState::DealingExchange { resharing })
|
||||
}
|
||||
|
||||
@@ -20,6 +20,13 @@ pub struct ContractVKShare {
|
||||
pub verified: bool,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct VkShareResponse {
|
||||
pub owner: Addr,
|
||||
pub epoch_id: EpochId,
|
||||
pub share: Option<ContractVKShare>,
|
||||
}
|
||||
|
||||
#[cw_serde]
|
||||
pub struct PagedVKSharesResponse {
|
||||
pub shares: Vec<ContractVKShare>,
|
||||
@@ -36,7 +43,10 @@ pub fn to_cosmos_msg(
|
||||
multisig_addr: String,
|
||||
expiration_time: Timestamp,
|
||||
) -> StdResult<CosmosMsg> {
|
||||
let verify_vk_share_req = ExecuteMsg::VerifyVerificationKeyShare { owner, resharing };
|
||||
let verify_vk_share_req = ExecuteMsg::VerifyVerificationKeyShare {
|
||||
owner: owner.to_string(),
|
||||
resharing,
|
||||
};
|
||||
let verify_vk_share_msg = CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: coconut_dkg_addr,
|
||||
msg: to_binary(&verify_vk_share_req)?,
|
||||
@@ -57,7 +67,14 @@ pub fn to_cosmos_msg(
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
pub fn owner_from_cosmos_msgs(msgs: &[CosmosMsg]) -> Option<Addr> {
|
||||
// DKG SAFETY:
|
||||
// each legit verification proposal will only contain a single execute msg,
|
||||
// if they have more than one, we can safely ignore it
|
||||
pub fn owner_from_cosmos_msgs(msgs: &[CosmosMsg]) -> Option<String> {
|
||||
if msgs.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
if let Some(CosmosMsg::Wasm(WasmMsg::Execute {
|
||||
contract_addr: _,
|
||||
msg,
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
// some sane upper-bound size on byte sizes
|
||||
// currently set to 128 bytes
|
||||
pub const MAX_DISPLAY_SIZE: usize = 128;
|
||||
|
||||
// TODO: if we are to use this for different types, it might make sense to introduce something like
|
||||
// CommitmentTypeId field on the below for distinguishing different ones. it would somehow become part of the trait
|
||||
// helps to transfer bytes between contract boundary to decrease amount of data sent accross
|
||||
// after it's put to `Binary`
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, JsonSchema)]
|
||||
pub struct ContractSafeBytes(pub Vec<u8>);
|
||||
|
||||
@@ -23,6 +23,18 @@ impl Deref for ContractSafeBytes {
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ContractSafeBytes {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for ContractSafeBytes {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
ContractSafeBytes(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ContractSafeBytes {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
if !self.0.is_empty() {
|
||||
|
||||
@@ -5,7 +5,9 @@ use nym_bandwidth_controller::acquire::state::State;
|
||||
use nym_client_core::config::disk_persistence::CommonClientPaths;
|
||||
use nym_config::DEFAULT_DATA_DIR;
|
||||
use nym_credential_storage::persistent_storage::PersistentStorage;
|
||||
use nym_validator_client::nyxd::contract_traits::{CoconutBandwidthSigningClient, DkgQueryClient};
|
||||
use nym_validator_client::nyxd::contract_traits::{
|
||||
dkg_query_client::EpochState, CoconutBandwidthSigningClient, DkgQueryClient,
|
||||
};
|
||||
use nym_validator_client::nyxd::Coin;
|
||||
use std::path::PathBuf;
|
||||
use std::process::exit;
|
||||
@@ -87,21 +89,29 @@ where
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.expect("the system clock is set to 01/01/1970 (or earlier)")
|
||||
.as_secs();
|
||||
|
||||
if epoch.state.is_final() {
|
||||
if current_timestamp_secs + SAFETY_BUFFER_SECS >= epoch.finish_timestamp.seconds() {
|
||||
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
|
||||
exit(0);
|
||||
if let Some(finish_timestamp) = epoch.finish_timestamp {
|
||||
if current_timestamp_secs + SAFETY_BUFFER_SECS >= finish_timestamp.seconds() {
|
||||
info!("In the next {} minute(s), a transition will take place in the coconut system. Deposits should be halted in this time for safety reasons.", SAFETY_BUFFER_SECS / 60);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
} else {
|
||||
} else if let Some(final_timestamp) = epoch.final_timestamp_secs() {
|
||||
// Use 1 additional second to not start the next iteration immediately and spam get_current_epoch queries
|
||||
let secs_until_final = epoch
|
||||
.final_timestamp_secs()
|
||||
.saturating_sub(current_timestamp_secs)
|
||||
+ 1;
|
||||
let secs_until_final = final_timestamp.saturating_sub(current_timestamp_secs) + 1;
|
||||
info!("Approximately {} seconds until coconut is available. Sleeping until then. You can safely kill the process at any moment.", secs_until_final);
|
||||
tokio::time::sleep(Duration::from_secs(secs_until_final)).await;
|
||||
} else if matches!(epoch.state, EpochState::WaitingInitialisation) {
|
||||
info!("dkg hasn't been initialised yet and it is not known when it will be. Going to check again later");
|
||||
tokio::time::sleep(Duration::from_secs(60 * 5)).await;
|
||||
} else {
|
||||
// this should never be the case since the only case where final timestamp is unknown is when it's waiting for initialisation,
|
||||
// but let's guard ourselves against future changes
|
||||
info!("it is unknown when coconut will be come available. Going to check again later");
|
||||
tokio::time::sleep(Duration::from_secs(60 * 5)).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,7 @@ use std::collections::HashMap;
|
||||
use std::ops::Neg;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(Clone, PartialEq, Eq))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Ciphertexts {
|
||||
pub rr: [G1Projective; NUM_CHUNKS],
|
||||
pub ss: [G1Projective; NUM_CHUNKS],
|
||||
|
||||
@@ -53,8 +53,7 @@ impl PublicKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PublicKeyWithProof {
|
||||
pub(crate) key: PublicKey,
|
||||
pub(crate) proof: ProofOfDiscreteLog,
|
||||
|
||||
@@ -67,8 +67,7 @@ impl<'a> Instance<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(Clone, PartialEq, Eq))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ProofOfChunking {
|
||||
y0: G1Projective,
|
||||
bb: Vec<G1Projective>,
|
||||
|
||||
@@ -13,8 +13,7 @@ use zeroize::Zeroize;
|
||||
const DISCRETE_LOG_DOMAIN: &[u8] =
|
||||
b"NYM_COCONUT_NIDKG_V01_CS01_WITH_BLS12381_XMD:SHA-256_SSWU_RO_PROOF_DISCRETE_LOG";
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ProofOfDiscreteLog {
|
||||
pub(crate) rand_commitment: G1Projective,
|
||||
pub(crate) response: Scalar,
|
||||
|
||||
@@ -76,8 +76,7 @@ impl<'a> Instance<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(Clone, PartialEq, Eq))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ProofOfSecretSharing {
|
||||
ff: G1Projective,
|
||||
aa: G2Projective,
|
||||
|
||||
+100
-20
@@ -82,8 +82,7 @@ impl RecoveredVerificationKeys {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Dealing {
|
||||
pub public_coefficients: PublicCoefficients,
|
||||
pub ciphertexts: Ciphertexts,
|
||||
@@ -321,9 +320,17 @@ impl<'a> TryFrom<&'a nym_contracts_common::dealings::ContractSafeBytes> for Deal
|
||||
}
|
||||
}
|
||||
|
||||
// this assumes all dealings have been verified
|
||||
/// Attempt to run the `VkCombine` algorithm to obtain the public master verification key, `VK`
|
||||
/// alongside shares of the verification key, `shvk_{1}`, `shvk_{2}`, ... `svhk_{n}`, where n is the number of receivers.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `dealings`: map of dealer indices to dealings they generated
|
||||
/// * `threshold`: explicit threshold value of the associated dealings
|
||||
/// * `receivers`:map of receiver indices to their public keys
|
||||
// note: this function assumes all dealings have already been verified
|
||||
pub fn try_recover_verification_keys(
|
||||
dealings: &[Dealing],
|
||||
dealings: &BTreeMap<NodeIndex, Dealing>,
|
||||
threshold: Threshold,
|
||||
receivers: &BTreeMap<NodeIndex, PublicKey>,
|
||||
) -> Result<RecoveredVerificationKeys, DkgError> {
|
||||
@@ -331,24 +338,31 @@ pub fn try_recover_verification_keys(
|
||||
return Err(DkgError::NoDealingsAvailable);
|
||||
}
|
||||
|
||||
let threshold_usize = threshold as usize;
|
||||
let threshold = threshold as usize;
|
||||
|
||||
if dealings.len() < threshold {
|
||||
return Err(DkgError::NotEnoughDealingsAvailable {
|
||||
available: dealings.len(),
|
||||
required: threshold,
|
||||
});
|
||||
}
|
||||
|
||||
if !dealings
|
||||
.iter()
|
||||
.all(|dealing| dealing.public_coefficients.size() == threshold_usize)
|
||||
.values()
|
||||
.all(|dealing| dealing.public_coefficients.size() == threshold)
|
||||
{
|
||||
return Err(DkgError::MismatchedDealings);
|
||||
}
|
||||
|
||||
let indices = receivers.keys().collect::<Vec<_>>();
|
||||
let dealer_indices = dealings.keys().collect::<Vec<_>>();
|
||||
|
||||
// Compute A0, ..., A_{t-1}
|
||||
let mut interpolated_coefficients = Vec::with_capacity(threshold_usize);
|
||||
for k in 0..threshold_usize {
|
||||
let mut samples = Vec::with_capacity(indices.len());
|
||||
for (j, dealing) in dealings.iter().enumerate() {
|
||||
let mut interpolated_coefficients = Vec::with_capacity(threshold);
|
||||
for k in 0..threshold {
|
||||
let mut samples = Vec::with_capacity(dealer_indices.len());
|
||||
for (dealer_index, dealing) in dealings.iter() {
|
||||
samples.push((
|
||||
Scalar::from(*indices[j]),
|
||||
Scalar::from(*dealer_index),
|
||||
*dealing.public_coefficients.nth(k),
|
||||
))
|
||||
}
|
||||
@@ -365,7 +379,7 @@ pub fn try_recover_verification_keys(
|
||||
// shvk_j = A0^{j^0} * A1^{j^1} * ... * A_{t-1}^{j^{t-1}}
|
||||
let verification_key_shares = receivers
|
||||
.keys()
|
||||
.map(|index| interpolated_coefficients.evaluate_at(&Scalar::from(*index)))
|
||||
.map(|receiver_index| interpolated_coefficients.evaluate_at(&Scalar::from(*receiver_index)))
|
||||
.collect();
|
||||
|
||||
Ok(RecoveredVerificationKeys {
|
||||
@@ -457,14 +471,17 @@ mod tests {
|
||||
let dealings = node_indices
|
||||
.iter()
|
||||
.map(|&dealer_index| {
|
||||
Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0
|
||||
(
|
||||
dealer_index,
|
||||
Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
let mut derived_secrets = Vec::new();
|
||||
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
|
||||
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
|
||||
let shares = dealings
|
||||
.iter()
|
||||
.values()
|
||||
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
|
||||
.collect();
|
||||
derived_secrets.push(
|
||||
@@ -513,9 +530,12 @@ mod tests {
|
||||
let dealings = node_indices
|
||||
.iter()
|
||||
.map(|&dealer_index| {
|
||||
Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0
|
||||
(
|
||||
dealer_index,
|
||||
Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
let RecoveredVerificationKeys {
|
||||
recovered_master,
|
||||
@@ -531,6 +551,66 @@ mod tests {
|
||||
.is_ok())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // expensive test
|
||||
fn verifying_partial_verification_keys_with_different_dealers_and_receivers() {
|
||||
let dummy_seed = [42u8; 32];
|
||||
let mut rng = rand_chacha::ChaCha20Rng::from_seed(dummy_seed);
|
||||
let params = setup();
|
||||
|
||||
let dealer_indices = [1, 2, 3, 8];
|
||||
let receiver_indices = [3, 4, 5, 6, 7];
|
||||
let threshold = 3;
|
||||
|
||||
let mut receivers = BTreeMap::new();
|
||||
let mut full_keys = Vec::new();
|
||||
for index in &receiver_indices {
|
||||
let (dk, pk) = keygen(¶ms, &mut rng);
|
||||
receivers.insert(*index, *pk.public_key());
|
||||
full_keys.push((dk, pk))
|
||||
}
|
||||
|
||||
let dealings = dealer_indices
|
||||
.iter()
|
||||
.map(|&dealer_index| {
|
||||
(
|
||||
dealer_index,
|
||||
Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0,
|
||||
)
|
||||
})
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
let RecoveredVerificationKeys {
|
||||
recovered_master,
|
||||
recovered_partials,
|
||||
} = try_recover_verification_keys(&dealings, threshold, &receivers).unwrap();
|
||||
|
||||
let g2 = G2Projective::generator();
|
||||
|
||||
let mut derived_secrets = Vec::new();
|
||||
for (i, (dk, _)) in full_keys.iter().enumerate() {
|
||||
let shares = dealings
|
||||
.values()
|
||||
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
|
||||
.collect();
|
||||
|
||||
let recovered_secret = combine_shares(shares, &dealer_indices).unwrap();
|
||||
|
||||
// make sure it matches the associated vk
|
||||
assert_eq!(recovered_partials[i], g2 * recovered_secret);
|
||||
|
||||
derived_secrets.push(recovered_secret)
|
||||
}
|
||||
|
||||
assert!(verify_verification_keys(
|
||||
&recovered_master,
|
||||
&recovered_partials,
|
||||
&receivers,
|
||||
threshold
|
||||
)
|
||||
.is_ok())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // expensive test
|
||||
fn dealing_roundtrip() {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[derive(Debug, Error, Clone)]
|
||||
pub enum DkgError {
|
||||
#[error("Provided set of values contained duplicate coordinate")]
|
||||
DuplicateCoordinate,
|
||||
|
||||
@@ -13,6 +13,7 @@ pub mod dealing;
|
||||
pub(crate) mod share;
|
||||
pub(crate) mod utils;
|
||||
|
||||
pub use bls12_381::{G2Projective, Scalar};
|
||||
pub use dealing::*;
|
||||
pub use share::*;
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ fn single_sender() {
|
||||
.unwrap();
|
||||
|
||||
// make sure each share is actually decryptable (even though proofs say they must be, perform this sanity check)
|
||||
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
|
||||
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
|
||||
let _recovered = decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap();
|
||||
}
|
||||
|
||||
@@ -91,10 +91,13 @@ fn full_threshold_secret_sharing() {
|
||||
let dealings = node_indices
|
||||
.iter()
|
||||
.map(|&dealer_index| {
|
||||
Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0
|
||||
(
|
||||
dealer_index,
|
||||
Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
for dealing in dealings.iter() {
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
for dealing in dealings.values() {
|
||||
dealing
|
||||
.verify(¶ms, threshold, &receivers, None)
|
||||
.unwrap();
|
||||
@@ -109,9 +112,9 @@ fn full_threshold_secret_sharing() {
|
||||
let g2 = G2Projective::generator();
|
||||
|
||||
let mut derived_secrets = Vec::new();
|
||||
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
|
||||
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
|
||||
let shares = dealings
|
||||
.iter()
|
||||
.values()
|
||||
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
|
||||
.collect();
|
||||
|
||||
@@ -169,9 +172,12 @@ fn full_threshold_secret_resharing() {
|
||||
let first_dealings = node_indices
|
||||
.iter()
|
||||
.map(|&dealer_index| {
|
||||
Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0
|
||||
(
|
||||
dealer_index,
|
||||
Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
// recover verification keys
|
||||
let RecoveredVerificationKeys {
|
||||
@@ -180,9 +186,9 @@ fn full_threshold_secret_resharing() {
|
||||
} = try_recover_verification_keys(&first_dealings, threshold, &receivers).unwrap();
|
||||
|
||||
let mut derived_secrets = Vec::new();
|
||||
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
|
||||
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
|
||||
let shares = first_dealings
|
||||
.iter()
|
||||
.values()
|
||||
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
|
||||
.collect();
|
||||
|
||||
@@ -203,19 +209,22 @@ fn full_threshold_secret_resharing() {
|
||||
.iter()
|
||||
.zip(derived_secrets.iter())
|
||||
.map(|(&dealer_index, prior_secret)| {
|
||||
Dealing::create(
|
||||
&mut rng,
|
||||
¶ms,
|
||||
(
|
||||
dealer_index,
|
||||
threshold,
|
||||
&receivers,
|
||||
Some(*prior_secret),
|
||||
Dealing::create(
|
||||
&mut rng,
|
||||
¶ms,
|
||||
dealer_index,
|
||||
threshold,
|
||||
&receivers,
|
||||
Some(*prior_secret),
|
||||
)
|
||||
.0,
|
||||
)
|
||||
.0
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
for (reshared_dealing, prior_vk) in resharing_dealings.iter().zip(recovered_partials.iter()) {
|
||||
for (reshared_dealing, prior_vk) in resharing_dealings.values().zip(recovered_partials.iter()) {
|
||||
reshared_dealing
|
||||
.verify(¶ms, threshold, &receivers, Some(*prior_vk))
|
||||
.unwrap();
|
||||
@@ -228,9 +237,9 @@ fn full_threshold_secret_resharing() {
|
||||
} = try_recover_verification_keys(&resharing_dealings, threshold, &receivers).unwrap();
|
||||
|
||||
let mut reshared_secrets = Vec::new();
|
||||
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
|
||||
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
|
||||
let shares = resharing_dealings
|
||||
.iter()
|
||||
.values()
|
||||
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
|
||||
.collect();
|
||||
|
||||
@@ -279,9 +288,12 @@ fn full_threshold_secret_resharing_left_party() {
|
||||
let first_dealings = node_indices
|
||||
.iter()
|
||||
.map(|&dealer_index| {
|
||||
Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0
|
||||
(
|
||||
dealer_index,
|
||||
Dealing::create(&mut rng, ¶ms, dealer_index, threshold, &receivers, None).0,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
// recover verification keys
|
||||
let RecoveredVerificationKeys {
|
||||
@@ -290,9 +302,9 @@ fn full_threshold_secret_resharing_left_party() {
|
||||
} = try_recover_verification_keys(&first_dealings, threshold, &receivers).unwrap();
|
||||
|
||||
let mut derived_secrets = Vec::new();
|
||||
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
|
||||
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
|
||||
let shares = first_dealings
|
||||
.iter()
|
||||
.values()
|
||||
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
|
||||
.collect();
|
||||
|
||||
@@ -323,20 +335,23 @@ fn full_threshold_secret_resharing_left_party() {
|
||||
.iter()
|
||||
.zip(derived_secrets.iter().take(2))
|
||||
.map(|(&dealer_index, prior_secret)| {
|
||||
Dealing::create(
|
||||
&mut rng,
|
||||
¶ms,
|
||||
(
|
||||
dealer_index,
|
||||
threshold,
|
||||
&receivers,
|
||||
Some(*prior_secret),
|
||||
Dealing::create(
|
||||
&mut rng,
|
||||
¶ms,
|
||||
dealer_index,
|
||||
threshold,
|
||||
&receivers,
|
||||
Some(*prior_secret),
|
||||
)
|
||||
.0,
|
||||
)
|
||||
.0
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
for (reshared_dealing, prior_vk) in resharing_dealings
|
||||
.iter()
|
||||
.values()
|
||||
.zip(recovered_partials.iter().take(2))
|
||||
{
|
||||
reshared_dealing
|
||||
@@ -351,9 +366,9 @@ fn full_threshold_secret_resharing_left_party() {
|
||||
} = try_recover_verification_keys(&resharing_dealings, threshold, &receivers).unwrap();
|
||||
|
||||
let mut reshared_secrets = Vec::new();
|
||||
for (i, (ref mut dk, _)) in full_keys.iter_mut().enumerate() {
|
||||
for (i, (ref dk, _)) in full_keys.iter().enumerate() {
|
||||
let shares = resharing_dealings
|
||||
.iter()
|
||||
.values()
|
||||
.map(|dealing| decrypt_share(dk, i, &dealing.ciphertexts, None).unwrap())
|
||||
.collect();
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ impl NymNetworkDetails {
|
||||
.with_additional_validator_endpoint(ValidatorDetails::new(
|
||||
var(var_names::NYXD).expect("nyxd validator not set"),
|
||||
Some(var(var_names::NYM_API).expect("nym api not set")),
|
||||
get_optional_env(var_names::NYXD_WEBSOCKET),
|
||||
))
|
||||
.with_mixnet_contract(Some(
|
||||
var(var_names::MIXNET_CONTRACT_ADDRESS).expect("mixnet contract not set"),
|
||||
@@ -340,6 +341,9 @@ impl DenomDetailsOwned {
|
||||
pub struct ValidatorDetails {
|
||||
// it is assumed those values are always valid since they're being provided in our defaults file
|
||||
pub nyxd_url: String,
|
||||
//
|
||||
pub websocket_url: Option<String>,
|
||||
|
||||
// Right now api_url is optional as we are not running the api reliably on all validators
|
||||
// however, later on it should be a mandatory field
|
||||
pub api_url: Option<String>,
|
||||
@@ -347,9 +351,10 @@ pub struct ValidatorDetails {
|
||||
}
|
||||
|
||||
impl ValidatorDetails {
|
||||
pub fn new<S: Into<String>>(nyxd_url: S, api_url: Option<S>) -> Self {
|
||||
pub fn new<S: Into<String>>(nyxd_url: S, api_url: Option<S>, websocket_url: Option<S>) -> Self {
|
||||
ValidatorDetails {
|
||||
nyxd_url: nyxd_url.into(),
|
||||
websocket_url: websocket_url.map(Into::into),
|
||||
api_url: api_url.map(Into::into),
|
||||
}
|
||||
}
|
||||
@@ -357,6 +362,7 @@ impl ValidatorDetails {
|
||||
pub fn new_nyxd_only<S: Into<String>>(nyxd_url: S) -> Self {
|
||||
ValidatorDetails {
|
||||
nyxd_url: nyxd_url.into(),
|
||||
websocket_url: None,
|
||||
api_url: None,
|
||||
}
|
||||
}
|
||||
@@ -372,6 +378,12 @@ impl ValidatorDetails {
|
||||
.as_ref()
|
||||
.map(|url| url.parse().expect("the provided api url is invalid!"))
|
||||
}
|
||||
|
||||
pub fn websocket_url(&self) -> Option<Url> {
|
||||
self.websocket_url
|
||||
.as_ref()
|
||||
.map(|url| url.parse().expect("the provided websocket url is invalid!"))
|
||||
}
|
||||
}
|
||||
|
||||
fn fix_deprecated_environmental_variables() {
|
||||
|
||||
@@ -26,6 +26,7 @@ pub const REWARDING_VALIDATOR_ADDRESS: &str = "n10yyd98e2tuwu0f7ypz9dy3hhjw7v772
|
||||
pub const STATISTICS_SERVICE_DOMAIN_ADDRESS: &str = "https://mainnet-stats.nymte.ch:8090/";
|
||||
pub const NYXD_URL: &str = "https://rpc.nymtech.net";
|
||||
pub const NYM_API: &str = "https://validator.nymtech.net/api/";
|
||||
pub const NYXD_WS: &str = "wss://rpc.nymtech.net/websocket";
|
||||
pub const EXPLORER_API: &str = "https://explorer.nymtech.net/api/";
|
||||
|
||||
// I'm making clippy mad on purpose, because that url HAS TO be updated and deployed before merging
|
||||
@@ -33,7 +34,11 @@ pub const EXIT_POLICY_URL: &str =
|
||||
"https://nymtech.net/.wellknown/network-requester/exit-policy.txt";
|
||||
|
||||
pub(crate) fn validators() -> Vec<ValidatorDetails> {
|
||||
vec![ValidatorDetails::new(NYXD_URL, Some(NYM_API))]
|
||||
vec![ValidatorDetails::new(
|
||||
NYXD_URL,
|
||||
Some(NYM_API),
|
||||
Some(NYXD_WS),
|
||||
)]
|
||||
}
|
||||
|
||||
const DEFAULT_SUFFIX: &str = "_MAINNET_DEFAULT";
|
||||
@@ -111,6 +116,7 @@ pub fn export_to_env() {
|
||||
);
|
||||
set_var_to_default(var_names::NYXD, NYXD_URL);
|
||||
set_var_to_default(var_names::NYM_API, NYM_API);
|
||||
set_var_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
|
||||
set_var_to_default(var_names::EXPLORER_API, EXPLORER_API);
|
||||
set_var_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
|
||||
}
|
||||
@@ -159,6 +165,7 @@ pub fn export_to_env_if_not_set() {
|
||||
);
|
||||
set_var_conditionally_to_default(var_names::NYXD, NYXD_URL);
|
||||
set_var_conditionally_to_default(var_names::NYM_API, NYM_API);
|
||||
set_var_conditionally_to_default(var_names::NYXD_WEBSOCKET, NYXD_WS);
|
||||
set_var_conditionally_to_default(var_names::EXPLORER_API, EXPLORER_API);
|
||||
set_var_conditionally_to_default(var_names::EXIT_POLICY_URL, EXIT_POLICY_URL);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ pub const SERVICE_PROVIDER_DIRECTORY_CONTRACT_ADDRESS: &str =
|
||||
pub const NAME_SERVICE_CONTRACT_ADDRESS: &str = "NAME_SERVICE_CONTRACT_ADDRESS";
|
||||
pub const NYXD: &str = "NYXD";
|
||||
pub const NYM_API: &str = "NYM_API";
|
||||
pub const NYXD_WEBSOCKET: &str = "NYXD_WS";
|
||||
pub const EXPLORER_API: &str = "EXPLORER_API";
|
||||
pub const EXIT_POLICY_URL: &str = "EXIT_POLICY";
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ fn bench_coconut(c: &mut Criterion) {
|
||||
b.iter(|| {
|
||||
verify_partial_blind_signature(
|
||||
¶ms,
|
||||
&blind_sign_request,
|
||||
blind_sign_request.get_private_attributes_pedersen_commitments(),
|
||||
&public_attributes,
|
||||
random_blind_signature,
|
||||
partial_verification_key,
|
||||
|
||||
@@ -43,3 +43,5 @@ mod utils;
|
||||
pub type Attribute = bls12_381::Scalar;
|
||||
pub type PrivateAttribute = Attribute;
|
||||
pub type PublicAttribute = Attribute;
|
||||
|
||||
pub use bls12_381::G1Projective;
|
||||
|
||||
@@ -362,12 +362,12 @@ pub fn blind_sign(
|
||||
/// The function returns `true` if the partial blind signature is valid, and `false` otherwise.
|
||||
pub fn verify_partial_blind_signature(
|
||||
params: &Parameters,
|
||||
blind_sign_request: &BlindSignRequest,
|
||||
private_attribute_commitments: &[G1Projective],
|
||||
public_attributes: &[&Attribute],
|
||||
blind_sig: &BlindedSignature,
|
||||
partial_verification_key: &VerificationKey,
|
||||
) -> bool {
|
||||
let num_private_attributes = blind_sign_request.private_attributes_commitments.len();
|
||||
let num_private_attributes = private_attribute_commitments.len();
|
||||
if num_private_attributes + public_attributes.len() > partial_verification_key.beta_g2.len() {
|
||||
return false;
|
||||
}
|
||||
@@ -388,8 +388,7 @@ pub fn verify_partial_blind_signature(
|
||||
];
|
||||
|
||||
// for each private attribute, add (cm_i, beta_i) to the miller terms
|
||||
for (private_attr_commit, beta_g2) in blind_sign_request
|
||||
.private_attributes_commitments
|
||||
for (private_attr_commit, beta_g2) in private_attribute_commitments
|
||||
.iter()
|
||||
.zip(&partial_verification_key.beta_g2)
|
||||
{
|
||||
@@ -422,7 +421,7 @@ pub fn verify_partial_blind_signature(
|
||||
// is equivalent to checking e(a, b) • e(c, d)^{-1} == id
|
||||
// and thus to e(a, b) • e(c^{-1}, d) == id
|
||||
//
|
||||
// compute e(c^1, g2) • e(s, alpha) • e(cm_0, beta_0) • e(cm_i, beta_i) • (s^pub_0, beta_{i+1}) (s^pub_j, beta_{i + j})
|
||||
// compute e(c^{-1}, g2) • e(s, alpha) • e(cm_0, beta_0) • e(cm_i, beta_i) • (s^pub_0, beta_{i+1}) (s^pub_j, beta_{i + j})
|
||||
multi_miller_loop(&terms_refs)
|
||||
.final_exponentiation()
|
||||
.is_identity()
|
||||
@@ -519,7 +518,7 @@ mod tests {
|
||||
|
||||
assert!(verify_partial_blind_signature(
|
||||
¶ms,
|
||||
&request,
|
||||
&request.private_attributes_commitments,
|
||||
&public_attributes,
|
||||
&blind_sig,
|
||||
validator_keypair.verification_key()
|
||||
@@ -539,7 +538,7 @@ mod tests {
|
||||
|
||||
assert!(verify_partial_blind_signature(
|
||||
¶ms,
|
||||
&request,
|
||||
&request.private_attributes_commitments,
|
||||
&[],
|
||||
&blind_sig,
|
||||
validator_keypair.verification_key()
|
||||
@@ -568,7 +567,7 @@ mod tests {
|
||||
// this assertion should fail, as we try to verify with a wrong validator key
|
||||
assert!(!verify_partial_blind_signature(
|
||||
¶ms,
|
||||
&request,
|
||||
&request.private_attributes_commitments,
|
||||
&public_attributes,
|
||||
&blind_sig,
|
||||
validator2_keypair.verification_key()
|
||||
|
||||
@@ -99,7 +99,9 @@ impl SecretKey {
|
||||
Self { x, ys }
|
||||
}
|
||||
|
||||
pub fn into_raw(&self) -> (Scalar, Vec<Scalar>) {
|
||||
/// Extract the Scalar copy of the underlying secrets.
|
||||
/// The caller of this function must exercise extreme care to not misuse the data and ensuring it gets zeroized
|
||||
pub fn hazmat_to_raw(&self) -> (Scalar, Vec<Scalar>) {
|
||||
(self.x, self.ys.clone())
|
||||
}
|
||||
|
||||
|
||||
@@ -228,10 +228,11 @@ pub fn check_vk_pairing(
|
||||
|
||||
// safety: we made an explicit check for if the length of the slice is 0, thus unwrap here is fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
if &vk.alpha != *dkg_values.last().as_ref().unwrap() {
|
||||
if &vk.alpha != *dkg_values.first().as_ref().unwrap() {
|
||||
return false;
|
||||
}
|
||||
if dkg_values
|
||||
let dkg_betas = &dkg_values[1..];
|
||||
if dkg_betas
|
||||
.iter()
|
||||
.zip(vk.beta_g2.iter())
|
||||
.any(|(dkg_beta, vk_beta)| dkg_beta != vk_beta)
|
||||
@@ -329,8 +330,9 @@ mod tests {
|
||||
let params = setup(2).unwrap();
|
||||
let keypair = keygen(¶ms);
|
||||
let vk = keypair.verification_key();
|
||||
let mut dkg_values = vk.beta_g2.clone();
|
||||
dkg_values.push(vk.alpha);
|
||||
|
||||
let mut dkg_values = vec![vk.alpha];
|
||||
dkg_values.append(&mut vk.beta_g2.clone());
|
||||
assert!(check_vk_pairing(¶ms, &dkg_values, vk));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,10 @@ use crate::storage::{persist_block, ScraperStorage};
|
||||
use futures::StreamExt;
|
||||
use std::collections::{BTreeMap, HashSet, VecDeque};
|
||||
use std::ops::{Add, Range};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc::{Sender, UnboundedReceiver};
|
||||
use tokio::sync::Notify;
|
||||
use tokio::time::{interval_at, Instant};
|
||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
@@ -39,6 +41,7 @@ impl PendingSync {
|
||||
|
||||
pub struct BlockProcessor {
|
||||
cancel: CancellationToken,
|
||||
synced: Arc<Notify>,
|
||||
last_processed_height: u32,
|
||||
last_processed_at: Instant,
|
||||
pending_sync: PendingSync,
|
||||
@@ -60,6 +63,7 @@ pub struct BlockProcessor {
|
||||
impl BlockProcessor {
|
||||
pub async fn new(
|
||||
cancel: CancellationToken,
|
||||
synced: Arc<Notify>,
|
||||
incoming: UnboundedReceiver<BlockToProcess>,
|
||||
block_requester: Sender<BlockRequest>,
|
||||
storage: ScraperStorage,
|
||||
@@ -69,6 +73,7 @@ impl BlockProcessor {
|
||||
|
||||
Ok(BlockProcessor {
|
||||
cancel,
|
||||
synced,
|
||||
last_processed_height: last_processed.try_into().unwrap_or_default(),
|
||||
last_processed_at: Instant::now(),
|
||||
pending_sync: Default::default(),
|
||||
@@ -243,6 +248,10 @@ impl BlockProcessor {
|
||||
}
|
||||
|
||||
self.try_request_pending().await;
|
||||
|
||||
if self.pending_sync.is_empty() {
|
||||
self.synced.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_request_pending(&mut self) -> bool {
|
||||
@@ -294,7 +303,7 @@ impl BlockProcessor {
|
||||
error!("failed to perform startup sync: {err}");
|
||||
self.cancel.cancel();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
|
||||
@@ -16,3 +16,4 @@ pub mod storage;
|
||||
|
||||
pub use modules::{BlockModule, MsgModule, TxModule};
|
||||
pub use scraper::{Config, NyxdScraper};
|
||||
pub use storage::models;
|
||||
|
||||
@@ -9,8 +9,10 @@ use crate::rpc_client::RpcClient;
|
||||
use crate::scraper::subscriber::{run_websocket_driver, ChainSubscriber};
|
||||
use crate::storage::ScraperStorage;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tendermint_rpc::WebSocketClientDriver;
|
||||
use tokio::sync::mpsc::{channel, unbounded_channel};
|
||||
use tokio::sync::Notify;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tokio_util::task::TaskTracker;
|
||||
use tracing::info;
|
||||
@@ -54,6 +56,7 @@ impl NyxdScraperBuilder {
|
||||
);
|
||||
let mut block_processor = BlockProcessor::new(
|
||||
scraper.cancel_token.clone(),
|
||||
scraper.startup_sync.clone(),
|
||||
processing_rx,
|
||||
req_tx,
|
||||
scraper.storage.clone(),
|
||||
@@ -112,7 +115,8 @@ pub struct NyxdScraper {
|
||||
|
||||
task_tracker: TaskTracker,
|
||||
cancel_token: CancellationToken,
|
||||
storage: ScraperStorage,
|
||||
startup_sync: Arc<Notify>,
|
||||
pub storage: ScraperStorage,
|
||||
}
|
||||
|
||||
impl NyxdScraper {
|
||||
@@ -127,6 +131,7 @@ impl NyxdScraper {
|
||||
config,
|
||||
task_tracker: TaskTracker::new(),
|
||||
cancel_token: CancellationToken::new(),
|
||||
startup_sync: Arc::new(Default::default()),
|
||||
storage,
|
||||
})
|
||||
}
|
||||
@@ -164,6 +169,7 @@ impl NyxdScraper {
|
||||
);
|
||||
let block_processor = BlockProcessor::new(
|
||||
self.cancel_token.clone(),
|
||||
self.startup_sync.clone(),
|
||||
processing_rx,
|
||||
req_tx,
|
||||
self.storage.clone(),
|
||||
@@ -189,8 +195,15 @@ impl NyxdScraper {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn wait_for_startup_sync(&self) {
|
||||
info!("awaiting startup chain sync");
|
||||
self.startup_sync.notified().await
|
||||
}
|
||||
|
||||
pub async fn stop(self) {
|
||||
info!("stopping the chain scrapper");
|
||||
assert!(self.task_tracker.is_closed());
|
||||
|
||||
self.cancel_token.cancel();
|
||||
self.task_tracker.wait().await
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::storage::models::Validator;
|
||||
use crate::storage::models::{CommitSignature, Validator};
|
||||
use sqlx::types::time::OffsetDateTime;
|
||||
use sqlx::{Executor, Sqlite};
|
||||
use tracing::{instrument, trace};
|
||||
@@ -84,8 +84,8 @@ impl StorageManager {
|
||||
SELECT COUNT(*) as count FROM pre_commit
|
||||
WHERE
|
||||
validator_address == ?
|
||||
AND height > ?
|
||||
AND height < ?
|
||||
AND height >= ?
|
||||
AND height <= ?
|
||||
"#,
|
||||
consensus_address,
|
||||
start_height,
|
||||
@@ -98,6 +98,24 @@ impl StorageManager {
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
pub(crate) async fn get_precommit(
|
||||
&self,
|
||||
consensus_address: &str,
|
||||
height: i64,
|
||||
) -> Result<Option<CommitSignature>, sqlx::Error> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT * FROM pre_commit
|
||||
WHERE validator_address = ?
|
||||
AND height = ?
|
||||
"#,
|
||||
)
|
||||
.bind(consensus_address)
|
||||
.bind(height)
|
||||
.fetch_optional(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_block_validators(
|
||||
&self,
|
||||
height: i64,
|
||||
@@ -118,6 +136,12 @@ impl StorageManager {
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_validators(&self) -> Result<Vec<Validator>, sqlx::Error> {
|
||||
sqlx::query_as("SELECT * FROM validator")
|
||||
.fetch_all(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(crate) async fn get_last_processed_height(&self) -> Result<i64, sqlx::Error> {
|
||||
let maybe_record = sqlx::query!(
|
||||
r#"
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::storage::manager::{
|
||||
insert_block, insert_message, insert_precommit, insert_transaction, insert_validator,
|
||||
update_last_processed, StorageManager,
|
||||
};
|
||||
use crate::storage::models::Validator;
|
||||
use crate::storage::models::{CommitSignature, Validator};
|
||||
use sqlx::types::time::OffsetDateTime;
|
||||
use sqlx::{ConnectOptions, Sqlite, Transaction};
|
||||
use std::fmt::Debug;
|
||||
@@ -19,7 +19,7 @@ use tracing::{debug, error, info, instrument, trace, warn};
|
||||
|
||||
mod helpers;
|
||||
mod manager;
|
||||
mod models;
|
||||
pub mod models;
|
||||
|
||||
pub type StorageTransaction = Transaction<'static, Sqlite>;
|
||||
|
||||
@@ -31,8 +31,6 @@ pub struct ScraperStorage {
|
||||
impl ScraperStorage {
|
||||
#[instrument]
|
||||
pub async fn init<P: AsRef<Path> + Debug>(database_path: P) -> Result<Self, ScraperError> {
|
||||
// TODO: we can inject here more stuff based on our nym-api global config
|
||||
// struct. Maybe different pool size or timeout intervals?
|
||||
let mut opts = sqlx::sqlite::SqliteConnectOptions::new()
|
||||
.filename(database_path)
|
||||
.create_if_missing(true);
|
||||
@@ -91,6 +89,21 @@ impl ScraperStorage {
|
||||
Ok(self.manager.get_last_block_height_before(time).await?)
|
||||
}
|
||||
|
||||
pub async fn get_blocks_between(
|
||||
&self,
|
||||
start_time: OffsetDateTime,
|
||||
end_time: OffsetDateTime,
|
||||
) -> Result<i64, ScraperError> {
|
||||
let Some(block_start) = self.get_first_block_height_after(start_time).await? else {
|
||||
return Ok(0);
|
||||
};
|
||||
let Some(block_end) = self.get_last_block_height_before(end_time).await? else {
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
Ok(block_end - block_start)
|
||||
}
|
||||
|
||||
pub async fn get_signed_between(
|
||||
&self,
|
||||
consensus_address: &str,
|
||||
@@ -120,10 +133,25 @@ impl ScraperStorage {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_precommit(
|
||||
&self,
|
||||
consensus_address: &str,
|
||||
height: i64,
|
||||
) -> Result<Option<CommitSignature>, ScraperError> {
|
||||
Ok(self
|
||||
.manager
|
||||
.get_precommit(consensus_address, height)
|
||||
.await?)
|
||||
}
|
||||
|
||||
pub async fn get_block_signers(&self, height: i64) -> Result<Vec<Validator>, ScraperError> {
|
||||
Ok(self.manager.get_block_validators(height).await?)
|
||||
}
|
||||
|
||||
pub async fn get_all_known_validators(&self) -> Result<Vec<Validator>, ScraperError> {
|
||||
Ok(self.manager.get_validators().await?)
|
||||
}
|
||||
|
||||
pub async fn get_last_processed_height(&self) -> Result<i64, ScraperError> {
|
||||
Ok(self.manager.get_last_processed_height().await?)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use sqlx::types::time::OffsetDateTime;
|
||||
use sqlx::FromRow;
|
||||
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, FromRow)]
|
||||
pub struct Validator {
|
||||
pub consensus_address: String,
|
||||
pub consensus_pubkey: String,
|
||||
|
||||
@@ -116,8 +116,8 @@ impl TaskManager {
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn catch_interrupt(mut self) -> Result<(), SentError> {
|
||||
let res = crate::wait_for_signal_and_error(&mut self).await;
|
||||
pub async fn catch_interrupt(&mut self) -> Result<(), SentError> {
|
||||
let res = crate::wait_for_signal_and_error(self).await;
|
||||
|
||||
log::info!("Sending shutdown");
|
||||
self.signal_shutdown().ok();
|
||||
@@ -629,7 +629,7 @@ impl TaskHandle {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub async fn wait_for_shutdown(self) -> Result<(), SentError> {
|
||||
match self {
|
||||
TaskHandle::Internal(task_manager) => task_manager.catch_interrupt().await,
|
||||
TaskHandle::Internal(mut task_manager) => task_manager.catch_interrupt().await,
|
||||
TaskHandle::External(mut task_client) => {
|
||||
task_client.recv().await;
|
||||
Ok(())
|
||||
|
||||
Generated
+6
-3
@@ -1220,12 +1220,13 @@ dependencies = [
|
||||
"cw-controllers",
|
||||
"cw-multi-test",
|
||||
"cw-storage-plus",
|
||||
"cw2",
|
||||
"cw4",
|
||||
"cw4-group",
|
||||
"lazy_static",
|
||||
"nym-coconut-dkg-common",
|
||||
"nym-group-contract-common",
|
||||
"rusty-fork",
|
||||
"semver",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
@@ -1237,6 +1238,8 @@ dependencies = [
|
||||
"cosmwasm-schema",
|
||||
"cosmwasm-std",
|
||||
"cw-utils",
|
||||
"cw2",
|
||||
"cw4",
|
||||
"nym-contracts-common",
|
||||
"nym-multisig-contract-common",
|
||||
]
|
||||
@@ -1879,9 +1882,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.17"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
|
||||
@@ -48,5 +48,6 @@ cw3 = "=1.1.0"
|
||||
cw3-fixed-multisig = "=1.1.0"
|
||||
cw4 = "=1.1.0"
|
||||
cw20 = "=1.1.0"
|
||||
semver = "1.0.21"
|
||||
|
||||
thiserror = "1.0.48"
|
||||
|
||||
@@ -20,15 +20,16 @@ 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"] }
|
||||
semver = { workspace = true, default-features = false }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
cw-multi-test = { workspace = true }
|
||||
cw4-group = { path = "../multisig/cw4-group" }
|
||||
nym-group-contract-common = { path = "../../common/cosmwasm-smart-contracts/group-contract" }
|
||||
lazy_static = "1.4"
|
||||
rusty-fork = "0.3"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group_addr",
|
||||
"key_size",
|
||||
"mix_denom",
|
||||
"multisig_addr"
|
||||
],
|
||||
@@ -15,6 +16,12 @@
|
||||
"group_addr": {
|
||||
"type": "string"
|
||||
},
|
||||
"key_size": {
|
||||
"description": "Specifies the number of elements in the derived keys",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"mix_denom": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -84,6 +91,19 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ExecuteMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"initiate_dkg"
|
||||
],
|
||||
"properties": {
|
||||
"initiate_dkg": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -95,6 +115,7 @@
|
||||
"required": [
|
||||
"announce_address",
|
||||
"bte_key_with_proof",
|
||||
"identity_key",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
@@ -104,6 +125,9 @@
|
||||
"bte_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"identity_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
@@ -122,12 +146,12 @@
|
||||
"commit_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealing_bytes",
|
||||
"dealing",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"dealing_bytes": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
"dealing": {
|
||||
"$ref": "#/definitions/PartialContractDealing"
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
@@ -227,6 +251,24 @@
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"PartialContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -234,6 +276,19 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "QueryMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_state"
|
||||
],
|
||||
"properties": {
|
||||
"get_state": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -352,6 +407,39 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealing_status"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealing_status": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -361,10 +449,47 @@
|
||||
"get_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"idx"
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"idx": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealings"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealings": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
@@ -379,9 +504,38 @@
|
||||
},
|
||||
"start_after": {
|
||||
"type": [
|
||||
"string",
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_verification_key"
|
||||
],
|
||||
"properties": {
|
||||
"get_verification_key": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"epoch_id",
|
||||
"owner"
|
||||
],
|
||||
"properties": {
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"owner": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -425,6 +579,20 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_cw2_contract_version"
|
||||
],
|
||||
"properties": {
|
||||
"get_cw2_contract_version": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -436,6 +604,26 @@
|
||||
},
|
||||
"sudo": null,
|
||||
"responses": {
|
||||
"get_c_w2_contract_version": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ContractVersion",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"contract",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"contract": {
|
||||
"description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing",
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"get_current_dealers": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedDealerResponse",
|
||||
@@ -480,7 +668,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -496,6 +685,9 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -508,7 +700,6 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"epoch_id",
|
||||
"finish_timestamp",
|
||||
"state",
|
||||
"time_configuration"
|
||||
],
|
||||
@@ -519,7 +710,14 @@
|
||||
"minimum": 0.0
|
||||
},
|
||||
"finish_timestamp": {
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/EpochState"
|
||||
@@ -535,6 +733,7 @@
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"waiting_initialisation",
|
||||
"in_progress"
|
||||
]
|
||||
},
|
||||
@@ -744,7 +943,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -760,6 +960,9 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -776,34 +979,36 @@
|
||||
},
|
||||
"get_dealing": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedDealingsResponse",
|
||||
"title": "DealingResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealings",
|
||||
"per_page"
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ContractDealing"
|
||||
}
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"per_page": {
|
||||
"type": "integer",
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_next_after": {
|
||||
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
|
||||
"dealing": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Addr"
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -812,21 +1017,91 @@
|
||||
"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"
|
||||
},
|
||||
"ContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing"
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_dealing_status": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealingStatusResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"dealing_submitted",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealing_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_dealings": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedDealingsResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealings",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PartialContractDealing"
|
||||
}
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_next_after": {
|
||||
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
@@ -835,6 +1110,24 @@
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"PartialContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -921,7 +1214,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -937,6 +1231,124 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_state": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "State",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group_addr",
|
||||
"key_size",
|
||||
"mix_denom",
|
||||
"multisig_addr"
|
||||
],
|
||||
"properties": {
|
||||
"group_addr": {
|
||||
"$ref": "#/definitions/Cw4Contract"
|
||||
},
|
||||
"key_size": {
|
||||
"description": "Specifies the number of elements in the derived keys",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"mix_denom": {
|
||||
"type": "string"
|
||||
},
|
||||
"multisig_addr": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"Cw4Contract": {
|
||||
"description": "Cw4Contract is a wrapper around Addr that provides a lot of helpers for working with cw4 contracts\n\nIf you wish to persist this, convert to Cw4CanonicalContract via .canonical()",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_verification_key": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "VkShareResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"epoch_id",
|
||||
"owner"
|
||||
],
|
||||
"properties": {
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"share": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ContractVKShare"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
"ContractVKShare": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"announce_address",
|
||||
"epoch_id",
|
||||
"node_index",
|
||||
"owner",
|
||||
"share",
|
||||
"verified"
|
||||
],
|
||||
"properties": {
|
||||
"announce_address": {
|
||||
"type": "string"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"node_index": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"share": {
|
||||
"type": "string"
|
||||
},
|
||||
"verified": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -2,6 +2,19 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ExecuteMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"initiate_dkg"
|
||||
],
|
||||
"properties": {
|
||||
"initiate_dkg": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -13,6 +26,7 @@
|
||||
"required": [
|
||||
"announce_address",
|
||||
"bte_key_with_proof",
|
||||
"identity_key",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
@@ -22,6 +36,9 @@
|
||||
"bte_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"identity_key": {
|
||||
"type": "string"
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
}
|
||||
@@ -40,12 +57,12 @@
|
||||
"commit_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealing_bytes",
|
||||
"dealing",
|
||||
"resharing"
|
||||
],
|
||||
"properties": {
|
||||
"dealing_bytes": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
"dealing": {
|
||||
"$ref": "#/definitions/PartialContractDealing"
|
||||
},
|
||||
"resharing": {
|
||||
"type": "boolean"
|
||||
@@ -145,6 +162,24 @@
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"PartialContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group_addr",
|
||||
"key_size",
|
||||
"mix_denom",
|
||||
"multisig_addr"
|
||||
],
|
||||
@@ -11,6 +12,12 @@
|
||||
"group_addr": {
|
||||
"type": "string"
|
||||
},
|
||||
"key_size": {
|
||||
"description": "Specifies the number of elements in the derived keys",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"mix_denom": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -2,6 +2,19 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "QueryMsg",
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_state"
|
||||
],
|
||||
"properties": {
|
||||
"get_state": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -120,6 +133,39 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealing_status"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealing_status": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -129,10 +175,47 @@
|
||||
"get_dealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"idx"
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"idx": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_dealings"
|
||||
],
|
||||
"properties": {
|
||||
"get_dealings": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"type": "string"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
@@ -147,9 +230,38 @@
|
||||
},
|
||||
"start_after": {
|
||||
"type": [
|
||||
"string",
|
||||
"integer",
|
||||
"null"
|
||||
]
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_verification_key"
|
||||
],
|
||||
"properties": {
|
||||
"get_verification_key": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"epoch_id",
|
||||
"owner"
|
||||
],
|
||||
"properties": {
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"owner": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -193,6 +305,20 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"description": "Gets the stored contract version information that's required by the CW2 spec interface for migrations.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"get_cw2_contract_version"
|
||||
],
|
||||
"properties": {
|
||||
"get_cw2_contract_version": {
|
||||
"type": "object",
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ContractVersion",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"contract",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"contract": {
|
||||
"description": "contract is the crate name of the implementing contract, eg. `crate:cw20-base` we will use other prefixes for other languages, and their standard global namespacing",
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"description": "version is any string that this implementation knows. It may be simple counter \"1\", \"2\". or semantic version on release tags \"v0.7.0\", or some custom feature flag list. the only code that needs to understand the version parsing is code that knows how to migrate from the given contract (and is tied to it's implementation somehow)",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -42,7 +42,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -58,6 +59,9 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
"type": "object",
|
||||
"required": [
|
||||
"epoch_id",
|
||||
"finish_timestamp",
|
||||
"state",
|
||||
"time_configuration"
|
||||
],
|
||||
@@ -15,7 +14,14 @@
|
||||
"minimum": 0.0
|
||||
},
|
||||
"finish_timestamp": {
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Timestamp"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/EpochState"
|
||||
@@ -31,6 +37,7 @@
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"waiting_initialisation",
|
||||
"in_progress"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -48,6 +49,9 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -1,33 +1,35 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedDealingsResponse",
|
||||
"title": "DealingResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealings",
|
||||
"per_page"
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/ContractDealing"
|
||||
}
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"per_page": {
|
||||
"type": "integer",
|
||||
"format": "uint",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_next_after": {
|
||||
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
|
||||
"dealing": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Addr"
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -36,22 +38,6 @@
|
||||
"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"
|
||||
},
|
||||
"ContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "DealingStatusResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealing_index",
|
||||
"dealing_submitted",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealing_index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"dealing_submitted": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "PagedDealingsResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"dealer",
|
||||
"dealings",
|
||||
"epoch_id"
|
||||
],
|
||||
"properties": {
|
||||
"dealer": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"dealings": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/PartialContractDealing"
|
||||
}
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"start_next_after": {
|
||||
"description": "Field indicating paging information for the following queries if the caller wishes to get further entries.",
|
||||
"type": [
|
||||
"integer",
|
||||
"null"
|
||||
],
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"ContractSafeBytes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"format": "uint8",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"PartialContractDealing": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"data",
|
||||
"index"
|
||||
],
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/ContractSafeBytes"
|
||||
},
|
||||
"index": {
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,8 @@
|
||||
"address",
|
||||
"announce_address",
|
||||
"assigned_index",
|
||||
"bte_public_key_with_proof"
|
||||
"bte_public_key_with_proof",
|
||||
"ed25519_identity"
|
||||
],
|
||||
"properties": {
|
||||
"address": {
|
||||
@@ -58,6 +59,9 @@
|
||||
},
|
||||
"bte_public_key_with_proof": {
|
||||
"type": "string"
|
||||
},
|
||||
"ed25519_identity": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "State",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"group_addr",
|
||||
"key_size",
|
||||
"mix_denom",
|
||||
"multisig_addr"
|
||||
],
|
||||
"properties": {
|
||||
"group_addr": {
|
||||
"$ref": "#/definitions/Cw4Contract"
|
||||
},
|
||||
"key_size": {
|
||||
"description": "Specifies the number of elements in the derived keys",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"mix_denom": {
|
||||
"type": "string"
|
||||
},
|
||||
"multisig_addr": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"Addr": {
|
||||
"description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.",
|
||||
"type": "string"
|
||||
},
|
||||
"Cw4Contract": {
|
||||
"description": "Cw4Contract is a wrapper around Addr that provides a lot of helpers for working with cw4 contracts\n\nIf you wish to persist this, convert to Cw4CanonicalContract via .canonical()",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/Addr"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "VkShareResponse",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"epoch_id",
|
||||
"owner"
|
||||
],
|
||||
"properties": {
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"share": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ContractVKShare"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
"ContractVKShare": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"announce_address",
|
||||
"epoch_id",
|
||||
"node_index",
|
||||
"owner",
|
||||
"share",
|
||||
"verified"
|
||||
],
|
||||
"properties": {
|
||||
"announce_address": {
|
||||
"type": "string"
|
||||
},
|
||||
"epoch_id": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"node_index": {
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"owner": {
|
||||
"$ref": "#/definitions/Addr"
|
||||
},
|
||||
"share": {
|
||||
"type": "string"
|
||||
},
|
||||
"verified": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,26 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealers::queries::{
|
||||
query_current_dealers_paged, query_dealer_details, query_past_dealers_paged,
|
||||
};
|
||||
use crate::dealers::transactions::try_add_dealer;
|
||||
use crate::dealings::queries::query_dealings_paged;
|
||||
use crate::dealings::transactions::try_commit_dealings;
|
||||
use crate::dealings::queries::{
|
||||
query_dealer_dealings_status, query_dealing_chunk, query_dealing_chunk_status,
|
||||
query_dealing_metadata, query_dealing_status,
|
||||
};
|
||||
use crate::dealings::transactions::{try_commit_dealings_chunk, try_submit_dealings_metadata};
|
||||
use crate::epoch_state::queries::{
|
||||
query_current_epoch, query_current_epoch_threshold, query_initial_dealers,
|
||||
};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_surpassed_threshold};
|
||||
use crate::epoch_state::transactions::{
|
||||
advance_epoch_state, try_initiate_dkg, try_surpassed_threshold,
|
||||
};
|
||||
use crate::error::ContractError;
|
||||
use crate::state::{State, MULTISIG, STATE};
|
||||
use crate::verification_key_shares::queries::query_vk_shares_paged;
|
||||
use crate::state::queries::query_state;
|
||||
use crate::state::storage::{DKG_ADMIN, MULTISIG, STATE};
|
||||
use crate::verification_key_shares::queries::{query_vk_share, query_vk_shares_paged};
|
||||
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::{
|
||||
@@ -22,7 +28,11 @@ use cosmwasm_std::{
|
||||
};
|
||||
use cw4::Cw4Contract;
|
||||
use nym_coconut_dkg_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochState};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochState, State};
|
||||
use semver::Version;
|
||||
|
||||
const CONTRACT_NAME: &str = "crate:nym-coconut-dkg";
|
||||
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
/// Instantiate the contract.
|
||||
///
|
||||
@@ -33,13 +43,15 @@ use nym_coconut_dkg_common::types::{Epoch, EpochState};
|
||||
pub fn instantiate(
|
||||
mut deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
_info: MessageInfo,
|
||||
info: MessageInfo,
|
||||
msg: InstantiateMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
let multisig_addr = deps.api.addr_validate(&msg.multisig_addr)?;
|
||||
MULTISIG.set(deps.branch(), Some(multisig_addr.clone()))?;
|
||||
|
||||
let group_addr = Cw4Contract(deps.api.addr_validate(&msg.group_addr).map_err(|_| {
|
||||
DKG_ADMIN.set(deps.branch(), Some(info.sender))?;
|
||||
|
||||
let group_addr = Cw4Contract::new(deps.api.addr_validate(&msg.group_addr).map_err(|_| {
|
||||
ContractError::InvalidGroup {
|
||||
addr: msg.group_addr.clone(),
|
||||
}
|
||||
@@ -49,19 +61,22 @@ pub fn instantiate(
|
||||
group_addr,
|
||||
multisig_addr,
|
||||
mix_denom: msg.mix_denom,
|
||||
key_size: msg.key_size,
|
||||
};
|
||||
STATE.save(deps.storage, &state)?;
|
||||
|
||||
CURRENT_EPOCH.save(
|
||||
deps.storage,
|
||||
&Epoch::new(
|
||||
EpochState::default(),
|
||||
EpochState::WaitingInitialisation,
|
||||
0,
|
||||
msg.time_configuration.unwrap_or_default(),
|
||||
env.block.time,
|
||||
),
|
||||
)?;
|
||||
|
||||
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
@@ -74,15 +89,28 @@ pub fn execute(
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::InitiateDkg {} => try_initiate_dkg(deps, env, info),
|
||||
ExecuteMsg::RegisterDealer {
|
||||
bte_key_with_proof,
|
||||
identity_key,
|
||||
announce_address,
|
||||
resharing,
|
||||
} => try_add_dealer(deps, info, bte_key_with_proof, announce_address, resharing),
|
||||
ExecuteMsg::CommitDealing {
|
||||
dealing_bytes,
|
||||
} => try_add_dealer(
|
||||
deps,
|
||||
info,
|
||||
bte_key_with_proof,
|
||||
identity_key,
|
||||
announce_address,
|
||||
resharing,
|
||||
} => try_commit_dealings(deps, info, dealing_bytes, resharing),
|
||||
),
|
||||
ExecuteMsg::CommitDealingsMetadata {
|
||||
dealing_index,
|
||||
chunks,
|
||||
resharing,
|
||||
} => try_submit_dealings_metadata(deps, info, dealing_index, chunks, resharing),
|
||||
ExecuteMsg::CommitDealingsChunk { chunk, resharing } => {
|
||||
try_commit_dealings_chunk(deps, env, info, chunk, resharing)
|
||||
}
|
||||
ExecuteMsg::CommitVerificationKeyShare { share, resharing } => {
|
||||
try_commit_verification_key_share(deps, env, info, share, resharing)
|
||||
}
|
||||
@@ -97,6 +125,7 @@ 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::GetCurrentEpochThreshold {} => {
|
||||
to_binary(&query_current_epoch_threshold(deps.storage)?)?
|
||||
@@ -111,24 +140,90 @@ pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse,
|
||||
QueryMsg::GetPastDealers { limit, start_after } => {
|
||||
to_binary(&query_past_dealers_paged(deps, start_after, limit)?)?
|
||||
}
|
||||
QueryMsg::GetDealing {
|
||||
idx,
|
||||
limit,
|
||||
start_after,
|
||||
} => to_binary(&query_dealings_paged(deps, idx, start_after, limit)?)?,
|
||||
QueryMsg::GetDealingsMetadata {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
} => to_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)?)?
|
||||
}
|
||||
QueryMsg::GetDealingStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
} => to_binary(&query_dealing_status(
|
||||
deps,
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
)?)?,
|
||||
QueryMsg::GetDealingChunkStatus {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
} => to_binary(&query_dealing_chunk_status(
|
||||
deps,
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
)?)?,
|
||||
QueryMsg::GetDealingChunk {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
} => to_binary(&query_dealing_chunk(
|
||||
deps,
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
)?)?,
|
||||
QueryMsg::GetVerificationKey { owner, epoch_id } => {
|
||||
to_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)?)?,
|
||||
};
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
Ok(Default::default())
|
||||
pub fn migrate(deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
fn parse_semver(raw: &str) -> Result<Version, ContractError> {
|
||||
raw.parse()
|
||||
.map_err(|error: semver::Error| ContractError::SemVerFailure {
|
||||
value: CONTRACT_VERSION.to_string(),
|
||||
error_message: error.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
// Note: don't remove this particular bit of code as we have to ALWAYS check whether we have to
|
||||
// update the stored version
|
||||
let build_version: Version = parse_semver(CONTRACT_VERSION)?;
|
||||
let stored_version: Version = parse_semver(&cw2::get_contract_version(deps.storage)?.version)?;
|
||||
|
||||
if stored_version < build_version {
|
||||
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||
|
||||
// If state structure changed in any contract version in the way migration is needed, it
|
||||
// should occur here, for example anything from `crate::queued_migrations::`
|
||||
}
|
||||
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -140,7 +235,8 @@ mod tests {
|
||||
use cosmwasm_std::{coins, Addr};
|
||||
use cw4::Member;
|
||||
use cw_multi_test::{App, AppBuilder, AppResponse, ContractWrapper, Executor};
|
||||
use nym_coconut_dkg_common::msg::ExecuteMsg::RegisterDealer;
|
||||
use nym_coconut_dkg_common::dealing::DEFAULT_DEALINGS;
|
||||
use nym_coconut_dkg_common::msg::ExecuteMsg::{InitiateDkg, RegisterDealer};
|
||||
use nym_coconut_dkg_common::types::NodeIndex;
|
||||
use nym_group_contract_common::msg::InstantiateMsg as GroupInstantiateMsg;
|
||||
|
||||
@@ -178,6 +274,7 @@ mod tests {
|
||||
multisig_addr: MULTISIG_CONTRACT.to_string(),
|
||||
time_configuration: None,
|
||||
mix_denom: TEST_MIX_DENOM.to_string(),
|
||||
key_size: DEFAULT_DEALINGS as u32,
|
||||
};
|
||||
app.instantiate_contract(
|
||||
coconut_dkg_code_id,
|
||||
@@ -213,6 +310,7 @@ mod tests {
|
||||
multisig_addr: "multisig_addr".to_string(),
|
||||
time_configuration: None,
|
||||
mix_denom: "nym".to_string(),
|
||||
key_size: 5,
|
||||
};
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
@@ -235,6 +333,14 @@ mod tests {
|
||||
});
|
||||
let coconut_dkg_contract_addr = instantiate_with_group(&mut app, &members);
|
||||
|
||||
app.execute_contract(
|
||||
Addr::unchecked(ADMIN_ADDRESS),
|
||||
coconut_dkg_contract_addr.clone(),
|
||||
&InitiateDkg {},
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for (idx, member) in members.iter().enumerate() {
|
||||
let res = app
|
||||
.execute_contract(
|
||||
@@ -242,6 +348,7 @@ mod tests {
|
||||
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,
|
||||
},
|
||||
@@ -256,6 +363,7 @@ mod tests {
|
||||
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,
|
||||
},
|
||||
@@ -272,6 +380,7 @@ mod tests {
|
||||
coconut_dkg_contract_addr,
|
||||
&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,
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::dealers::storage as dealers_storage;
|
||||
use crate::epoch_state::storage::INITIAL_REPLACEMENT_DATA;
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::STATE;
|
||||
use crate::state::storage::STATE;
|
||||
use cosmwasm_std::{Addr, DepsMut, MessageInfo, Response};
|
||||
use nym_coconut_dkg_common::types::{DealerDetails, EncodedBTEPublicKeyWithProof, EpochState};
|
||||
|
||||
@@ -34,10 +34,13 @@ fn verify_dealer(deps: DepsMut<'_>, dealer: &Addr, resharing: bool) -> Result<()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// future optimisation:
|
||||
// for a recurring dealer just let it refresh the keys without having to do all the storage operations
|
||||
pub fn try_add_dealer(
|
||||
mut deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
bte_key_with_proof: EncodedBTEPublicKeyWithProof,
|
||||
identity_key: String,
|
||||
announce_address: String,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
@@ -65,6 +68,7 @@ pub fn try_add_dealer(
|
||||
let dealer_details = DealerDetails {
|
||||
address: info.sender.clone(),
|
||||
bte_public_key_with_proof: bte_key_with_proof,
|
||||
ed25519_identity: identity_key,
|
||||
announce_address,
|
||||
assigned_index: node_index,
|
||||
};
|
||||
@@ -77,10 +81,10 @@ pub fn try_add_dealer(
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::dealers::storage::current_dealers;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
|
||||
use crate::support::tests::fixtures::dealer_details_fixture;
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, GROUP_MEMBERS};
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS, GROUP_MEMBERS};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cw4::Member;
|
||||
use nym_coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
|
||||
@@ -137,10 +141,13 @@ pub(crate) mod tests {
|
||||
#[test]
|
||||
fn invalid_state() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let owner = Addr::unchecked("owner");
|
||||
let mut env = mock_env();
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let owner = Addr::unchecked("owner");
|
||||
let info = mock_info(owner.as_str(), &[]);
|
||||
let bte_key_with_proof = String::from("bte_key_with_proof");
|
||||
let identity = String::from("identity");
|
||||
let announce_address = String::from("localhost:8000");
|
||||
|
||||
env.block.time = env
|
||||
@@ -155,6 +162,7 @@ pub(crate) mod tests {
|
||||
deps.as_mut(),
|
||||
info,
|
||||
bte_key_with_proof,
|
||||
identity,
|
||||
announce_address,
|
||||
false,
|
||||
)
|
||||
@@ -163,7 +171,7 @@ pub(crate) mod tests {
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::DealingExchange { resharing: false }.to_string(),
|
||||
expected_state: EpochState::default().to_string(),
|
||||
expected_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,195 +1,353 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealings::storage;
|
||||
use crate::dealings::storage::DEALINGS_BYTES;
|
||||
use cosmwasm_std::{Deps, Order, StdResult};
|
||||
use cw_storage_plus::Bound;
|
||||
use nym_coconut_dkg_common::dealer::{ContractDealing, PagedDealingsResponse};
|
||||
use nym_coconut_dkg_common::types::TOTAL_DEALINGS;
|
||||
use crate::dealings::storage::{StoredDealing, DEALINGS_METADATA};
|
||||
use crate::state::storage::STATE;
|
||||
use cosmwasm_std::{Deps, StdResult};
|
||||
use nym_coconut_dkg_common::dealing::{
|
||||
DealerDealingsStatusResponse, DealingChunkResponse, DealingChunkStatusResponse,
|
||||
DealingMetadataResponse, DealingStatus, DealingStatusResponse,
|
||||
};
|
||||
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochId};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub fn query_dealings_paged(
|
||||
/// Get the metadata associated with the particular dealing
|
||||
pub fn query_dealing_metadata(
|
||||
deps: Deps<'_>,
|
||||
idx: u64,
|
||||
start_after: Option<String>,
|
||||
limit: Option<u32>,
|
||||
) -> StdResult<PagedDealingsResponse> {
|
||||
let limit = limit
|
||||
.unwrap_or(storage::DEALINGS_PAGE_DEFAULT_LIMIT)
|
||||
.min(storage::DEALINGS_PAGE_MAX_LIMIT) as usize;
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> StdResult<DealingMetadataResponse> {
|
||||
let dealer = deps.api.addr_validate(&dealer)?;
|
||||
let metadata = DEALINGS_METADATA.may_load(deps.storage, (epoch_id, &dealer, dealing_index))?;
|
||||
|
||||
let idx = idx as usize;
|
||||
if idx >= TOTAL_DEALINGS {
|
||||
return Ok(PagedDealingsResponse::new(vec![], limit, None));
|
||||
}
|
||||
|
||||
let addr = start_after
|
||||
.map(|addr| deps.api.addr_validate(&addr))
|
||||
.transpose()?;
|
||||
|
||||
let start = addr.as_ref().map(Bound::exclusive);
|
||||
|
||||
let dealings = DEALINGS_BYTES[idx]
|
||||
.range(deps.storage, start, None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| res.map(|(dealer, dealing)| ContractDealing::new(dealing, dealer)))
|
||||
.collect::<StdResult<Vec<_>>>()?;
|
||||
|
||||
let start_next_after = dealings.last().map(|dealing| dealing.dealer.clone());
|
||||
|
||||
Ok(PagedDealingsResponse::new(
|
||||
dealings,
|
||||
limit,
|
||||
start_next_after,
|
||||
))
|
||||
Ok(DealingMetadataResponse {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
metadata,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::dealings::storage::{DEALINGS_PAGE_DEFAULT_LIMIT, DEALINGS_PAGE_MAX_LIMIT};
|
||||
use crate::support::tests::fixtures::dealing_bytes_fixture;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use cosmwasm_std::{Addr, DepsMut};
|
||||
/// Get the status of all dealings of particular dealer for given epoch.
|
||||
pub fn query_dealer_dealings_status(
|
||||
deps: Deps<'_>,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
) -> StdResult<DealerDealingsStatusResponse> {
|
||||
let dealer = deps.api.addr_validate(&dealer)?;
|
||||
let state = STATE.load(deps.storage)?;
|
||||
|
||||
fn fill_dealings(deps: DepsMut<'_>, size: usize) {
|
||||
for n in 0..size {
|
||||
let dealing_share = dealing_bytes_fixture();
|
||||
let sender = Addr::unchecked(format!("owner{}", n));
|
||||
(0..TOTAL_DEALINGS).for_each(|idx| {
|
||||
DEALINGS_BYTES[idx]
|
||||
.save(deps.storage, &sender, &dealing_share)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
let mut dealing_submission_status: BTreeMap<DealingIndex, DealingStatus> = BTreeMap::new();
|
||||
|
||||
// Since our key size is in single digit range, querying all of this at once on chain is fine
|
||||
for dealing_index in 0..state.key_size {
|
||||
let metadata =
|
||||
DEALINGS_METADATA.may_load(deps.storage, (epoch_id, &dealer, dealing_index))?;
|
||||
dealing_submission_status.insert(dealing_index, metadata.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_on_bad_idx() {
|
||||
let mut deps = init_contract();
|
||||
fill_dealings(deps.as_mut(), 1000);
|
||||
|
||||
for idx in TOTAL_DEALINGS as u64..100 * TOTAL_DEALINGS as u64 {
|
||||
let page1 = query_dealings_paged(deps.as_ref(), idx, None, None).unwrap();
|
||||
assert_eq!(0, page1.dealings.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealings_empty_on_init() {
|
||||
let deps = init_contract();
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
let response = query_dealings_paged(deps.as_ref(), idx, None, Option::from(2)).unwrap();
|
||||
assert_eq!(0, response.dealings.len());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealings_paged_retrieval_obeys_limits() {
|
||||
let mut deps = init_contract();
|
||||
let limit = 2;
|
||||
fill_dealings(deps.as_mut(), 1000);
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, None, Option::from(limit)).unwrap();
|
||||
assert_eq!(limit, page1.dealings.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealings_paged_retrieval_has_default_limit() {
|
||||
let mut deps = init_contract();
|
||||
fill_dealings(deps.as_mut(), 1000);
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_dealings_paged(deps.as_ref(), idx, None, None).unwrap();
|
||||
|
||||
assert_eq!(DEALINGS_PAGE_DEFAULT_LIMIT, page1.dealings.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealings_paged_retrieval_has_max_limit() {
|
||||
let mut deps = init_contract();
|
||||
fill_dealings(deps.as_mut(), 1000);
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000 * DEALINGS_PAGE_MAX_LIMIT;
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, None, Option::from(crazy_limit)).unwrap();
|
||||
|
||||
// we default to a decent sized upper bound instead
|
||||
let expected_limit = DEALINGS_PAGE_MAX_LIMIT;
|
||||
assert_eq!(expected_limit, page1.dealings.len() as u32);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dealings_pagination_works() {
|
||||
let mut deps = init_contract();
|
||||
|
||||
fill_dealings(deps.as_mut(), 1);
|
||||
|
||||
let per_page = 2;
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap();
|
||||
|
||||
// page should have 1 result on it
|
||||
assert_eq!(1, page1.dealings.len());
|
||||
}
|
||||
|
||||
// save another
|
||||
fill_dealings(deps.as_mut(), 2);
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
// page1 should have 2 results on it
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap();
|
||||
assert_eq!(2, page1.dealings.len());
|
||||
}
|
||||
|
||||
fill_dealings(deps.as_mut(), 3);
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
// page1 still has 2 results
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap();
|
||||
assert_eq!(2, page1.dealings.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_dealings_paged(
|
||||
deps.as_ref(),
|
||||
idx,
|
||||
Option::from(start_after.to_string()),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, page2.dealings.len());
|
||||
}
|
||||
|
||||
fill_dealings(deps.as_mut(), 4);
|
||||
|
||||
for idx in 0..TOTAL_DEALINGS as u64 {
|
||||
let page1 =
|
||||
query_dealings_paged(deps.as_ref(), idx, None, Option::from(per_page)).unwrap();
|
||||
let start_after = page1.start_next_after.unwrap();
|
||||
let page2 = query_dealings_paged(
|
||||
deps.as_ref(),
|
||||
idx,
|
||||
Option::from(start_after.to_string()),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
assert_eq!(2, page2.dealings.len());
|
||||
}
|
||||
}
|
||||
Ok(DealerDealingsStatusResponse {
|
||||
epoch_id,
|
||||
dealer,
|
||||
all_dealings_fully_submitted: dealing_submission_status
|
||||
.values()
|
||||
.all(|d| d.fully_submitted),
|
||||
dealing_submission_status,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the status of particular dealing, i.e. whether it has been fully submitted.
|
||||
pub fn query_dealing_status(
|
||||
deps: Deps<'_>,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
) -> StdResult<DealingStatusResponse> {
|
||||
let dealer = deps.api.addr_validate(&dealer)?;
|
||||
let metadata = DEALINGS_METADATA.may_load(deps.storage, (epoch_id, &dealer, dealing_index))?;
|
||||
|
||||
Ok(DealingStatusResponse {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
status: metadata.into(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the status of particular chunk, i.e. whether (and when) it has been fully submitted.
|
||||
pub fn query_dealing_chunk_status(
|
||||
deps: Deps<'_>,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> StdResult<DealingChunkStatusResponse> {
|
||||
let dealer = deps.api.addr_validate(&dealer)?;
|
||||
let metadata = DEALINGS_METADATA.may_load(deps.storage, (epoch_id, &dealer, dealing_index))?;
|
||||
|
||||
let status = metadata
|
||||
.as_ref()
|
||||
.and_then(|m| m.submitted_chunks.get(&chunk_index))
|
||||
.map(|&c| c.status)
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(DealingChunkStatusResponse {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
status,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the particular chunk of the dealing.
|
||||
pub fn query_dealing_chunk(
|
||||
deps: Deps<'_>,
|
||||
epoch_id: EpochId,
|
||||
dealer: String,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> StdResult<DealingChunkResponse> {
|
||||
let dealer = deps.api.addr_validate(&dealer)?;
|
||||
let chunk = StoredDealing::read(deps.storage, epoch_id, &dealer, dealing_index, chunk_index);
|
||||
|
||||
Ok(DealingChunkResponse {
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
chunk,
|
||||
})
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// pub(crate) mod tests {
|
||||
// use super::*;
|
||||
// use crate::dealings::storage::{DEALINGS_PAGE_DEFAULT_LIMIT, DEALINGS_PAGE_MAX_LIMIT};
|
||||
// use crate::support::tests::fixtures::{dealing_bytes_fixture, partial_dealing_fixture};
|
||||
// use crate::support::tests::helpers::init_contract;
|
||||
// use cosmwasm_std::{Addr, DepsMut};
|
||||
// use nym_coconut_dkg_common::types::PartialContractDealing;
|
||||
//
|
||||
// fn fill_dealings(deps: DepsMut<'_>, epoch: EpochId, dealers: usize, key_size: u32) {
|
||||
// for i in 0..dealers {
|
||||
// let dealer = Addr::unchecked(format!("dealer{i}"));
|
||||
// for dealing_index in 0..key_size {
|
||||
// StoredDealing::save(
|
||||
// deps.storage,
|
||||
// epoch,
|
||||
// &dealer,
|
||||
// PartialContractDealing {
|
||||
// dealing_index: dealing_index,
|
||||
// data: dealing_bytes_fixture(),
|
||||
// },
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_query_dealing() {
|
||||
// let mut deps = init_contract();
|
||||
//
|
||||
// let bad_address = "FOOMP".to_string();
|
||||
// assert!(query_dealing(deps.as_ref(), 0, bad_address, 0).is_err());
|
||||
//
|
||||
// let empty = query_dealing(deps.as_ref(), 0, "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!(empty.dealing.is_none());
|
||||
//
|
||||
// // insert the dealing
|
||||
// let dealing = partial_dealing_fixture();
|
||||
// StoredDealing::save(
|
||||
// deps.as_mut().storage,
|
||||
// 0,
|
||||
// &Addr::unchecked("foo"),
|
||||
// dealing.clone(),
|
||||
// );
|
||||
//
|
||||
// let retrieved = query_dealing(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
// assert_eq!(retrieved.epoch_id, 0);
|
||||
// assert_eq!(retrieved.dealing_index, dealing.dealing_index);
|
||||
// assert_eq!(retrieved.dealer, Addr::unchecked("foo"));
|
||||
// assert_eq!(retrieved.dealing.unwrap(), dealing.data);
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_query_dealing_status() {
|
||||
// let mut deps = init_contract();
|
||||
//
|
||||
// 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();
|
||||
// assert_eq!(empty.epoch_id, 0);
|
||||
// assert_eq!(empty.dealing_index, 0);
|
||||
// assert_eq!(empty.dealer, Addr::unchecked("foo"));
|
||||
// assert!(!empty.dealing_submitted);
|
||||
//
|
||||
// // insert the dealing
|
||||
// let dealing = partial_dealing_fixture();
|
||||
// StoredDealing::save(
|
||||
// deps.as_mut().storage,
|
||||
// 0,
|
||||
// &Addr::unchecked("foo"),
|
||||
// dealing.clone(),
|
||||
// );
|
||||
//
|
||||
// let retrieved = query_dealing_status(deps.as_ref(), 0, "foo".to_string(), 0).unwrap();
|
||||
// assert_eq!(retrieved.epoch_id, 0);
|
||||
// assert_eq!(retrieved.dealing_index, dealing.dealing_index);
|
||||
// assert_eq!(retrieved.dealer, Addr::unchecked("foo"));
|
||||
// assert!(retrieved.dealing_submitted)
|
||||
// }
|
||||
//
|
||||
// #[cfg(test)]
|
||||
// mod query_dealings {
|
||||
// use super::*;
|
||||
// use nym_coconut_dkg_common::types::DEFAULT_DEALINGS;
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_empty_on_init() {
|
||||
// let deps = init_contract();
|
||||
// let all_dealings = StoredDealing::unchecked_all_entries(&deps.storage);
|
||||
// assert!(all_dealings.is_empty())
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_paged_retrieval_obeys_limits() {
|
||||
// let mut deps = init_contract();
|
||||
// let limit = 2;
|
||||
// fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(limit))
|
||||
// .unwrap();
|
||||
// assert_eq!(limit, page1.dealings.len() as u32);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_paged_retrieval_has_default_limit() {
|
||||
// let mut deps = init_contract();
|
||||
// fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// // query without explicitly setting a limit
|
||||
// let page1 = query_dealings_paged(deps.as_ref(), 0, dealer, None, None).unwrap();
|
||||
//
|
||||
// assert_eq!(DEALINGS_PAGE_DEFAULT_LIMIT, page1.dealings.len() as u32);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_paged_retrieval_has_max_limit() {
|
||||
// let mut deps = init_contract();
|
||||
// fill_dealings(deps.as_mut(), 0, 10, DEFAULT_DEALINGS as u32);
|
||||
//
|
||||
// // query with a crazily high limit in an attempt to use too many resources
|
||||
// let crazy_limit = 1000 * DEALINGS_PAGE_MAX_LIMIT;
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(crazy_limit))
|
||||
// .unwrap();
|
||||
//
|
||||
// // we default to a decent sized upper bound instead
|
||||
// let expected_limit = DEALINGS_PAGE_MAX_LIMIT;
|
||||
// assert_eq!(expected_limit, page1.dealings.len() as u32);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn dealings_pagination_works() {
|
||||
// let mut deps = init_contract();
|
||||
//
|
||||
// fill_dealings(deps.as_mut(), 0, 10, 1);
|
||||
// let per_page = 2;
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 0, dealer, None, Option::from(per_page))
|
||||
// .unwrap();
|
||||
//
|
||||
// // page should have 1 result on it
|
||||
// assert_eq!(1, page1.dealings.len());
|
||||
// }
|
||||
//
|
||||
// // save another
|
||||
// fill_dealings(deps.as_mut(), 1, 10, 2);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// // page1 should have 2 results on it
|
||||
// let page1 =
|
||||
// query_dealings_paged(deps.as_ref(), 1, dealer, None, Option::from(per_page))
|
||||
// .unwrap();
|
||||
// assert_eq!(2, page1.dealings.len());
|
||||
// }
|
||||
//
|
||||
// fill_dealings(deps.as_mut(), 3, 10, 3);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// // page1 still has 2 results
|
||||
// let page1 = query_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 3,
|
||||
// dealer.clone(),
|
||||
// None,
|
||||
// Option::from(per_page),
|
||||
// )
|
||||
// .unwrap();
|
||||
// assert_eq!(2, page1.dealings.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_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 3,
|
||||
// dealer,
|
||||
// Option::from(start_after),
|
||||
// Option::from(per_page),
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// assert_eq!(1, page2.dealings.len());
|
||||
// }
|
||||
//
|
||||
// fill_dealings(deps.as_mut(), 4, 10, 4);
|
||||
//
|
||||
// for dealer in 0..10 {
|
||||
// let dealer = format!("dealer{dealer}");
|
||||
// let page1 = query_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 4,
|
||||
// dealer.clone(),
|
||||
// None,
|
||||
// Option::from(per_page),
|
||||
// )
|
||||
// .unwrap();
|
||||
// let start_after = page1.start_next_after.unwrap();
|
||||
// let page2 = query_dealings_paged(
|
||||
// deps.as_ref(),
|
||||
// 4,
|
||||
// dealer,
|
||||
// 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.dealings.len());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -1,33 +1,421 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::Addr;
|
||||
use cw_storage_plus::Map;
|
||||
use nym_coconut_dkg_common::types::{ContractSafeBytes, TOTAL_DEALINGS};
|
||||
use crate::error::ContractError;
|
||||
use cosmwasm_std::{Addr, Order, Record, StdResult, Storage};
|
||||
use cw_storage_plus::{Bound, Key, KeyDeserialize, Map, Path, Prefix, Prefixer, PrimaryKey};
|
||||
use nym_coconut_dkg_common::dealing::{DealingMetadata, PartialContractDealing};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
ChunkIndex, ContractSafeBytes, DealingIndex, EpochId, PartialContractDealingData,
|
||||
};
|
||||
|
||||
pub(crate) const DEALINGS_PAGE_MAX_LIMIT: u32 = 2;
|
||||
pub(crate) const DEALINGS_PAGE_DEFAULT_LIMIT: u32 = 1;
|
||||
type Dealer<'a> = &'a Addr;
|
||||
|
||||
type DealingKey<'a> = &'a Addr;
|
||||
/// Metadata for a dealing for given `EpochId`, submitted by particular `Dealer` for given `DealingIndex`.
|
||||
pub(crate) const DEALINGS_METADATA: Map<(EpochId, Dealer, DealingIndex), DealingMetadata> =
|
||||
Map::new("dealings_metadata");
|
||||
|
||||
// Note to whoever is looking at this implementation and is thinking of using something similar
|
||||
// for storing small commitments/hashes of data on chain:
|
||||
// If there's a lot of entries you want to store thinking, "oh, this digest is only 32 bytes, it's not that much",
|
||||
// the default cosmwasm' serializer will bloat it to around ~100B. So you really don't want to be using
|
||||
// Buckets/Maps, etc. for that purpose. Instead you want to use `storage` directly (look into the actual implementation of
|
||||
// `Map` or `Bucket` to see what I mean. Instead of using the `to_vec` method on serde_json_wasm, you'd
|
||||
// provide your data directly yourself.
|
||||
// but you must be extremely careful when doing so, as you might end up overwriting some existing data
|
||||
// if you don't choose your prefixes wisely.
|
||||
// I didn't have to do it here as I'm storing relatively little data and after just base58-encoding
|
||||
// my bytes, I was fine with the json overhead.
|
||||
pub(crate) fn metadata_exists(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
) -> bool {
|
||||
DEALINGS_METADATA.has(storage, (epoch_id, dealer, dealing_index))
|
||||
}
|
||||
|
||||
// if TOTAL_DEALINGS is modified to anything other then current value (5), this part will also need
|
||||
// to be modified
|
||||
pub(crate) const DEALINGS_BYTES: [Map<'_, DealingKey<'_>, ContractSafeBytes>; TOTAL_DEALINGS] = [
|
||||
Map::new("dbyt1"),
|
||||
Map::new("dbyt2"),
|
||||
Map::new("dbyt3"),
|
||||
Map::new("dbyt4"),
|
||||
Map::new("dbyt5"),
|
||||
];
|
||||
pub(crate) fn may_read_metadata(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<Option<DealingMetadata>, ContractError> {
|
||||
Ok(DEALINGS_METADATA.may_load(storage, (epoch_id, dealer, dealing_index))?)
|
||||
}
|
||||
|
||||
pub(crate) fn must_read_metadata(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
) -> Result<DealingMetadata, ContractError> {
|
||||
DEALINGS_METADATA
|
||||
.may_load(storage, (epoch_id, dealer, dealing_index))?
|
||||
.ok_or_else(|| ContractError::UnavailableDealingMetadata {
|
||||
epoch_id,
|
||||
dealer: dealer.to_owned(),
|
||||
dealing_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn store_metadata(
|
||||
storage: &mut dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
metadata: &DealingMetadata,
|
||||
) -> Result<(), ContractError> {
|
||||
Ok(DEALINGS_METADATA.save(storage, (epoch_id, dealer, dealing_index), metadata)?)
|
||||
}
|
||||
|
||||
// dealings data is stored in a multilevel map with the following hierarchy:
|
||||
// - epoch-id:
|
||||
// - issuer-address:
|
||||
// - dealing id:
|
||||
// - chunk_id:
|
||||
// - dealing content
|
||||
// NOTE: we're storing raw bytes bypassing serialization, so we can't use the `Map` type,
|
||||
// thus make sure you always use the below methods for using the storage!
|
||||
pub(crate) struct StoredDealing;
|
||||
|
||||
impl StoredDealing {
|
||||
const NAMESPACE: &'static [u8] = b"dealing";
|
||||
|
||||
fn deserialize_dealing_record(
|
||||
kv: Record,
|
||||
) -> StdResult<(ChunkIndex, PartialContractDealingData)> {
|
||||
let (k, v) = kv;
|
||||
let index = <ChunkIndex as KeyDeserialize>::from_vec(k)?;
|
||||
let data = ContractSafeBytes(v);
|
||||
|
||||
Ok((index, data))
|
||||
}
|
||||
|
||||
fn storage_key(
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> Path<Vec<u8>> {
|
||||
// just replicate the behaviour from `Map::key`
|
||||
// note: `PrimaryKey` trait is not implemented for tuple (T, U, V, W), only for up to (T, U, V)
|
||||
// that's why we create a (T, U, (V, W)) tuple(s) instead
|
||||
let key = (epoch_id, dealer, (dealing_index, chunk_index));
|
||||
Path::new(
|
||||
Self::NAMESPACE,
|
||||
&key.key().iter().map(Key::as_ref).collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
|
||||
fn prefix(
|
||||
prefix: (EpochId, Dealer, DealingIndex),
|
||||
) -> Prefix<ChunkIndex, PartialContractDealingData, ChunkIndex> {
|
||||
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"),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn exists(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: &Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> StdResult<bool> {
|
||||
// whenever the dealing is saved, the metadata is appropriately updated
|
||||
// reading metadata is way cheaper than the dealing chunk itself
|
||||
let Some(metadata) =
|
||||
DEALINGS_METADATA.may_load(storage, (epoch_id, dealer, dealing_index))?
|
||||
else {
|
||||
return Ok(false);
|
||||
};
|
||||
let Some(chunk_info) = metadata.submitted_chunks.get(&chunk_index) else {
|
||||
return Ok(false);
|
||||
};
|
||||
Ok(chunk_info.status.submitted())
|
||||
// StoredDealing::storage_key(epoch_id, dealer, dealing_index).has(storage)
|
||||
}
|
||||
|
||||
pub(crate) fn save(
|
||||
storage: &mut dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealng_chunk: PartialContractDealing,
|
||||
) {
|
||||
// NOTE: we're storing bytes directly here!
|
||||
let storage_key = Self::storage_key(
|
||||
epoch_id,
|
||||
dealer,
|
||||
dealng_chunk.dealing_index,
|
||||
dealng_chunk.chunk_index,
|
||||
);
|
||||
storage.set(&storage_key, dealng_chunk.data.as_slice());
|
||||
}
|
||||
|
||||
pub(crate) fn read(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> Option<PartialContractDealingData> {
|
||||
let storage_key = Self::storage_key(epoch_id, dealer, dealing_index, chunk_index);
|
||||
storage.get(&storage_key).map(ContractSafeBytes)
|
||||
}
|
||||
|
||||
pub(crate) fn prefix_range<'a>(
|
||||
storage: &'a dyn Storage,
|
||||
prefix: (EpochId, Dealer, DealingIndex),
|
||||
start: Option<Bound<ChunkIndex>>,
|
||||
) -> impl Iterator<Item = StdResult<PartialContractDealing>> + 'a {
|
||||
let dealing_index = prefix.2;
|
||||
Self::prefix(prefix)
|
||||
.range(storage, start, None, Order::Ascending)
|
||||
.map(move |maybe_record| {
|
||||
maybe_record.map(|(chunk_index, data)| PartialContractDealing {
|
||||
dealing_index,
|
||||
chunk_index,
|
||||
data,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// iterate over all values, only to be used in tests due to the amount of data being returned
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) fn unchecked_all_entries(
|
||||
storage: &dyn Storage,
|
||||
) -> Vec<(
|
||||
(EpochId, Addr, (DealingIndex, ChunkIndex)),
|
||||
PartialContractDealingData,
|
||||
)> {
|
||||
type StorageKey<'a> = (EpochId, Dealer<'a>, (DealingIndex, ChunkIndex));
|
||||
|
||||
let empty_prefix: Prefix<StorageKey, PartialContractDealingData, StorageKey> =
|
||||
Prefix::with_deserialization_functions(
|
||||
Self::NAMESPACE,
|
||||
&[],
|
||||
&[],
|
||||
|_, _, kv| StorageKey::from_vec(kv.0).map(|kt| (kt, ContractSafeBytes(kv.1))),
|
||||
|_, _, _| unimplemented!(),
|
||||
);
|
||||
|
||||
empty_prefix
|
||||
.range(storage, None, None, Order::Ascending)
|
||||
.collect::<StdResult<_>>()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn dealing_data(
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> PartialContractDealingData {
|
||||
ContractSafeBytes(
|
||||
format!("{epoch_id},{dealer},{dealing_index},{chunk_index}")
|
||||
.as_bytes()
|
||||
.to_vec(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saving_dealing_chunks() {
|
||||
let mut deps = init_contract();
|
||||
|
||||
fn exists_in_storage(
|
||||
storage: &dyn Storage,
|
||||
epoch_id: EpochId,
|
||||
dealer: Dealer,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
) -> bool {
|
||||
StoredDealing::storage_key(epoch_id, dealer, dealing_index, chunk_index).has(storage)
|
||||
}
|
||||
|
||||
// make sure to check all combinations of epoch id, dealer address and dealing index to ensure nothing overlaps
|
||||
let epochs = [54, 423, 754];
|
||||
let dealers = [
|
||||
Addr::unchecked("dealer1"),
|
||||
Addr::unchecked("dealer2"),
|
||||
Addr::unchecked("dealer3"),
|
||||
Addr::unchecked("dealer4"),
|
||||
Addr::unchecked("dealer5"),
|
||||
];
|
||||
let dealing_indices = [0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let chunk_indices = [0, 1, 2, 3, 4];
|
||||
|
||||
for epoch_id in &epochs {
|
||||
for dealer in &dealers {
|
||||
for dealing_index in &dealing_indices {
|
||||
for chunk_index in &chunk_indices {
|
||||
assert!(!exists_in_storage(
|
||||
&deps.storage,
|
||||
*epoch_id,
|
||||
dealer,
|
||||
*dealing_index,
|
||||
*chunk_index
|
||||
));
|
||||
|
||||
StoredDealing::save(
|
||||
deps.as_mut().storage,
|
||||
*epoch_id,
|
||||
dealer,
|
||||
PartialContractDealing {
|
||||
dealing_index: *dealing_index,
|
||||
chunk_index: *chunk_index,
|
||||
data: dealing_data(*epoch_id, dealer, *dealing_index, *chunk_index),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let all: HashMap<_, _> = StoredDealing::unchecked_all_entries(&deps.storage)
|
||||
.into_iter()
|
||||
.collect();
|
||||
assert_eq!(
|
||||
all.len(),
|
||||
epochs.len() * dealers.len() * dealing_indices.len() * chunk_indices.len()
|
||||
);
|
||||
|
||||
for epoch_id in &epochs {
|
||||
for dealer in &dealers {
|
||||
for dealing_index in &dealing_indices {
|
||||
for chunk_index in &chunk_indices {
|
||||
assert!(exists_in_storage(
|
||||
&deps.storage,
|
||||
*epoch_id,
|
||||
dealer,
|
||||
*dealing_index,
|
||||
*chunk_index
|
||||
));
|
||||
|
||||
let content = StoredDealing::read(
|
||||
&deps.storage,
|
||||
*epoch_id,
|
||||
dealer,
|
||||
*dealing_index,
|
||||
*chunk_index,
|
||||
)
|
||||
.unwrap();
|
||||
let expected =
|
||||
dealing_data(*epoch_id, dealer, *dealing_index, *chunk_index);
|
||||
assert_eq!(expected, content);
|
||||
assert_eq!(
|
||||
&expected,
|
||||
all.get(&(*epoch_id, dealer.clone(), (*dealing_index, *chunk_index)))
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterating_over_dealing_chunks() {
|
||||
let mut deps = init_contract();
|
||||
|
||||
let epochs = [54, 423, 754];
|
||||
let dealers = [
|
||||
Addr::unchecked("dealer1"),
|
||||
Addr::unchecked("dealer2"),
|
||||
Addr::unchecked("dealer3"),
|
||||
Addr::unchecked("dealer4"),
|
||||
Addr::unchecked("dealer5"),
|
||||
];
|
||||
let dealing_indices = [0, 1, 2, 3, 4, 5, 6, 7];
|
||||
let chunk_indices = [0, 1, 2, 3, 4];
|
||||
|
||||
for epoch_id in &epochs {
|
||||
for dealer in &dealers {
|
||||
for dealing_index in &dealing_indices {
|
||||
for chunk_index in &chunk_indices {
|
||||
StoredDealing::save(
|
||||
deps.as_mut().storage,
|
||||
*epoch_id,
|
||||
dealer,
|
||||
PartialContractDealing {
|
||||
dealing_index: *dealing_index,
|
||||
chunk_index: *chunk_index,
|
||||
data: dealing_data(*epoch_id, dealer, *dealing_index, *chunk_index),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remember, we're not testing the iterator implementation
|
||||
|
||||
// nothing under epoch 0
|
||||
let dealings =
|
||||
StoredDealing::prefix_range(&deps.storage, (0, &dealers[0], dealing_indices[0]), None)
|
||||
.collect::<Vec<_>>();
|
||||
assert!(dealings.is_empty());
|
||||
|
||||
// nothing for dealer "foo"
|
||||
let foo = Addr::unchecked("foo");
|
||||
let dealings =
|
||||
StoredDealing::prefix_range(&deps.storage, (epochs[0], &foo, dealing_indices[0]), None)
|
||||
.collect::<Vec<_>>();
|
||||
assert!(dealings.is_empty());
|
||||
|
||||
// nothing for dealing index 99
|
||||
let dealings =
|
||||
StoredDealing::prefix_range(&deps.storage, (epochs[0], &dealers[0], 99), None)
|
||||
.collect::<Vec<_>>();
|
||||
assert!(dealings.is_empty());
|
||||
|
||||
let all = StoredDealing::prefix_range(
|
||||
&deps.storage,
|
||||
(epochs[0], &dealers[0], dealing_indices[0]),
|
||||
None,
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(all.len(), chunk_indices.len());
|
||||
|
||||
for (i, dealing) in all.iter().enumerate() {
|
||||
let expected =
|
||||
dealing_data(epochs[0], &dealers[0], dealing_indices[0], chunk_indices[i]);
|
||||
assert_eq!(expected, dealing.as_ref().unwrap().data);
|
||||
assert_eq!(chunk_indices[i], dealing.as_ref().unwrap().chunk_index);
|
||||
}
|
||||
|
||||
// for sanity sake, check another dealer with different epoch and different dealing index
|
||||
let all_other = StoredDealing::prefix_range(
|
||||
&deps.storage,
|
||||
(epochs[2], &dealers[3], dealing_indices[4]),
|
||||
None,
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(all_other.len(), chunk_indices.len());
|
||||
|
||||
for (i, dealing) in all_other.iter().enumerate() {
|
||||
let expected =
|
||||
dealing_data(epochs[2], &dealers[3], dealing_indices[4], chunk_indices[i]);
|
||||
assert_eq!(expected, dealing.as_ref().unwrap().data);
|
||||
assert_eq!(chunk_indices[i], dealing.as_ref().unwrap().chunk_index);
|
||||
}
|
||||
|
||||
let without_first = StoredDealing::prefix_range(
|
||||
&deps.storage,
|
||||
(epochs[0], &dealers[0], dealing_indices[0]),
|
||||
Some(Bound::exclusive(chunk_indices[0])),
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(&all[1..], without_first);
|
||||
|
||||
let mid = StoredDealing::prefix_range(
|
||||
&deps.storage,
|
||||
(epochs[0], &dealers[0], dealing_indices[0]),
|
||||
Some(Bound::inclusive(chunk_indices[3])),
|
||||
)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(&all[3..], mid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,142 +1,346 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealers::storage as dealers_storage;
|
||||
use crate::dealings::storage::DEALINGS_BYTES;
|
||||
use crate::epoch_state::storage::INITIAL_REPLACEMENT_DATA;
|
||||
use crate::dealings::storage::{
|
||||
metadata_exists, must_read_metadata, store_metadata, StoredDealing,
|
||||
};
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA};
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use cosmwasm_std::{DepsMut, MessageInfo, Response};
|
||||
use nym_coconut_dkg_common::types::{ContractSafeBytes, EpochState};
|
||||
use crate::state::storage::STATE;
|
||||
use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response, Storage};
|
||||
use nym_coconut_dkg_common::dealing::{
|
||||
DealingChunkInfo, DealingMetadata, PartialContractDealing, MAX_DEALING_CHUNKS,
|
||||
};
|
||||
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochState};
|
||||
|
||||
pub fn try_commit_dealings(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
dealing_bytes: ContractSafeBytes,
|
||||
// make sure the epoch is in the dealing exchange and the message sender is a valid dealer for this epoch
|
||||
fn ensure_permission(
|
||||
storage: &dyn Storage,
|
||||
sender: &Addr,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
check_epoch_state(deps.storage, EpochState::DealingExchange { resharing })?;
|
||||
) -> Result<(), ContractError> {
|
||||
check_epoch_state(storage, EpochState::DealingExchange { resharing })?;
|
||||
|
||||
// ensure the sender is a dealer
|
||||
if dealers_storage::current_dealers()
|
||||
.may_load(deps.storage, &info.sender)?
|
||||
.may_load(storage, sender)?
|
||||
.is_none()
|
||||
{
|
||||
return Err(ContractError::NotADealer);
|
||||
}
|
||||
if resharing
|
||||
&& !INITIAL_REPLACEMENT_DATA
|
||||
.load(deps.storage)?
|
||||
.load(storage)?
|
||||
.initial_dealers
|
||||
.contains(&info.sender)
|
||||
.contains(sender)
|
||||
{
|
||||
return Err(ContractError::NotAnInitialDealer);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// check if this dealer has already committed to all dealings
|
||||
// (we don't want to allow overwriting anything)
|
||||
for dealings in DEALINGS_BYTES {
|
||||
if !dealings.has(deps.storage, &info.sender) {
|
||||
dealings.save(deps.storage, &info.sender, &dealing_bytes)?;
|
||||
return Ok(Response::default());
|
||||
pub fn try_submit_dealings_metadata(
|
||||
deps: DepsMut,
|
||||
info: MessageInfo,
|
||||
dealing_index: DealingIndex,
|
||||
chunks: Vec<DealingChunkInfo>,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
ensure_permission(deps.storage, &info.sender, resharing)?;
|
||||
|
||||
let state = STATE.load(deps.storage)?;
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
|
||||
// don't allow overwriting existing metadata
|
||||
if metadata_exists(deps.storage, epoch.epoch_id, &info.sender, dealing_index) {
|
||||
return Err(ContractError::MetadataAlreadyExists {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index,
|
||||
});
|
||||
}
|
||||
|
||||
// make sure the dealing index is in the allowed range
|
||||
// note: dealing indexing starts from 0
|
||||
if dealing_index >= state.key_size {
|
||||
return Err(ContractError::DealingOutOfRange {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
index: dealing_index,
|
||||
key_size: state.key_size,
|
||||
});
|
||||
}
|
||||
|
||||
// make sure the metadata is not empty
|
||||
if chunks.is_empty() {
|
||||
return Err(ContractError::EmptyMetadata {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index,
|
||||
});
|
||||
}
|
||||
|
||||
// make sure the chunks are non empty
|
||||
if chunks.iter().any(|c| c.size == 0) {
|
||||
return Err(ContractError::EmptyMetadata {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index,
|
||||
});
|
||||
}
|
||||
|
||||
// make sure the number of dealing chunks is in the allowed range
|
||||
// to prevent somebody splitting their dealings into 10B chunks
|
||||
if chunks.len() > MAX_DEALING_CHUNKS {
|
||||
return Err(ContractError::TooFragmentedMetadata {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index,
|
||||
chunks: chunks.len(),
|
||||
});
|
||||
}
|
||||
|
||||
// make sure all chunks, but the last one, have the same size
|
||||
// SAFETY: we checked for whether `chunks` is empty and returned an error in that case
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let first_chunk_size = chunks.first().unwrap().size;
|
||||
|
||||
for (chunk_index, chunk_info) in chunks.iter().enumerate().take(chunks.len() - 1) {
|
||||
if chunk_info.size != first_chunk_size {
|
||||
return Err(ContractError::UnevenChunkSplit {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index,
|
||||
chunk_index: chunk_index as ChunkIndex,
|
||||
first_chunk_size,
|
||||
size: chunk_info.size,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Err(ContractError::AlreadyCommitted {
|
||||
commitment: String::from("dealing"),
|
||||
})
|
||||
// finally, construct and store the metadata
|
||||
let metadata = DealingMetadata::new(dealing_index, chunks);
|
||||
|
||||
store_metadata(
|
||||
deps.storage,
|
||||
epoch.epoch_id,
|
||||
&info.sender,
|
||||
dealing_index,
|
||||
&metadata,
|
||||
)?;
|
||||
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
pub fn try_commit_dealings_chunk(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
chunk: PartialContractDealing,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
ensure_permission(deps.storage, &info.sender, resharing)?;
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
|
||||
// read meta
|
||||
let mut metadata = must_read_metadata(
|
||||
deps.storage,
|
||||
epoch.epoch_id,
|
||||
&info.sender,
|
||||
chunk.dealing_index,
|
||||
)?;
|
||||
|
||||
// check if the received chunk is within the declared range
|
||||
let Some(submission_status) = metadata.submitted_chunks.get_mut(&chunk.chunk_index) else {
|
||||
return Err(ContractError::DealingChunkNotInMetadata {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index: chunk.dealing_index,
|
||||
chunk_index: chunk.chunk_index,
|
||||
});
|
||||
};
|
||||
|
||||
// check if this dealer has already committed this particular dealing chunk
|
||||
if let Some(submission_height) = submission_status.status.submission_height {
|
||||
return Err(ContractError::DealingChunkAlreadyCommitted {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index: chunk.dealing_index,
|
||||
chunk_index: chunk.chunk_index,
|
||||
block_height: submission_height,
|
||||
});
|
||||
}
|
||||
|
||||
// check if the received chunk has the specified size
|
||||
if submission_status.info.size != chunk.data.len() {
|
||||
return Err(ContractError::InconsistentChunkLength {
|
||||
epoch_id: epoch.epoch_id,
|
||||
dealer: info.sender,
|
||||
dealing_index: chunk.dealing_index,
|
||||
chunk_index: chunk.chunk_index,
|
||||
metadata_length: submission_status.info.size,
|
||||
received: chunk.data.len(),
|
||||
});
|
||||
}
|
||||
|
||||
// update the metadata
|
||||
submission_status.status.submission_height = Some(env.block.height);
|
||||
store_metadata(
|
||||
deps.storage,
|
||||
epoch.epoch_id,
|
||||
&info.sender,
|
||||
chunk.dealing_index,
|
||||
&metadata,
|
||||
)?;
|
||||
|
||||
// store the dealing
|
||||
StoredDealing::save(deps.storage, epoch.epoch_id, &info.sender, chunk);
|
||||
|
||||
Ok(Response::new())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, dealing_bytes_fixture};
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, partial_dealing_fixture};
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::add_fixture_dealer;
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_coconut_dkg_common::dealer::DealerDetails;
|
||||
use nym_coconut_dkg_common::types::{InitialReplacementData, TimeConfiguration};
|
||||
use nym_coconut_dkg_common::types::{
|
||||
ContractSafeBytes, InitialReplacementData, TimeConfiguration,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn invalid_commit_dealing() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let owner = Addr::unchecked("owner1");
|
||||
let mut env = mock_env();
|
||||
let info = mock_info(owner.as_str(), &[]);
|
||||
let dealing_bytes = dealing_bytes_fixture();
|
||||
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), false)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
expected_state: EpochState::DealingExchange { resharing: false }.to_string()
|
||||
}
|
||||
);
|
||||
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
add_fixture_dealer(deps.as_mut());
|
||||
advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), false)
|
||||
.unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotADealer);
|
||||
|
||||
let dealer_details = DealerDetails {
|
||||
address: owner.clone(),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
announce_address: String::new(),
|
||||
assigned_index: 1,
|
||||
};
|
||||
dealers_storage::current_dealers()
|
||||
.save(deps.as_mut().storage, &owner, &dealer_details)
|
||||
.unwrap();
|
||||
|
||||
// assume we're in resharing mode
|
||||
CURRENT_EPOCH
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
epoch.state = EpochState::DealingExchange { resharing: true };
|
||||
Ok(epoch)
|
||||
})
|
||||
.unwrap();
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&InitialReplacementData {
|
||||
initial_dealers: vec![],
|
||||
initial_height: 1,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), true)
|
||||
.unwrap_err();
|
||||
assert_eq!(ret, ContractError::NotAnInitialDealer);
|
||||
|
||||
INITIAL_REPLACEMENT_DATA
|
||||
.update::<_, ContractError>(deps.as_mut().storage, |mut data| {
|
||||
data.initial_dealers = vec![dealer_details_fixture(1).address];
|
||||
Ok(data)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
for dealings in DEALINGS_BYTES {
|
||||
assert!(!dealings.has(deps.as_mut().storage, &owner));
|
||||
let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing_bytes.clone(), true);
|
||||
assert!(ret.is_ok());
|
||||
assert!(dealings.has(deps.as_mut().storage, &owner));
|
||||
}
|
||||
let ret = try_commit_dealings(deps.as_mut(), info, dealing_bytes, true).unwrap_err();
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::AlreadyCommitted {
|
||||
commitment: String::from("dealing"),
|
||||
}
|
||||
);
|
||||
todo!()
|
||||
// let mut deps = helpers::init_contract();
|
||||
// let mut env = mock_env();
|
||||
// try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
//
|
||||
// let owner = Addr::unchecked("owner1");
|
||||
// let info = mock_info(owner.as_str(), &[]);
|
||||
// let dealing = partial_dealing_fixture();
|
||||
//
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
// assert_eq!(
|
||||
// ret,
|
||||
// ContractError::IncorrectEpochState {
|
||||
// current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
// expected_state: EpochState::DealingExchange { resharing: false }.to_string()
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// env.block.time = env
|
||||
// .block
|
||||
// .time
|
||||
// .plus_seconds(TimeConfiguration::default().public_key_submission_time_secs);
|
||||
// add_fixture_dealer(deps.as_mut());
|
||||
// advance_epoch_state(deps.as_mut(), env).unwrap();
|
||||
//
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
// assert_eq!(ret, ContractError::NotADealer);
|
||||
//
|
||||
// let dealer_details = DealerDetails {
|
||||
// address: owner.clone(),
|
||||
// bte_public_key_with_proof: String::new(),
|
||||
// ed25519_identity: String::new(),
|
||||
// announce_address: String::new(),
|
||||
// assigned_index: 1,
|
||||
// };
|
||||
// dealers_storage::current_dealers()
|
||||
// .save(deps.as_mut().storage, &owner, &dealer_details)
|
||||
// .unwrap();
|
||||
//
|
||||
// // assume we're in resharing mode
|
||||
// CURRENT_EPOCH
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
// epoch.state = EpochState::DealingExchange { resharing: true };
|
||||
// Ok(epoch)
|
||||
// })
|
||||
// .unwrap();
|
||||
// INITIAL_REPLACEMENT_DATA
|
||||
// .save(
|
||||
// deps.as_mut().storage,
|
||||
// &InitialReplacementData {
|
||||
// initial_dealers: vec![],
|
||||
// initial_height: 1,
|
||||
// },
|
||||
// )
|
||||
// .unwrap();
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), true).unwrap_err();
|
||||
// assert_eq!(ret, ContractError::NotAnInitialDealer);
|
||||
//
|
||||
// INITIAL_REPLACEMENT_DATA
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut data| {
|
||||
// data.initial_dealers = vec![dealer_details_fixture(1).address];
|
||||
// Ok(data)
|
||||
// })
|
||||
// .unwrap();
|
||||
//
|
||||
// // back to 'normal' mode
|
||||
// CURRENT_EPOCH
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
// epoch.state = EpochState::DealingExchange { resharing: false };
|
||||
// Ok(epoch)
|
||||
// })
|
||||
// .unwrap();
|
||||
//
|
||||
// // dealing out of range
|
||||
// let ret = try_commit_dealings(
|
||||
// deps.as_mut(),
|
||||
// info.clone(),
|
||||
// PartialContractDealing {
|
||||
// dealing_index: 42,
|
||||
// data: ContractSafeBytes(vec![1, 2, 3]),
|
||||
// },
|
||||
// false,
|
||||
// )
|
||||
// .unwrap_err();
|
||||
// assert_eq!(
|
||||
// ret,
|
||||
// ContractError::DealingOutOfRange {
|
||||
// epoch_id: 0,
|
||||
// dealer: info.sender.clone(),
|
||||
// index: 42,
|
||||
// key_size: DEFAULT_DEALINGS as u32,
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// // 'good' dealing
|
||||
// let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false);
|
||||
// assert!(ret.is_ok());
|
||||
//
|
||||
// // duplicate dealing
|
||||
// let ret =
|
||||
// try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false).unwrap_err();
|
||||
// assert_eq!(
|
||||
// ret,
|
||||
// ContractError::DealingAlreadyCommitted {
|
||||
// epoch_id: 0,
|
||||
// dealer: info.sender.clone(),
|
||||
// index: 0,
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// // same index, but next epoch
|
||||
// CURRENT_EPOCH
|
||||
// .update::<_, ContractError>(deps.as_mut().storage, |mut epoch| {
|
||||
// epoch.epoch_id += 1;
|
||||
// Ok(epoch)
|
||||
// })
|
||||
// .unwrap();
|
||||
//
|
||||
// let ret = try_commit_dealings(deps.as_mut(), info.clone(), dealing.clone(), false);
|
||||
// assert!(ret.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,22 +27,29 @@ pub(crate) fn query_initial_dealers(
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers::init_contract;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
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 nym_coconut_dkg_common::types::{EpochState, TimeConfiguration};
|
||||
|
||||
#[test]
|
||||
fn query_state() {
|
||||
let mut deps = init_contract();
|
||||
let epoch = query_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::WaitingInitialisation);
|
||||
assert_eq!(epoch.finish_timestamp, None);
|
||||
|
||||
let env = mock_env();
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let epoch = query_current_epoch(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
mock_env()
|
||||
.block
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(TimeConfiguration::default().public_key_submission_time_secs)
|
||||
);
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::dealers::storage::{current_dealers, past_dealers};
|
||||
use crate::dealings::storage::DEALINGS_BYTES;
|
||||
use crate::epoch_state::storage::{CURRENT_EPOCH, INITIAL_REPLACEMENT_DATA, THRESHOLD};
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::STATE;
|
||||
use crate::state::storage::{DKG_ADMIN, STATE};
|
||||
use crate::verification_key_shares::storage::verified_dealers;
|
||||
use cosmwasm_std::{Addr, Deps, DepsMut, Env, Order, Response, Storage};
|
||||
use cosmwasm_std::{Addr, Deps, DepsMut, Env, MessageInfo, Order, Response, Storage};
|
||||
use nym_coconut_dkg_common::types::{Epoch, EpochState, InitialReplacementData};
|
||||
|
||||
fn reset_epoch_state(storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
fn reset_dkg_state(storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
THRESHOLD.remove(storage);
|
||||
let dealers: Vec<_> = current_dealers()
|
||||
.keys(storage, None, None, Order::Ascending)
|
||||
@@ -19,15 +18,6 @@ fn reset_epoch_state(storage: &mut dyn Storage) -> Result<(), ContractError> {
|
||||
|
||||
for dealer_addr in dealers {
|
||||
let details = current_dealers().load(storage, &dealer_addr)?;
|
||||
for dealings in DEALINGS_BYTES {
|
||||
let dealing_keys: Vec<_> = dealings
|
||||
.keys(storage, None, None, Order::Ascending)
|
||||
.flatten()
|
||||
.collect();
|
||||
for key in dealing_keys {
|
||||
dealings.remove(storage, &key);
|
||||
}
|
||||
}
|
||||
current_dealers().remove(storage, &dealer_addr)?;
|
||||
past_dealers().save(storage, &dealer_addr, &details)?;
|
||||
}
|
||||
@@ -80,18 +70,43 @@ fn replacement_threshold_surpassed(deps: &DepsMut<'_>) -> Result<bool, ContractE
|
||||
Ok(removed_dealer_count >= replacement_threshold)
|
||||
}
|
||||
|
||||
pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Response, ContractError> {
|
||||
pub(crate) fn try_initiate_dkg(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
) -> Result<Response, ContractError> {
|
||||
// only the admin is allowed to kick start the process
|
||||
DKG_ADMIN.assert_admin(deps.as_ref(), &info.sender)?;
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
if epoch.finish_timestamp > env.block.time {
|
||||
return Err(ContractError::EarlyEpochStateAdvancement(
|
||||
epoch
|
||||
.finish_timestamp
|
||||
.minus_seconds(env.block.time.seconds())
|
||||
.seconds(),
|
||||
));
|
||||
if !matches!(epoch.state, EpochState::WaitingInitialisation) {
|
||||
return Err(ContractError::AlreadyInitialised);
|
||||
}
|
||||
|
||||
// the first exchange won't involve resharing
|
||||
let initial_state = EpochState::PublicKeySubmission { resharing: false };
|
||||
let initial_epoch = Epoch::new(initial_state, 0, epoch.time_configuration, env.block.time);
|
||||
CURRENT_EPOCH.save(deps.storage, &initial_epoch)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Response, ContractError> {
|
||||
let current_epoch = CURRENT_EPOCH.load(deps.storage)?;
|
||||
if current_epoch.state == EpochState::WaitingInitialisation {
|
||||
return Err(ContractError::WaitingInitialisation);
|
||||
}
|
||||
|
||||
if let Some(finish_timestamp) = current_epoch.finish_timestamp {
|
||||
if finish_timestamp > env.block.time {
|
||||
return Err(ContractError::EarlyEpochStateAdvancement(
|
||||
finish_timestamp
|
||||
.minus_seconds(env.block.time.seconds())
|
||||
.seconds(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let next_epoch = if let Some(state) = current_epoch.state.next() {
|
||||
// We are during DKG process
|
||||
let mut new_state = state;
|
||||
@@ -123,6 +138,8 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
|
||||
} else if dealers_eq_members(&deps)? {
|
||||
// The dealer set hasn't changed, so we only extend the finish timestamp
|
||||
// The epoch remains the same, as we use it as key for storing VKs
|
||||
|
||||
// TODO: change that behaviour in the following PR
|
||||
Epoch::new(
|
||||
current_epoch.state,
|
||||
current_epoch.epoch_id,
|
||||
@@ -139,6 +156,7 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
|
||||
// ... in reshare mode
|
||||
if INITIAL_REPLACEMENT_DATA.may_load(deps.storage)?.is_some() {
|
||||
INITIAL_REPLACEMENT_DATA.update::<_, ContractError>(deps.storage, |mut data| {
|
||||
// TODO: FIXME: for second reshare the added set of dealers won't be allowed to participate
|
||||
data.initial_height = env.block.height;
|
||||
Ok(data)
|
||||
})?;
|
||||
@@ -152,7 +170,7 @@ pub(crate) fn advance_epoch_state(deps: DepsMut<'_>, env: Env) -> Result<Respons
|
||||
|
||||
EpochState::PublicKeySubmission { resharing: true }
|
||||
};
|
||||
reset_epoch_state(deps.storage)?;
|
||||
reset_dkg_state(deps.storage)?;
|
||||
Epoch::new(
|
||||
state,
|
||||
current_epoch.epoch_id + 1,
|
||||
@@ -174,7 +192,7 @@ pub(crate) fn try_surpassed_threshold(
|
||||
let threshold = THRESHOLD.load(deps.storage)?;
|
||||
let dealers = verified_dealers(deps.storage)?;
|
||||
if dealers_still_active(&deps.as_ref(), dealers.into_iter())? < threshold as usize {
|
||||
reset_epoch_state(deps.storage)?;
|
||||
reset_dkg_state(deps.storage)?;
|
||||
CURRENT_EPOCH.update::<_, ContractError>(deps.storage, |epoch| {
|
||||
Ok(Epoch::new(
|
||||
EpochState::default(),
|
||||
@@ -193,20 +211,19 @@ pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::error::ContractError::EarlyEpochStateAdvancement;
|
||||
use crate::support::tests::fixtures::{dealer_details_fixture, vk_share_fixture};
|
||||
use crate::support::tests::helpers::{init_contract, GROUP_MEMBERS};
|
||||
use crate::support::tests::helpers::{init_contract, ADMIN_ADDRESS, GROUP_MEMBERS};
|
||||
use crate::verification_key_shares::storage::vk_shares;
|
||||
use cosmwasm_std::testing::mock_env;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::Addr;
|
||||
use cw4::Member;
|
||||
use nym_coconut_dkg_common::types::{
|
||||
ContractSafeBytes, DealerDetails, EpochState, TimeConfiguration,
|
||||
};
|
||||
use cw_controllers::AdminError;
|
||||
use nym_coconut_dkg_common::types::{DealerDetails, EpochState, TimeConfiguration};
|
||||
use rusty_fork::rusty_fork_test;
|
||||
|
||||
// Because of the global variable handling group, we need individual process for each test
|
||||
|
||||
rusty_fork_test! {
|
||||
// Using values from the DKG document
|
||||
// Using values from the DKG document
|
||||
#[test]
|
||||
fn threshold_surpassed() {
|
||||
let mut deps = init_contract();
|
||||
@@ -217,14 +234,18 @@ pub(crate) mod tests {
|
||||
|
||||
for n in [10, 25, 50, 100] {
|
||||
let dealers: Vec<_> = (0..n).map(dealer_details_fixture).collect();
|
||||
let shares: Vec<_> = (0..n).map(|idx| vk_share_fixture(&format!("owner{}", idx), 0)).collect();
|
||||
let shares: Vec<_> = (0..n)
|
||||
.map(|idx| vk_share_fixture(&format!("owner{}", idx), 0))
|
||||
.collect();
|
||||
let initial_dealers = dealers.iter().map(|d| d.address.clone()).collect();
|
||||
let data = InitialReplacementData {
|
||||
initial_dealers,
|
||||
initial_height: 1,
|
||||
};
|
||||
for share in shares {
|
||||
vk_shares().save(deps.as_mut().storage, (&share.owner, 0), &share).unwrap();
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), &share)
|
||||
.unwrap();
|
||||
}
|
||||
for f in [two_thirds, three_fourths, ninty_pc] {
|
||||
let threshold = f(n);
|
||||
@@ -361,7 +382,8 @@ pub(crate) mod tests {
|
||||
fn advance_state() {
|
||||
let mut deps = init_contract();
|
||||
let mut env = mock_env();
|
||||
{
|
||||
|
||||
{
|
||||
let mut group = GROUP_MEMBERS.lock().unwrap();
|
||||
|
||||
group.push((
|
||||
@@ -394,13 +416,21 @@ pub(crate) mod tests {
|
||||
));
|
||||
}
|
||||
|
||||
// can't advance the state if dkg hasn't been initiated
|
||||
assert_eq!(
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap_err(),
|
||||
ContractError::WaitingInitialisation
|
||||
);
|
||||
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs)
|
||||
@@ -424,7 +454,8 @@ pub(crate) mod tests {
|
||||
);
|
||||
|
||||
// setup dealer details
|
||||
let all_shares: [_; 4] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
let all_shares: [_; 4] =
|
||||
std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), share)
|
||||
@@ -441,7 +472,10 @@ pub(crate) mod tests {
|
||||
.may_load(&deps.storage)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
env.block.time = env.block.time.plus_seconds(epoch.time_configuration.public_key_submission_time_secs);
|
||||
env.block.time = env
|
||||
.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(
|
||||
@@ -449,7 +483,7 @@ pub(crate) mod tests {
|
||||
EpochState::DealingExchange { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.dealing_exchange_time_secs)
|
||||
@@ -472,7 +506,7 @@ pub(crate) mod tests {
|
||||
EpochState::VerificationKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
@@ -499,7 +533,7 @@ pub(crate) mod tests {
|
||||
EpochState::VerificationKeyValidation { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
@@ -526,7 +560,7 @@ pub(crate) mod tests {
|
||||
EpochState::VerificationKeyFinalization { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block.time.plus_seconds(
|
||||
epoch
|
||||
.time_configuration
|
||||
@@ -548,7 +582,7 @@ pub(crate) mod tests {
|
||||
let epoch = CURRENT_EPOCH.load(deps.as_mut().storage).unwrap();
|
||||
assert_eq!(epoch.state, EpochState::InProgress);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp,
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.in_progress_time_secs)
|
||||
@@ -614,7 +648,9 @@ pub(crate) mod tests {
|
||||
|
||||
let all_details: [_; 4] = std::array::from_fn(|i| dealer_details_fixture(i as u64 + 2));
|
||||
for details in all_details.iter() {
|
||||
past_dealers().remove(deps.as_mut().storage, &details.address).unwrap();
|
||||
past_dealers()
|
||||
.remove(deps.as_mut().storage, &details.address)
|
||||
.unwrap();
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
@@ -622,9 +658,15 @@ pub(crate) mod tests {
|
||||
for times in [
|
||||
epoch.time_configuration.public_key_submission_time_secs,
|
||||
epoch.time_configuration.dealing_exchange_time_secs,
|
||||
epoch.time_configuration.verification_key_submission_time_secs,
|
||||
epoch.time_configuration.verification_key_validation_time_secs,
|
||||
epoch.time_configuration.verification_key_finalization_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_submission_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_validation_time_secs,
|
||||
epoch
|
||||
.time_configuration
|
||||
.verification_key_finalization_time_secs,
|
||||
] {
|
||||
env.block.time = env.block.time.plus_seconds(times);
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
@@ -634,7 +676,7 @@ pub(crate) mod tests {
|
||||
let mut share = vk_share_fixture(&format!("owner{}", i + 1), 1);
|
||||
share.verified = i % 2 == 0;
|
||||
share
|
||||
});
|
||||
});
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), share)
|
||||
@@ -670,6 +712,8 @@ pub(crate) mod tests {
|
||||
fn surpass_threshold() {
|
||||
let mut deps = init_contract();
|
||||
let mut env = mock_env();
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let time_configuration = TimeConfiguration::default();
|
||||
{
|
||||
let mut group = GROUP_MEMBERS.lock().unwrap();
|
||||
@@ -701,13 +745,13 @@ pub(crate) mod tests {
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
expected_state: EpochState::InProgress.to_string()
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
let all_shares: [_; 3] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
let all_shares: [_; 3] =
|
||||
std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, 0), share)
|
||||
@@ -719,7 +763,8 @@ pub(crate) mod tests {
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
}
|
||||
let all_shares: [_; 3] = std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
let all_shares: [_; 3] =
|
||||
std::array::from_fn(|i| vk_share_fixture(&format!("owner{}", i + 1), 0));
|
||||
for share in all_shares.iter() {
|
||||
vk_shares()
|
||||
.save(deps.as_mut().storage, (&share.owner, share.epoch_id), share)
|
||||
@@ -778,6 +823,46 @@ pub(crate) mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn initialising_dkg() {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
let initial_epoch_info = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
assert!(initial_epoch_info.finish_timestamp.is_none());
|
||||
|
||||
// can only be executed by the admin
|
||||
let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info("not an admin", &[]))
|
||||
.unwrap_err();
|
||||
assert_eq!(ContractError::Admin(AdminError::NotAdmin {}), res);
|
||||
|
||||
let res = try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(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, &[]))
|
||||
.unwrap_err();
|
||||
assert_eq!(ContractError::AlreadyInitialised, res);
|
||||
|
||||
// sets the correct epoch data
|
||||
let epoch = CURRENT_EPOCH.load(&deps.storage).unwrap();
|
||||
assert_eq!(epoch.epoch_id, 0);
|
||||
assert_eq!(
|
||||
epoch.state,
|
||||
EpochState::PublicKeySubmission { resharing: false }
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.time_configuration,
|
||||
initial_epoch_info.time_configuration
|
||||
);
|
||||
assert_eq!(
|
||||
epoch.finish_timestamp.unwrap(),
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(epoch.time_configuration.public_key_submission_time_secs)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_state() {
|
||||
let mut deps = init_contract();
|
||||
@@ -788,27 +873,12 @@ pub(crate) mod tests {
|
||||
current_dealers()
|
||||
.save(deps.as_mut().storage, &details.address, details)
|
||||
.unwrap();
|
||||
for dealings in DEALINGS_BYTES {
|
||||
dealings
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&details.address,
|
||||
&ContractSafeBytes(vec![1, 2, 3]),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
reset_epoch_state(deps.as_mut().storage).unwrap();
|
||||
reset_dkg_state(deps.as_mut().storage).unwrap();
|
||||
|
||||
assert!(THRESHOLD.may_load(&deps.storage).unwrap().is_none());
|
||||
for details in all_details {
|
||||
for dealings in DEALINGS_BYTES {
|
||||
assert!(dealings
|
||||
.may_load(&deps.storage, &details.address)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
}
|
||||
assert!(current_dealers()
|
||||
.may_load(deps.as_mut().storage, &details.address)
|
||||
.unwrap()
|
||||
@@ -826,6 +896,7 @@ pub(crate) 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();
|
||||
|
||||
assert!(THRESHOLD.may_load(deps.as_mut().storage).unwrap().is_none());
|
||||
|
||||
@@ -838,6 +909,7 @@ pub(crate) mod tests {
|
||||
&DealerDetails {
|
||||
address: address.clone(),
|
||||
bte_public_key_with_proof: "bte_public_key_with_proof".to_string(),
|
||||
ed25519_identity: "identity".to_string(),
|
||||
announce_address: "127.0.0.1".to_string(),
|
||||
assigned_index: i,
|
||||
},
|
||||
|
||||
@@ -33,14 +33,14 @@ pub(crate) mod test {
|
||||
let mut deps = init_contract();
|
||||
let env = mock_env();
|
||||
|
||||
for fixed_state in EpochState::default().all_until(EpochState::InProgress) {
|
||||
for fixed_state in EpochState::first().all_until(EpochState::InProgress) {
|
||||
CURRENT_EPOCH
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&Epoch::new(fixed_state, 0, TimeConfiguration::default(), env.block.time),
|
||||
)
|
||||
.unwrap();
|
||||
for against_state in EpochState::default().all_until(EpochState::InProgress) {
|
||||
for against_state in EpochState::first().all_until(EpochState::InProgress) {
|
||||
let ret = check_epoch_state(deps.as_mut().storage, against_state);
|
||||
if fixed_state == against_state {
|
||||
assert!(ret.is_ok());
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::StdError;
|
||||
use cosmwasm_std::{Addr, StdError};
|
||||
use cw_controllers::AdminError;
|
||||
use nym_coconut_dkg_common::dealing::MAX_DEALING_CHUNKS;
|
||||
use nym_coconut_dkg_common::types::{ChunkIndex, DealingIndex, EpochId};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Custom errors for contract failure conditions.
|
||||
@@ -14,6 +16,12 @@ pub enum ContractError {
|
||||
#[error(transparent)]
|
||||
Admin(#[from] AdminError),
|
||||
|
||||
#[error("Dkg hasn't been initialised yet")]
|
||||
WaitingInitialisation,
|
||||
|
||||
#[error("Dkg has already been initialised")]
|
||||
AlreadyInitialised,
|
||||
|
||||
#[error("Group contract invalid address '{addr}'")]
|
||||
InvalidGroup { addr: String },
|
||||
|
||||
@@ -30,7 +38,7 @@ pub enum ContractError {
|
||||
EpochNotInitialised,
|
||||
|
||||
#[error(
|
||||
"Requested action needs state to be {expected_state}, currently in state {current_state}, "
|
||||
"Requested action needs state to be {expected_state}, currently in state {current_state}"
|
||||
)]
|
||||
IncorrectEpochState {
|
||||
current_state: String,
|
||||
@@ -43,9 +51,89 @@ pub enum ContractError {
|
||||
#[error("This sender is not a dealer for the current resharing epoch")]
|
||||
NotAnInitialDealer,
|
||||
|
||||
#[error("Dealer {dealer} has already committed dealing chunk for epoch {epoch_id} with dealing index {dealing_index} and chunk index {chunk_index} at height {block_height}")]
|
||||
DealingChunkAlreadyCommitted {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
block_height: u64,
|
||||
},
|
||||
|
||||
#[error("dealer {dealer} tried to commit chunk {chunk_index} of dealing {dealing_index} for epoch {epoch_id}, but it hasn't been declared in the prior metadata")]
|
||||
DealingChunkNotInMetadata {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
},
|
||||
|
||||
#[error("dealer {dealer} has attempted to commit dealing chunk for epoch {epoch_id} with dealing index {index} while the key size is set to {key_size}")]
|
||||
DealingOutOfRange {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
index: DealingIndex,
|
||||
key_size: u32,
|
||||
},
|
||||
|
||||
#[error("dealer {dealer} has attempted to commit dealing metadata for epoch {epoch_id} for dealing index {dealing_index} with {chunks} chunks while at most {} chunks are allowed", MAX_DEALING_CHUNKS)]
|
||||
TooFragmentedMetadata {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunks: usize,
|
||||
},
|
||||
|
||||
#[error("the declared chunk split for epoch {epoch_id} from dealer {dealer} for dealing index {dealing_index} is uneven. first chunk has size of {first_chunk_size} while chunk at index {chunk_index} has {size}")]
|
||||
UnevenChunkSplit {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
first_chunk_size: usize,
|
||||
size: usize,
|
||||
},
|
||||
|
||||
#[error("the received chunk for epoch {epoch_id} from dealer {dealer} at dealing index {dealing_index} at chunk index {chunk_index} has inconsistent length. the metadata contains length of {metadata_length} while the received data is {received} bytes long")]
|
||||
InconsistentChunkLength {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
chunk_index: ChunkIndex,
|
||||
metadata_length: usize,
|
||||
received: usize,
|
||||
},
|
||||
|
||||
#[error("dealer {dealer} has attempted to commit dealing metadata for epoch {epoch_id} for dealing index {dealing_index} zero chunks")]
|
||||
EmptyMetadata {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
},
|
||||
|
||||
#[error("metadata for dealing for epoch {epoch_id} from {dealer} at index {dealing_index} does not exist")]
|
||||
UnavailableDealingMetadata {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
},
|
||||
|
||||
#[error("metadata for dealing for epoch {epoch_id} from {dealer} at index {dealing_index} already exists")]
|
||||
MetadataAlreadyExists {
|
||||
epoch_id: EpochId,
|
||||
dealer: Addr,
|
||||
dealing_index: DealingIndex,
|
||||
},
|
||||
|
||||
#[error("This dealer has already committed {commitment}")]
|
||||
AlreadyCommitted { commitment: String },
|
||||
|
||||
#[error("No verification key committed for owner {owner}")]
|
||||
NoCommitForOwner { owner: String },
|
||||
|
||||
#[error("failed to parse {value} into a valid SemVer version: {error_message}")]
|
||||
SemVerFailure {
|
||||
value: String,
|
||||
error_message: String,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::Addr;
|
||||
use cw4::Cw4Contract;
|
||||
use cw_controllers::Admin;
|
||||
use cw_storage_plus::Item;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// unique items
|
||||
pub const STATE: Item<State> = Item::new("state");
|
||||
pub const MULTISIG: Admin = Admin::new("multisig");
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
|
||||
pub struct State {
|
||||
pub mix_denom: String,
|
||||
pub multisig_addr: Addr,
|
||||
pub group_addr: Cw4Contract,
|
||||
}
|
||||
pub mod queries;
|
||||
pub mod storage;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::state::storage::STATE;
|
||||
use cosmwasm_std::{StdResult, Storage};
|
||||
use nym_coconut_dkg_common::types::State;
|
||||
|
||||
pub(crate) fn query_state(storage: &dyn Storage) -> StdResult<State> {
|
||||
STATE.load(storage)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cw_controllers::Admin;
|
||||
use cw_storage_plus::Item;
|
||||
use nym_coconut_dkg_common::types::State;
|
||||
|
||||
// unique items
|
||||
pub const DKG_ADMIN: Admin = Admin::new("dkg-admin");
|
||||
|
||||
pub const STATE: Item<State> = Item::new("state");
|
||||
|
||||
pub const MULTISIG: Admin = Admin::new("multisig");
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::Addr;
|
||||
use nym_coconut_dkg_common::dealer::DealerDetails;
|
||||
use nym_coconut_dkg_common::dealing::PartialContractDealing;
|
||||
use nym_coconut_dkg_common::types::ContractSafeBytes;
|
||||
use nym_coconut_dkg_common::verification_key::ContractVKShare;
|
||||
|
||||
@@ -20,13 +21,22 @@ pub fn vk_share_fixture(owner: &str, index: u64) -> ContractVKShare {
|
||||
}
|
||||
|
||||
pub fn dealing_bytes_fixture() -> ContractSafeBytes {
|
||||
ContractSafeBytes(vec![])
|
||||
ContractSafeBytes(vec![1, 2, 3])
|
||||
}
|
||||
|
||||
pub fn partial_dealing_fixture() -> PartialContractDealing {
|
||||
PartialContractDealing {
|
||||
chunk_index: 0,
|
||||
dealing_index: 0,
|
||||
data: ContractSafeBytes(vec![1, 2, 3]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dealer_details_fixture(assigned_index: u64) -> DealerDetails {
|
||||
DealerDetails {
|
||||
address: Addr::unchecked(format!("owner{}", assigned_index)),
|
||||
bte_public_key_with_proof: "".to_string(),
|
||||
ed25519_identity: "".to_string(),
|
||||
announce_address: "".to_string(),
|
||||
assigned_index,
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use cosmwasm_std::{
|
||||
QuerierResult, SystemResult, WasmQuery,
|
||||
};
|
||||
use cw4::{Cw4QueryMsg, Member, MemberListResponse, MemberResponse};
|
||||
use lazy_static::lazy_static;
|
||||
use nym_coconut_dkg_common::dealing::DEFAULT_DEALINGS;
|
||||
use nym_coconut_dkg_common::msg::InstantiateMsg;
|
||||
use nym_coconut_dkg_common::types::DealerDetails;
|
||||
use std::sync::Mutex;
|
||||
@@ -20,9 +20,7 @@ pub const ADMIN_ADDRESS: &str = "admin address";
|
||||
pub const GROUP_CONTRACT: &str = "group contract address";
|
||||
pub const MULTISIG_CONTRACT: &str = "multisig contract address";
|
||||
|
||||
lazy_static! {
|
||||
pub static ref GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(vec![]);
|
||||
}
|
||||
pub(crate) static GROUP_MEMBERS: Mutex<Vec<(Member, u64)>> = Mutex::new(Vec::new());
|
||||
|
||||
pub fn add_fixture_dealer(deps: DepsMut<'_>) {
|
||||
let owner = Addr::unchecked("owner");
|
||||
@@ -33,6 +31,7 @@ pub fn add_fixture_dealer(deps: DepsMut<'_>) {
|
||||
&DealerDetails {
|
||||
address: owner.clone(),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
ed25519_identity: String::new(),
|
||||
announce_address: String::new(),
|
||||
assigned_index: 100,
|
||||
},
|
||||
@@ -87,6 +86,7 @@ pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>>
|
||||
multisig_addr: String::from(MULTISIG_CONTRACT),
|
||||
time_configuration: None,
|
||||
mix_denom: TEST_MIX_DENOM.to_string(),
|
||||
key_size: DEFAULT_DEALINGS as u32,
|
||||
};
|
||||
let env = mock_env();
|
||||
let info = mock_info(ADMIN_ADDRESS, &[]);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// Copyright 2022-2024 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::verification_key_shares::storage;
|
||||
@@ -6,7 +6,23 @@ use crate::verification_key_shares::storage::vk_shares;
|
||||
use cosmwasm_std::{Deps, Order, StdResult};
|
||||
use cw_storage_plus::Bound;
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_coconut_dkg_common::verification_key::PagedVKSharesResponse;
|
||||
use nym_coconut_dkg_common::verification_key::{PagedVKSharesResponse, VkShareResponse};
|
||||
|
||||
// TODO: unit tests
|
||||
pub fn query_vk_share(
|
||||
deps: Deps<'_>,
|
||||
owner: String,
|
||||
epoch_id: EpochId,
|
||||
) -> StdResult<VkShareResponse> {
|
||||
let owner = deps.api.addr_validate(&owner)?;
|
||||
let share = vk_shares().may_load(deps.storage, (&owner, epoch_id))?;
|
||||
|
||||
Ok(VkShareResponse {
|
||||
owner,
|
||||
epoch_id,
|
||||
share,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_vk_shares_paged(
|
||||
deps: Deps<'_>,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::constants::{VK_SHARES_EPOCH_ID_IDX_NAMESPACE, VK_SHARES_PK_NAMESPACE};
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
|
||||
@@ -6,9 +6,9 @@ use crate::dealers::storage as dealers_storage;
|
||||
use crate::epoch_state::storage::CURRENT_EPOCH;
|
||||
use crate::epoch_state::utils::check_epoch_state;
|
||||
use crate::error::ContractError;
|
||||
use crate::state::{MULTISIG, STATE};
|
||||
use crate::state::storage::{MULTISIG, STATE};
|
||||
use crate::verification_key_shares::storage::vk_shares;
|
||||
use cosmwasm_std::{Addr, DepsMut, Env, MessageInfo, Response};
|
||||
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
|
||||
use nym_coconut_dkg_common::types::EpochState;
|
||||
use nym_coconut_dkg_common::verification_key::{
|
||||
to_cosmos_msg, ContractVKShare, VerificationKeyShare,
|
||||
@@ -54,6 +54,7 @@ pub fn try_commit_verification_key_share(
|
||||
resharing,
|
||||
env.contract.address.to_string(),
|
||||
STATE.load(deps.storage)?.multisig_addr.to_string(),
|
||||
// TODO: make this value configurable
|
||||
env.block
|
||||
.time
|
||||
.plus_seconds(BLOCK_TIME_FOR_VERIFICATION_SECS),
|
||||
@@ -65,9 +66,11 @@ pub fn try_commit_verification_key_share(
|
||||
pub fn try_verify_verification_key_share(
|
||||
deps: DepsMut<'_>,
|
||||
info: MessageInfo,
|
||||
owner: Addr,
|
||||
owner: String,
|
||||
resharing: bool,
|
||||
) -> Result<Response, ContractError> {
|
||||
let owner = deps.api.addr_validate(&owner)?;
|
||||
|
||||
check_epoch_state(
|
||||
deps.storage,
|
||||
EpochState::VerificationKeyFinalization { resharing },
|
||||
@@ -91,10 +94,11 @@ pub fn try_verify_verification_key_share(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::epoch_state::transactions::advance_epoch_state;
|
||||
use crate::epoch_state::transactions::{advance_epoch_state, try_initiate_dkg};
|
||||
use crate::support::tests::helpers;
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, MULTISIG_CONTRACT};
|
||||
use crate::support::tests::helpers::{add_fixture_dealer, ADMIN_ADDRESS, MULTISIG_CONTRACT};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::Addr;
|
||||
use cw_controllers::AdminError;
|
||||
use nym_coconut_dkg_common::dealer::DealerDetails;
|
||||
use nym_coconut_dkg_common::types::{EpochState, TimeConfiguration};
|
||||
@@ -103,6 +107,8 @@ 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();
|
||||
|
||||
let info = mock_info("requester", &[]);
|
||||
let share = "share".to_string();
|
||||
|
||||
@@ -122,6 +128,7 @@ mod tests {
|
||||
let dealer_details = DealerDetails {
|
||||
address: dealer.clone(),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
ed25519_identity: String::new(),
|
||||
announce_address: announce_address.clone(),
|
||||
assigned_index: 1,
|
||||
};
|
||||
@@ -149,6 +156,8 @@ 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();
|
||||
|
||||
let info = mock_info("requester", &[]);
|
||||
let share = "share".to_string();
|
||||
|
||||
@@ -163,7 +172,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
expected_state: EpochState::VerificationKeySubmission { resharing: false }
|
||||
.to_string()
|
||||
}
|
||||
@@ -193,6 +202,7 @@ mod tests {
|
||||
let dealer_details = DealerDetails {
|
||||
address: dealer.clone(),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
ed25519_identity: String::new(),
|
||||
announce_address: String::new(),
|
||||
assigned_index: 1,
|
||||
};
|
||||
@@ -223,8 +233,10 @@ 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();
|
||||
|
||||
let info = mock_info("requester", &[]);
|
||||
let owner = Addr::unchecked("owner");
|
||||
let owner = "owner".to_string();
|
||||
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
|
||||
|
||||
let ret =
|
||||
@@ -233,7 +245,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
ret,
|
||||
ContractError::IncorrectEpochState {
|
||||
current_state: EpochState::default().to_string(),
|
||||
current_state: EpochState::PublicKeySubmission { resharing: false }.to_string(),
|
||||
expected_state: EpochState::VerificationKeyFinalization { resharing: false }
|
||||
.to_string()
|
||||
}
|
||||
@@ -280,7 +292,9 @@ mod tests {
|
||||
fn verify_vk_share() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let mut env = mock_env();
|
||||
let owner = Addr::unchecked("owner");
|
||||
try_initiate_dkg(deps.as_mut(), env.clone(), mock_info(ADMIN_ADDRESS, &[])).unwrap();
|
||||
|
||||
let owner = "owner".to_string();
|
||||
let info = mock_info(owner.as_ref(), &[]);
|
||||
let share = "share".to_string();
|
||||
let multisig_info = mock_info(MULTISIG_CONTRACT, &[]);
|
||||
@@ -298,13 +312,18 @@ mod tests {
|
||||
advance_epoch_state(deps.as_mut(), env.clone()).unwrap();
|
||||
|
||||
let dealer_details = DealerDetails {
|
||||
address: owner.clone(),
|
||||
address: Addr::unchecked(&owner),
|
||||
bte_public_key_with_proof: String::new(),
|
||||
ed25519_identity: String::new(),
|
||||
announce_address: String::new(),
|
||||
assigned_index: 1,
|
||||
};
|
||||
dealers_storage::current_dealers()
|
||||
.save(deps.as_mut().storage, &owner, &dealer_details)
|
||||
.save(
|
||||
deps.as_mut().storage,
|
||||
&Addr::unchecked(&owner),
|
||||
&dealer_details,
|
||||
)
|
||||
.unwrap();
|
||||
try_commit_verification_key_share(deps.as_mut(), env.clone(), info, share, false).unwrap();
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ use cw4::Member;
|
||||
use cw_multi_test::Executor;
|
||||
use cw_utils::{Duration, Threshold};
|
||||
use nym_coconut_dkg_common::msg::ExecuteMsg::{
|
||||
AdvanceEpochState, CommitVerificationKeyShare, RegisterDealer,
|
||||
AdvanceEpochState, CommitVerificationKeyShare, InitiateDkg, RegisterDealer,
|
||||
};
|
||||
use nym_coconut_dkg_common::msg::InstantiateMsg as DkgInstantiateMsg;
|
||||
use nym_coconut_dkg_common::msg::QueryMsg::GetVerificationKeys;
|
||||
@@ -75,6 +75,7 @@ fn dkg_proposal() {
|
||||
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(
|
||||
@@ -99,11 +100,20 @@ fn dkg_proposal() {
|
||||
)
|
||||
.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,
|
||||
},
|
||||
|
||||
@@ -41,7 +41,7 @@ bs58 = "0.4.0"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
time = { version = "0.3", features = ["macros"] }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
semver = { workspace = true, default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
rand_chacha = "0.2"
|
||||
|
||||
@@ -20,7 +20,7 @@ cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.5.0" }
|
||||
nym-name-service-common = { path = "../../common/cosmwasm-smart-contracts/name-service" }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
semver = { workspace = true, default-features = false }
|
||||
serde = { version = "1.0.155", default-features = false, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ cw-utils = { workspace = true }
|
||||
cw2 = { workspace = true }
|
||||
nym-contracts-common = { path = "../../common/cosmwasm-smart-contracts/contracts-common", version = "0.5.0" }
|
||||
nym-service-provider-directory-common = { path = "../../common/cosmwasm-smart-contracts/service-provider-directory" }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
semver = { workspace = true, default-features = false }
|
||||
serde = { version = "1.0.155", default-features = false, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ cw-storage-plus = { workspace = true, features = ["iterator"] }
|
||||
|
||||
serde = { version = "1.0", default-features = false, features = ["derive"] }
|
||||
thiserror ={ workspace = true }
|
||||
semver = { version = "1.0.16", default-features = false }
|
||||
semver = { workspace = true, default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
rand_chacha = "0.3.1"
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
- [NymConnect X Electrum](tutorials/electrum.md)
|
||||
- [NymConnect X Firo wallet](tutorials/firo.md)
|
||||
|
||||
# Code Examples
|
||||
# Code Examples
|
||||
|
||||
- [Custom Service Providers](examples/custom-services.md)
|
||||
- [Apps Using Network Requesters](examples/using-nrs.md)
|
||||
@@ -49,9 +49,9 @@
|
||||
- [Preparing Your Service](tutorials/cosmos-service/service.md)
|
||||
- [Preparing Your Service pt2](tutorials/cosmos-service/service-src.md)
|
||||
- [Querying the Chain](tutorials/cosmos-service/querying.md)
|
||||
|
||||
|
||||
- [Typescript](tutorials/typescript.md)
|
||||
- [Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
|
||||
- [Simple Service Provider](tutorials/simple-service-provider/simple-service-provider.md)
|
||||
- [Tutorial Overview](tutorials/simple-service-provider/overview.md)
|
||||
- [Preparing Your User Client Environment](tutorials/simple-service-provider/preparating-env.md)
|
||||
- [Building Your User Client](tutorials/simple-service-provider/user-client.md)
|
||||
@@ -68,6 +68,12 @@
|
||||
|
||||
# Events
|
||||
|
||||
- [CryptoTalk](events/cryptotalk/nym-vpn.md)
|
||||
- [NymVPN Linux Guide](events/cryptotalk/nym-vpn-linux.md)
|
||||
- [NymVPN MacOS Guide](events/cryptotalk/nym-vpn-mac.md)
|
||||
- [NymVPN Testing](events/cryptotalk/nym-vpn-testing.md)
|
||||
- [NymVPN Troubleshooting](events/cryptotalk/nym-vpn-troubleshooting.md)
|
||||
- [NymVPN FAQ](events/cryptotalk/nym-vpn-faq.md)
|
||||
- [Web3Privacy Now](./events/web3-privacy.md)
|
||||
- [HCPP23-serinko](./events/hcpp23-serinko.md)
|
||||
- [HCPP23-max](./events/hcpp23-max.md)
|
||||
@@ -83,7 +89,7 @@
|
||||
- [Community Applications and Guides](community-resources/community-applications-and-guides.md)
|
||||
- [Change Service Grantee Information](info-request.md)
|
||||
|
||||
---
|
||||
---
|
||||
# Misc.
|
||||
- [Code of Conduct](coc.md)
|
||||
- [Licensing](licensing.md)
|
||||
|
||||
@@ -1,457 +0,0 @@
|
||||
# NymVPN alpha
|
||||
|
||||
Nym has announced [NymVPN](https://nymvpn.com/en) and presented the [NymVPN Litepaper](https://nymvpn.com/nymVPN-litepaper.pdf). At CCC 2023 we have the unique opportunity to do the first alpha public testing. This page provides a how to guide, explaining steps to install and run NymVPN CLI and GUI on the Nym Sandbox testnet.
|
||||
|
||||
NymVPN is a client that uses [Nym Mixnet](https://nymtech.net) to anonymise users entire internet traffic.
|
||||
|
||||
The default is to run in 5-hop mode:
|
||||
|
||||
```
|
||||
┌─►mix──┐ mix mix
|
||||
│ │
|
||||
Entry │ │ Exit
|
||||
client ───► Gateway ──┘ mix │ mix ┌─►mix ───► Gateway ───► internet
|
||||
│ │
|
||||
│ │
|
||||
mix └─►mix──┘ mix
|
||||
```
|
||||
|
||||
Users can switch to 2-hop only mode, which is a faster but less private option. In this mode traffic is only sent between the two Gateway's, and is not passed between Mix Nodes.
|
||||
|
||||
The client can optionally do the first connection to the entry gateway using Wireguard. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device.
|
||||
|
||||
```admonish warning
|
||||
NymVPN is an experimental software and it's for [testing](./nym-vpn.md#testing) purposes only. All users testing the client are expected to sign GDPR Information Sheet and Consent Form (shared at the event), and follow the steps listed in the form [*NymVPN User research at 37c3*](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko).
|
||||
```
|
||||
|
||||
## Goals
|
||||
|
||||
This alpha testing will help:
|
||||
|
||||
* Stabilise NymVPN client
|
||||
* Understand NymVPN client behavior in various setups (OS, connectivity, etc.)
|
||||
* Stabilize the VPN infrastructure and improve its reliability / speed / features (e.g. IPv6 support)
|
||||
* Load test the network in Sandbox environment and identify / anticipate potential weaknesses
|
||||
|
||||
|
||||
```admonish info
|
||||
Our alpha testing round is done live with some participants at CCC 2023. This guide will not work for everyone, as the NymVPN binaries aren't publicly accessible yet. Note that this setup of Nym testnet Sandbox environment is limited for CCC 2023 event and some of the configurations will not work in the future.
|
||||
```
|
||||
|
||||
> **If you commit to test NymVPN alpha, please start with the [user research form](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko) where all the steps will be provided**.
|
||||
|
||||
## Preparation
|
||||
|
||||
> Any syntax in `<>` brackets is a user's/version unique variable. Exchange with a corresponding name without the `<>` brackets.
|
||||
|
||||
We have CLI and UI binaries available for Linux (Debian based) and macOS.
|
||||
|
||||

|
||||
|
||||
* Visit the [releases page](https://github.com/nymtech/nym/releases/tag/ccc) to download the binary for your system.
|
||||
* Open terminal in the same directory and make executable by running:
|
||||
|
||||
```sh
|
||||
# for CLI
|
||||
chmod +x ./nym-vpn-cli
|
||||
|
||||
# for GUI
|
||||
chmod +x ./nym-vpn_0.0.0_amd64.AppImage
|
||||
# make sure your path to package is correct and the package name as well
|
||||
```
|
||||
|
||||
* If you prefer to use the `.deb` version for installation (Linux only), open terminal in the same directory and run:
|
||||
```
|
||||
sudo dpkg -i ./<PACKAGE_NAME>.deb
|
||||
# or
|
||||
sudo apt-get install -f ./<PACKAGE_NAME>.deb
|
||||
```
|
||||
|
||||
* For CLI: Create Sandbox environment config file by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the same directory as your NymVPN binaries. In case of GUI setup, see the steps below.
|
||||
|
||||
### GUI configuration
|
||||
|
||||
* Create a NymVPN config directory called `nym-vpn` in your `~/.config`, either manually or by a command:
|
||||
```sh
|
||||
# for Linux
|
||||
mkdir $HOME/.config/nym-vpn/
|
||||
|
||||
# for macOS
|
||||
mkdir $HOME/Library/Application Support/nym-vpn/
|
||||
```
|
||||
* Create the network config by saving [this](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) as `sandbox.env` in the directory `nym-vpn` you just created
|
||||
* Create the main config file called `config.toml` in the same directory with this content:
|
||||
```toml
|
||||
# for Linux
|
||||
env_config_file = "/home/<USER>/.config/nym-vpn/sandbox.env" # change <USER> to your username
|
||||
entry_node_location = "DE" # two letters country code
|
||||
|
||||
# for macOS
|
||||
env_config_file = "$HOME/Library/Application Support/nym-vpn/"
|
||||
entry_node_location = "DE" # two letters country code
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
***For NymVPN to work, all other VPNs must be switched off!***
|
||||
|
||||
**Note:** At this alpha stage of NymVPN, network connection (wifi) must be re-connected after or in-between NymVPN testing rounds.
|
||||
|
||||
* Get your private key for wireguard setup [here](https://nymvpn.com/en/37c3)
|
||||
* See a JSON list of all Gateways [here](https://nymvpn.com/en/ccc/api/gateways)
|
||||
* As NymVPN need a permission for network settings, it is run with `sudo` root command
|
||||
|
||||
### CLI
|
||||
|
||||
Make sure your terminal is open in the same directory as your `nym-vpn-cli` binary.
|
||||
|
||||
```admonish info
|
||||
The entire command syntax with all the needed arguments' values and your private key for testing purposes can be found on our event webpage [nymvpn.com/en/37c3](https://nymvpn.com/en/37c3).
|
||||
```
|
||||
|
||||
Running a help command:
|
||||
|
||||
```sh
|
||||
./nym-vpn-cli --help
|
||||
```
|
||||
|
||||
~~~admonish example collapsible=true title="Console output"
|
||||
```
|
||||
Usage: nym-vpn-cli [OPTIONS]
|
||||
|
||||
Options:
|
||||
-c, --config-env-file <CONFIG_ENV_FILE>
|
||||
Path pointing to an env file describing the network
|
||||
--mixnet-client-path <MIXNET_CLIENT_PATH>
|
||||
Path to the data directory of a previously initialised mixnet client, where the keys reside
|
||||
--entry-gateway-id <ENTRY_GATEWAY_ID>
|
||||
Mixnet public ID of the entry gateway
|
||||
--entry-gateway-country <ENTRY_GATEWAY_COUNTRY>
|
||||
Auto-select entry gateway by country ISO
|
||||
--exit-router-address <EXIT_ROUTER_ADDRESS>
|
||||
Mixnet recipient address
|
||||
--exit-gateway-id <EXIT_GATEWAY_ID>
|
||||
|
||||
--exit-router-country <EXIT_ROUTER_COUNTRY>
|
||||
Mixnet recipient address
|
||||
--enable-wireguard
|
||||
Enable the wireguard traffic between the client and the entry gateway
|
||||
--private-key <PRIVATE_KEY>
|
||||
Associated private key
|
||||
--wg-ip <WG_IP>
|
||||
The IP address of the wireguard interface
|
||||
--ip <IP>
|
||||
The IP address of the TUN device
|
||||
--mtu <MTU>
|
||||
The MTU of the TUN device
|
||||
--disable-routing
|
||||
Disable routing all traffic through the VPN TUN device
|
||||
--enable-two-hop
|
||||
Enable two-hop mixnet traffic. This means that traffic jumps directly from entry gateway to exit gateway
|
||||
--enable-poisson-rate
|
||||
Enable Poisson process rate limiting of outbound traffic
|
||||
-h, --help
|
||||
Print help
|
||||
-V, --version
|
||||
Print version
|
||||
|
||||
|
||||
```
|
||||
~~~
|
||||
|
||||
To run the CLI a few things need to be specified:
|
||||
|
||||
```sh
|
||||
sudo ./nym-vpn-cli -c ./sandbox.env --entry-gateway-id <ENTRY_GATEWAY_ID> --exit-router-address <EXIT_ROUTER_ADDRESS> --enable-wireguard --private-key <PRIVATE_KEY> --wg-ip <WG_IP>
|
||||
```
|
||||
|
||||
### Options Flags
|
||||
|
||||
Here is a list of the options and their descriptions. Some are essential, some are more technical and not needed to adjusted by users:
|
||||
|
||||
- `-c` is a path to the [Sandbox config](https://raw.githubusercontent.com/nymtech/nym/develop/envs/sandbox.env) file saved as `sandbox.env`
|
||||
- `--entry-gateway-id`: paste one of the values labeled with a key `"identityKey"` (without `" "`) from [here](https://nymvpn.com/en/ccc/api/gateways)
|
||||
- `--exit-router-address`: paste one of the values labeled with a key `"address"` (without `" "`) from here [here](https://nymvpn.com/en/ccc/api/gateways)
|
||||
- `--enable-wireguard`: Enable the wireguard traffic between the client and the entry gateway. NymVPN uses Mullvad libraries for wrapping `wireguard-go` and to setup local routing rules to route all traffic to the TUN virtual network device
|
||||
- `--wg-ip`: The address of the wireguard interface, you can get it [here](https://nymvpn.com/en/37c3)
|
||||
- `--private-key`: get your private key for testing purposes [here](https://nymvpn.com/en/37c3)
|
||||
- `--enable-two-hop` is a faster setup where the traffic is routed from the client to Entry Gateway and directly to Exit Gateway (default is 5-hops)
|
||||
|
||||
**More advanced options**
|
||||
|
||||
- `--enable-poisson`: Enables process rate limiting of outbound traffic (disabled by default). It means that NymVPN client will send packets at a steady stream to the Entry Gateway. By default it's on average one sphinx packet per 20ms, but there is some randomness (poisson distribution). When there are no real data to fill the sphinx packets with, cover packets are generated instead.
|
||||
- `--ip` is the IP address of the TUN device. That is the IP address of the local private network that is set up between local client and the Exit Gateway.
|
||||
- `--mtu`: The MTU of the TUN device. That is the max IP packet size of the local private network that is set up between local client and the Exit Gateway.
|
||||
- `--disable-routing`: Disable routing all traffic through the VPN TUN device.
|
||||
|
||||
### GUI
|
||||
|
||||
If you installed the `.deb` package you may be able to have a NymVPN application icon in your app menu. However this may not work as the application needs root permission.
|
||||
|
||||
Make sure you went through the GUI configuration in the [preparation section](./nym-vpn.md#gui-configuration). Then open terminal in the same directory where you [installed](./nym-vpn.md#preparation) the binary run:
|
||||
|
||||
```sh
|
||||
sudo -E ./nym-vpn_0.0.0_amd64.AppImage
|
||||
|
||||
# In case you use another binary, just add the correct name as:
|
||||
sudo -E ./<FULL_BINARY_NAME>
|
||||
```
|
||||
|
||||
In case of errors, see [troubleshooting section](./nym-vpn.md#macos-alert-on-nymvpn-ui-startup).
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
One of the main aims of the demo is testing; your results will help us to make NymVPN robust and stabilise both the client and the network through provided measurements. Before you go into testing, make sure you went through [preparation steps](./nym-vpn.md#preparation) and the [CLI binary](https://github.com/nymtech/nym/releases/tag/ccc) for your system is ready to run.
|
||||
|
||||
1. Create a directory called `nym-vpn-tests` and copy your `nym-vpn-cli` binary and `sandbox.env` to that directory
|
||||
2. Copy the [block below](./nym-vpn.md#testssh) and save it to the same folder as `tests.sh`
|
||||
3. Open terminal in the same directory
|
||||
4. Turn off any existing VPN's (including the NymVPN instances) and make the script executable by running `chmod +x ./tests.sh`
|
||||
5. Run the tests: `sudo ./tests.sh`
|
||||
6. In case of errors, see [troubleshooting section](./nym-vpn.md#missing-jq-error) below
|
||||
7. The script will print a JSON view of existing Gateways and prompt you to:
|
||||
- ***(Make sure to use two different Gateways for entry and exit!)***
|
||||
- `enter a gateway ID:` paste one of the values labeled with a key `"identityKey"` printed above (without `" "`)
|
||||
- `enter an exit address:` paste one of the values labeled with a key `"address"` printed above (without `" "`)
|
||||
- `enable WireGuard? (yes/no):` if you chose yes, find your private key and wireguard IP [here](https://nymvpn.com/en/37c3)
|
||||
8. Note that the testing script doesn't print many logs, in case of doubts you can check logs in the log file `temp_log.txt` located in the same directory.
|
||||
9. The script shall run the tests and generate a folder called `tests_<LONG_STRING>` and files `perf_test_results.log` or `two_hop_perf_test_results.log` as well as some temp files. This is how the directory structure will look like:
|
||||
```sh
|
||||
nym-vpn-tests
|
||||
├── tests.sh
|
||||
├── nym-vpn-cli
|
||||
├── sandbox.env
|
||||
├── perf_test_results.log
|
||||
├── tests_<LONG_STRING>
|
||||
│ ├── api_response_times.txt
|
||||
│ ├── download_time.txt
|
||||
│ └── ping_results.txt
|
||||
├── timeout
|
||||
└── two_hop_perf_test_results.log
|
||||
```
|
||||
10. In case of errors, see [troubleshooting section](./nym-vpn.md#missing-jq-error) below
|
||||
11. When the tests are finished, remove the `nym-vpn-cli` binary from the folder and compress it as `nym-vpn-tests.zip`
|
||||
12. Upload this compressed file to the [questionnaire](https://opnform.com/forms/nymvpn-user-research-at-37c3-yccqko) upload field when prompted.
|
||||
|
||||
#### tests.sh
|
||||
|
||||
```sh
|
||||
#!/bin/bash
|
||||
|
||||
ENDPOINT="https://sandbox-nym-api1.nymtech.net/api/v1/gateways/described"
|
||||
json_array=()
|
||||
echo "🚀 🏎 - please be patient, i'm fetching you your entry points - 🚀 🏎 "
|
||||
|
||||
data=$(curl -s "$ENDPOINT" | jq -c '.[] | {host: .bond.gateway.host, hostname: .self_described.host_information.hostname, identity_key: .bond.gateway.identity_key, exitGateway: .self_described.ip_packet_router.address}')
|
||||
|
||||
while IFS= read -r entry; do
|
||||
host=$(echo "$entry" | jq -r '.host')
|
||||
hostname=$(echo "$entry" | jq -r '.hostname')
|
||||
identity_key=$(echo "$entry" | jq -r '.identity_key')
|
||||
exit_gateway_address=$(echo "$entry" | jq -r '.exitGateway // empty')
|
||||
valid_ip=$(echo "$host")
|
||||
|
||||
if [ -n "$exit_gateway_address" ]; then
|
||||
exit_gateway="{\"address\": \"${exit_gateway_address}\"}"
|
||||
else
|
||||
exit_gateway="{}"
|
||||
fi
|
||||
if [[ $valid_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
country_info=$(curl -s "http://ipinfo.io/${valid_ip}/country" | tr -d '\n')
|
||||
country_info_escaped=$(echo "$country_info" | tr -d '\n' | jq -aRs . | tr -d '"')
|
||||
else
|
||||
country_info_escaped=""
|
||||
fi
|
||||
json_object="{\"hostname\": \"${hostname}\", \"identityKey\": \"${identity_key}\", \"exitGateway\": ${exit_gateway}, \"location\": \"${country_info_escaped}\"}"
|
||||
json_array+=("$json_object")
|
||||
done < <(echo "$data")
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "error fetching data from endpoint"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
download_file() {
|
||||
local file_url=$1
|
||||
local output_file=$2
|
||||
local time_file=$3
|
||||
|
||||
echo "starting download speed test..."
|
||||
local start_time=$(date +%s)
|
||||
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
|
||||
wget -O $output_file $file_url
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
curl -o $output_file $file_url
|
||||
fi
|
||||
local end_time=$(date +%s)
|
||||
local elapsed_time=$((end_time - start_time))
|
||||
echo "download speed test completed in $elapsed_time seconds." >"$time_file"
|
||||
}
|
||||
|
||||
if ! command -v jq &>/dev/null; then
|
||||
echo "jq is not installed. Please install jq to proceed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
temp_log_file="temp_log.txt"
|
||||
|
||||
perform_tests() {
|
||||
local gateway_id=$1
|
||||
local exit_address=$2
|
||||
local test_directory="tests_${gateway_id}_${exit_address}"
|
||||
local file_url="http://ipv4.download.thinkbroadband.com/2MB.zip"
|
||||
|
||||
mkdir -p "$test_directory"
|
||||
local ping_results_file="${test_directory}/ping_results.txt"
|
||||
local download_time_file="${test_directory}/download_time.txt"
|
||||
local api_response_file="${test_directory}/api_response_times.txt"
|
||||
|
||||
# ping test
|
||||
echo "starting ping test..."
|
||||
for site in google.com youtube.com facebook.com baidu.com wikipedia.org amazon.com twitter.com instagram.com yahoo.com ebay.com netflix.com; do
|
||||
ping -c 4 $site >>"$ping_results_file"
|
||||
done
|
||||
echo "ping test completed. Results saved in $ping_results_file"
|
||||
|
||||
# download speed test
|
||||
download_file $file_url /dev/null "$download_time_file"
|
||||
|
||||
# api test
|
||||
local api_endpoint="https://validator.nymtech.net/api/v1/mixnodes"
|
||||
local iterations=10
|
||||
>"$api_response_file"
|
||||
for i in $(seq 1 $iterations); do
|
||||
local start_time=$(date +%s)
|
||||
local response=$(curl -s -o /dev/null -w '%{http_code}' $api_endpoint)
|
||||
local end_time=$(date +%s)
|
||||
|
||||
local elapsed_seconds=$((end_time - start_time))
|
||||
local hours=$((elapsed_seconds / 3600))
|
||||
local minutes=$(((elapsed_seconds % 3600) / 60))
|
||||
local seconds=$((elapsed_seconds % 60))
|
||||
|
||||
local human_readable_time=$(printf "%02dh:%02dm:%02ds" $hours $minutes $seconds)
|
||||
echo "iteration $i: response Time = ${human_readable_time}, status code = $response" >>"$api_response_file"
|
||||
done
|
||||
echo "api response test completed. Results saved in $api_response_file."
|
||||
}
|
||||
|
||||
printf "%s\n" "${json_array[@]}" | jq -s .
|
||||
|
||||
read -p "enter a gateway ID: " identity_key
|
||||
read -p "enter an exit address: " exit_address
|
||||
|
||||
while true; do
|
||||
read -p "enable WireGuard? (yes/no): " enable_wireguard
|
||||
enable_wireguard=$(echo "$enable_wireguard" | tr '[:upper:]' '[:lower:]')
|
||||
|
||||
case "$enable_wireguard" in
|
||||
"yes")
|
||||
read -p "enter your WireGuard private key: " priv_key
|
||||
read -p "enter your WireGuard IP: " wg_ip
|
||||
wireguard_options="--enable-wireguard --private-key $priv_key --wg-ip $wg_ip"
|
||||
break
|
||||
;;
|
||||
"no")
|
||||
wireguard_options=""
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "invalid response. please enter 'yes' or 'no'."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
sudo ./nym-vpn-cli -c sandbox.env --entry-gateway-id ${identity_key} --exit-router-address ${exit_address} --enable-two-hop $wireguard_options >"$temp_log_file" 2>&1 &
|
||||
|
||||
timeout=15
|
||||
start_time=$(date +%s)
|
||||
while true; do
|
||||
current_time=$(date +%s)
|
||||
if grep -q "received plain" "$temp_log_file"; then
|
||||
echo "successful configuration with identity_key: $identity_key and exit address: $exit_address" >>perf_test_results.log
|
||||
perform_tests "$identity_key" "$exit_address"
|
||||
break
|
||||
fi
|
||||
if ((current_time - start_time > timeout)); then
|
||||
echo "failed to connect with identity_key: $identity_key using the exit address: $exit_address" >>perf_test_results.log
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "terminating nym-vpn-cli..."
|
||||
pkill -f './nym-vpn-cli'
|
||||
sleep 5
|
||||
rm -f "$temp_log_file"
|
||||
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Below are listed some points which may need to be addressed when testing NymVPN alpha. If you crashed into any errors which are not listed, please contact us at the event.
|
||||
|
||||
#### Thread `main` panicked
|
||||
|
||||
If you see a message like:
|
||||
```sh
|
||||
thread 'main' panicked at /Users/runner/.cargo/git/checkouts/mullvadvpn-app-a575cf705b5dfd76/ccfbaa2/talpid-routing/src/unix.rs:301:30:
|
||||
```
|
||||
Restart your wifi connection and start again.
|
||||
|
||||
#### macOS alert on NymVPN UI startup
|
||||
|
||||
If you are running NymVPN on mac OS for the first time, you may see this alert message:
|
||||
|
||||

|
||||
|
||||
1. Head to System Settings -> Privacy & Security and click `Allow anyway`
|
||||
|
||||

|
||||
|
||||
2. Confirm with your password or TouchID
|
||||
|
||||
3. Possibly you may have to confirm again upon running the application
|
||||
|
||||
#### Missing `jq` error
|
||||
|
||||
In case of missing `jq` on Linux (Debian) install it with:
|
||||
```sh
|
||||
# Linux (Debian)
|
||||
sudo apt-get install jq
|
||||
# macOS
|
||||
brew install jq
|
||||
```
|
||||
On some Linux distributions however the [script](./nym-vpn.md#testssh) returns `jq` error even if your system claims that `jq is already the newest version`.
|
||||
In that case, comment the `jq` check in the script as follows:
|
||||
```sh
|
||||
#if ! command -v jq &>/dev/null; then
|
||||
# echo "jq is not installed. Please install jq to proceed."
|
||||
# exit 1
|
||||
#fi
|
||||
```
|
||||
|
||||
#### Error current_time: not found
|
||||
|
||||
When running `sudo sh ./test.sh` you may see an error like: `93: current_time: not found`. This has something to do with the `current_time` setup of your system and on itself shall not have a negative impact on the test. It has nothing to do with the client at all as it only relates to the code in our testing script.
|
||||
|
||||
#### Not connecting to the endpoint
|
||||
|
||||
In case the automatic download of all the Gateways fail (and it shouldn't), you do an easy manual work around:
|
||||
|
||||
1. Open the list of Gateways created by API [here](https://nymvpn.com/en/ccc/api/gateways)
|
||||
2. On top click on `JSON` option (shall be default view) and `Save`
|
||||
3. Save it as `data.json` to the `nym-vpn-tests` folder
|
||||
4. Replace line 3 in the [script `tests.sh`](./nym-vpn.md#testssh) with:
|
||||
```sh
|
||||
NEW_ENDPOINT="http://localhost:8000/data.json"
|
||||
```
|
||||
5. In a new terminal window run:
|
||||
```sh
|
||||
python3 -m http.server 8000
|
||||
```
|
||||
6. Continue with the steps listed in [testing section](./nym-vpn.md#testing)
|
||||
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Chaos Communication Congress 2023
|
||||
|
||||
At [CCC 2019](https://constructiveproof.com/posts/2020-01-27-nym-testnet-up-and-running/) we announced the first demo of the [Nym Mixnet](https://github.com/nymtech/nym). Now, four years later we are honoured to present our new product: [NymVPN](https://blog.nymtech.net/nymvpn-an-invitation-for-privacy-experts-and-enthusiasts-63644139d09d). NymVPN is a client/application which captures the entire network stack of a user's device, sending this traffic through either a 2-hop VPN or through the 5-hop Mixnet. CCC 2023 is the first public demo of both the GUI and CLI versions of NymVPN.
|
||||
|
||||
At first glance Nym platform may seem pretty complex. To make the connection between Nym and the community of developers, operators and users participating on the event as smooth as possible, we created a segment of documentation tailored specifically for the conference. The following pages provide an easy introduction to the NymVPN alpha client. We recommend interested developers to begin with [Nym network overview](https://nymtech.net/docs/architecture/network-overview.html) and the [Mixnet traffic flow](https://nymtech.net/docs/architecture/traffic-flow.html) pages.
|
||||
|
||||
**See pages below for more content:**
|
||||
|
||||
* [NymVPN alpha guide](nym-vpn.md)
|
||||
* [FAQ @ 37c3](faq.md)
|
||||
|
||||
<div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/897010658?h=1f55870fe6&badge=0&autopause=0&player_id=0&app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="NYMVPN alpha demo 37C3"></iframe></div><script src="https://player.vimeo.com/api/player.js"></script>
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user