Compare commits

..

3 Commits

Author SHA1 Message Date
Bogdan-Ștefan Neacşu 30dfa09e18 Simplify the migration to trust the owner more (#1593) 2022-09-05 17:31:06 +03:00
Mark Sinclair 7e7072258d Add nym-cli tool (#1577)
Co-authored-by: tommy <tommyvez@protonmail.com>
2022-09-05 12:06:35 +01:00
Bogdan-Ștefan Neacşu 10221a1767 Migrate heights to timestamps (#1584)
* Migrate heights to timestamps

* Improve test with a possible scenario

* Use account id instead of address, as returned by the query
2022-09-05 12:25:57 +03:00
125 changed files with 9798 additions and 655 deletions
+2 -3
View File
@@ -6,17 +6,16 @@ Post 1.0.0 release, the changelog format is based on [Keep a Changelog](https://
### Added
- nym-cli: added CLI tool for interacting with the Nyx blockchain and Nym mixnet smart contracts ([#1577])
- validator-client: added `query_contract_smart` and `query_contract_raw` on `NymdClient` ([#1558])
### Changed
- validator-client: made `fee` argument optional for `execute` and `execute_multiple` ([#1541])
- wasm-client: fixed build errors on MacOS and changed example JS code to use mainnet ([#1585])
[#1541]: https://github.com/nymtech/nym/pull/1541
[#1558]: https://github.com/nymtech/nym/pull/1558
[#1585]: https://github.com/nymtech/nym/pull/1585
[#1577]: https://github.com/nymtech/nym/pull/1577
## [nym-binaries-1.0.2](https://github.com/nymtech/nym/tree/nym-binaries-1.0.2)
Generated
+213 -112
View File
@@ -583,6 +583,25 @@ dependencies = [
"textwrap 0.15.0",
]
[[package]]
name = "clap_complete"
version = "3.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4179da71abd56c26b54dd0c248cc081c1f43b0a1a7e8448e28e57a29baa993d"
dependencies = [
"clap 3.2.8",
]
[[package]]
name = "clap_complete_fig"
version = "3.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed37b4c0c1214673eba6ad8ea31666626bf72be98ffb323067d973c48b4964b9"
dependencies = [
"clap 3.2.8",
"clap_complete",
]
[[package]]
name = "clap_derive"
version = "3.2.7"
@@ -671,6 +690,18 @@ dependencies = [
"winapi",
]
[[package]]
name = "comfy-table"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "121d8a5b0346092c18a4b2fd6f620d7a06f0eb7ac0a45860939a0884bc579c56"
dependencies = [
"crossterm",
"strum 0.24.1",
"strum_macros 0.24.3",
"unicode-width",
]
[[package]]
name = "config"
version = "0.1.0"
@@ -1027,6 +1058,31 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "crossterm"
version = "0.23.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2102ea4f781910f8a5b98dd061f4c2023f479ce7bb1236330099ceb5a93cf17"
dependencies = [
"bitflags",
"crossterm_winapi",
"libc",
"mio",
"parking_lot 0.12.0",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
dependencies = [
"winapi",
]
[[package]]
name = "crunchy"
version = "0.2.2"
@@ -1128,7 +1184,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8"
dependencies = [
"sct 0.6.1",
"sct",
]
[[package]]
@@ -1904,6 +1960,10 @@ name = "futures-timer"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
dependencies = [
"gloo-timers",
"send_wrapper",
]
[[package]]
name = "futures-util"
@@ -1954,8 +2014,8 @@ dependencies = [
"secp256k1",
"thiserror",
"tokio",
"tokio-tungstenite 0.17.2",
"tungstenite 0.17.3",
"tokio-tungstenite",
"tungstenite",
"url",
"validator-client",
"wasm-bindgen",
@@ -1980,7 +2040,7 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
"tungstenite 0.17.3",
"tungstenite",
]
[[package]]
@@ -2088,6 +2148,18 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "gloo-timers"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d12a7f4e95cfe710f1d624fb1210b7d961a5fb05c4fd942f4feab06e61f590e"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "group"
version = "0.10.0"
@@ -2380,7 +2452,7 @@ dependencies = [
"tokio",
"tokio-rustls",
"tower-service",
"webpki 0.21.4",
"webpki",
]
[[package]]
@@ -2393,12 +2465,12 @@ dependencies = [
"futures-util",
"hyper",
"log",
"rustls 0.19.1",
"rustls",
"rustls-native-certs",
"tokio",
"tokio-rustls",
"webpki 0.21.4",
"webpki-roots 0.21.1",
"webpki",
"webpki-roots",
]
[[package]]
@@ -3048,6 +3120,57 @@ dependencies = [
"libc",
]
[[package]]
name = "nym-cli"
version = "1.0.0"
dependencies = [
"anyhow",
"base64",
"bip39",
"bs58",
"clap 3.2.8",
"clap_complete",
"clap_complete_fig",
"dotenv",
"log",
"network-defaults",
"nym-cli-commands",
"pretty_env_logger",
"serde",
"serde_json",
"tokio",
"validator-client",
]
[[package]]
name = "nym-cli-commands"
version = "1.0.0"
dependencies = [
"base64",
"bip39",
"bs58",
"cfg-if 1.0.0",
"clap 3.2.8",
"comfy-table",
"cosmrs",
"cosmwasm-std",
"handlebars",
"humantime-serde",
"k256",
"log",
"mixnet-contract-common",
"network-defaults",
"rand 0.6.5",
"serde",
"serde_json",
"thiserror",
"time 0.3.9",
"toml",
"url",
"validator-client",
"vesting-contract-common",
]
[[package]]
name = "nym-client"
version = "1.0.2"
@@ -3073,7 +3196,7 @@ dependencies = [
"serde_json",
"sled",
"tokio",
"tokio-tungstenite 0.17.2",
"tokio-tungstenite",
"topology",
"url",
"validator-client",
@@ -3120,7 +3243,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-stream",
"tokio-tungstenite 0.17.2",
"tokio-tungstenite",
"tokio-util 0.7.3",
"url",
"validator-api-requests",
@@ -3194,7 +3317,7 @@ dependencies = [
"statistics-common",
"thiserror",
"tokio",
"tokio-tungstenite 0.14.0",
"tokio-tungstenite",
"websocket-requests",
]
@@ -3211,7 +3334,7 @@ dependencies = [
"statistics-common",
"thiserror",
"tokio",
"tokio-tungstenite 0.14.0",
"tokio-tungstenite",
]
[[package]]
@@ -3265,7 +3388,7 @@ dependencies = [
"schemars",
"serde",
"serde_json",
"strum",
"strum 0.23.0",
"tempfile",
"thiserror",
"ts-rs",
@@ -3346,7 +3469,7 @@ dependencies = [
"nym-types",
"serde",
"serde_json",
"strum",
"strum 0.23.0",
"ts-rs",
"validator-client",
"vesting-contract",
@@ -4714,20 +4837,8 @@ dependencies = [
"base64",
"log",
"ring",
"sct 0.6.1",
"webpki 0.21.4",
]
[[package]]
name = "rustls"
version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033"
dependencies = [
"log",
"ring",
"sct 0.7.0",
"webpki 0.22.0",
"sct",
"webpki",
]
[[package]]
@@ -4737,7 +4848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092"
dependencies = [
"openssl-probe",
"rustls 0.19.1",
"rustls",
"schannel",
"security-framework",
]
@@ -4829,16 +4940,6 @@ dependencies = [
"untrusted",
]
[[package]]
name = "sct"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "sec1"
version = "0.2.1"
@@ -4932,6 +5033,12 @@ dependencies = [
"pest",
]
[[package]]
name = "send_wrapper"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0"
[[package]]
name = "serde"
version = "1.0.136"
@@ -5118,6 +5225,27 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "signal-hook"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
@@ -5330,7 +5458,7 @@ dependencies = [
"once_cell",
"paste",
"percent-encoding",
"rustls 0.19.1",
"rustls",
"sha2 0.9.9",
"smallvec 1.8.0",
"sqlformat",
@@ -5339,8 +5467,8 @@ dependencies = [
"thiserror",
"tokio-stream",
"url",
"webpki 0.21.4",
"webpki-roots 0.21.1",
"webpki",
"webpki-roots",
]
[[package]]
@@ -5439,9 +5567,15 @@ version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cae14b91c7d11c9a851d3fbc80a963198998c2a64eec840477fa92d8ce9b70bb"
dependencies = [
"strum_macros",
"strum_macros 0.23.1",
]
[[package]]
name = "strum"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
[[package]]
name = "strum_macros"
version = "0.23.1"
@@ -5455,6 +5589,19 @@ dependencies = [
"syn",
]
[[package]]
name = "strum_macros"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck 0.4.0",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "subtle"
version = "1.0.0"
@@ -5799,9 +5946,9 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
dependencies = [
"rustls 0.19.1",
"rustls",
"tokio",
"webpki 0.21.4",
"webpki",
]
[[package]]
@@ -5838,19 +5985,7 @@ dependencies = [
"log",
"pin-project",
"tokio",
"tungstenite 0.13.0",
]
[[package]]
name = "tokio-tungstenite"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181"
dependencies = [
"futures-util",
"log",
"tokio",
"tungstenite 0.17.3",
"tungstenite",
]
[[package]]
@@ -6118,28 +6253,6 @@ dependencies = [
"utf-8",
]
[[package]]
name = "tungstenite"
version = "0.17.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0"
dependencies = [
"base64",
"byteorder",
"bytes",
"http",
"httparse",
"log",
"rand 0.8.5",
"rustls 0.20.6",
"sha-1 0.10.0",
"thiserror",
"url",
"utf-8",
"webpki 0.22.0",
"webpki-roots 0.22.4",
]
[[package]]
name = "typenum"
version = "1.15.0"
@@ -6447,23 +6560,25 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.83"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
dependencies = [
"cfg-if 1.0.0",
"serde",
"serde_json",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.83"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
@@ -6484,9 +6599,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -6494,9 +6609,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.83"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
dependencies = [
"proc-macro2",
"quote",
@@ -6507,9 +6622,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.83"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
[[package]]
name = "wasm-utils"
@@ -6517,7 +6632,7 @@ version = "0.1.0"
dependencies = [
"futures",
"js-sys",
"tungstenite 0.17.3",
"tungstenite",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
@@ -6547,12 +6662,15 @@ dependencies = [
"ethereum-types",
"futures",
"futures-timer",
"getrandom 0.2.6",
"headers",
"hex",
"js-sys",
"jsonrpc-core",
"log",
"parking_lot 0.11.2",
"pin-project",
"rand 0.8.5",
"reqwest",
"rlp",
"secp256k1",
@@ -6564,6 +6682,8 @@ dependencies = [
"tokio-stream",
"tokio-util 0.6.9",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web3-async-native-tls",
]
@@ -6589,32 +6709,13 @@ dependencies = [
"untrusted",
]
[[package]]
name = "webpki"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "webpki-roots"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
dependencies = [
"webpki 0.21.4",
]
[[package]]
name = "webpki-roots"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf"
dependencies = [
"webpki 0.22.0",
"webpki",
]
[[package]]
+2
View File
@@ -27,6 +27,7 @@ members = [
"common/client-libs/mixnet-client",
"common/client-libs/validator-client",
"common/coconut-interface",
"common/commands",
"common/config",
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/contracts-common",
@@ -69,6 +70,7 @@ members = [
"service-providers/network-statistics",
"validator-api",
"validator-api/validator-api-requests",
"tools/nym-cli",
"tools/ts-rs-cli"
]
+3
View File
@@ -69,6 +69,9 @@ build-wallet:
build-connect:
cargo build --manifest-path nym-connect/Cargo.toml --workspace
build-nym-cli:
cargo build --release --manifest-path tools/nym-cli/Cargo.toml
fmt-main:
cargo fmt --all
+1 -1
View File
@@ -28,7 +28,7 @@ rand = { version = "0.7.3", features = ["wasm-bindgen"] } # rng-related traits +
serde = { version = "1.0.104", features = ["derive"] } # for config serialization/deserialization
sled = "0.34" # for storage of replySURB decryption keys
tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal"] } # async runtime
tokio-tungstenite = "0.17.2" # websocket
tokio-tungstenite = "0.14" # websocket
## internal
client-core = { path = "../client-core" }
+3 -4
View File
@@ -1,7 +1,7 @@
[package]
name = "nym-client-wasm"
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jedrzej Stuczynski <andrew@nymtech.net>"]
version = "1.0.0"
version = "1.0.1"
edition = "2021"
keywords = ["nym", "sphinx", "wasm", "webassembly", "privacy", "client"]
license = "Apache-2.0"
@@ -20,8 +20,7 @@ coconut = ["coconut-interface", "credentials", "gateway-client/coconut"]
[dependencies]
futures = "0.3"
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.4"
wasm-bindgen = { version = "=0.2.83", features = ["serde-serialize"] }
wasm-bindgen = { version = "=0.2.78", features = ["serde-serialize"] }
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
@@ -33,7 +32,7 @@ credentials = { path = "../../common/credentials", optional = true }
crypto = { path = "../../common/crypto" }
nymsphinx = { path = "../../common/nymsphinx" }
topology = { path = "../../common/topology" }
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm", "coconut"] }
gateway-client = { path = "../../common/client-libs/gateway-client", default-features = false, features = ["wasm"] }
validator-client = { path = "../../common/client-libs/validator-client", default-features = false }
wasm-utils = { path = "../../common/wasm-utils" }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 B

+1 -1
View File
@@ -25,7 +25,7 @@ async function main() {
set_panic_hook();
// validator server we will use to get topology from
const validator = "https://validator.nymtech.net/api"; //"http://localhost:8081";
const validator = "https://sandbox-validator.nymtech.net/api"; //"http://localhost:8081";
client = new NymClient(validator);
+2 -16
View File
@@ -211,7 +211,7 @@ impl NymClient {
self
}
pub(crate) fn choose_gateway(&self) -> gateway::Node {
pub(crate) fn choose_gateway(&self) -> &gateway::Node {
let topology = self
.topology
.as_ref()
@@ -219,21 +219,7 @@ impl NymClient {
// choose the first one available
assert!(!topology.gateways().is_empty());
topology.gateways().first().unwrap();
console_log!("picking nym gateway");
gateway::Node {
owner: "n1kymvkx6vsq7pvn6hfurkpg06h3j4gxj4em7tlg".to_string(),
stake: 100000000,
location: "PRIVACY HQ".to_string(),
host: "gateway1.nymtech.net".parse().unwrap(),
mix_host: "213.219.38.119:1789".parse().unwrap(),
clients_port: 443,
identity_key: identity::PublicKey::from_base58_string("E3mvZTHQCdBvhfr178Swx9g4QG3kkRUun7YnToLMcMbM").unwrap(),
sphinx_key: encryption::PublicKey::from_base58_string("CYcrjoJ8GT7Dp54zViUyyRUfegeRCyPifWQZHRgMZrfX").unwrap(),
version: "1.0.0-rc.2".to_string()
}
topology.gateways().first().unwrap()
}
// Right now it's impossible to have async exported functions to take `&mut self` rather than mut self
@@ -167,7 +167,7 @@ impl ReceivedMessagesProcessor {
mix_msgs = fused_mixnet_messages_receiver.next() => {
for mix_msg in mix_msgs.unwrap() {
if let Some(processed) = self.process_received_fragment(mix_msg) {
let arg1 = serde_wasm_bindgen::to_value(&processed).unwrap();
let arg1 = JsValue::from_serde(&processed).unwrap();
on_message.call1(&this, &arg1).expect("on message failed!");
}
}
+7 -7
View File
@@ -15,8 +15,8 @@ log = "0.4"
thiserror = "1.0"
url = "2.2"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
secp256k1 = { version = "0.20.3", optional = true }
web3 = { version = "0.17.0", default-features = false, optional = true }
secp256k1 = "0.20.3"
web3 = { version = "0.17.0", default-features = false }
async-trait = { version = "0.1.51" }
# internal
@@ -30,8 +30,8 @@ network-defaults = { path = "../../network-defaults" }
validator-client = { path = "../validator-client", optional = true }
[dependencies.tungstenite]
version = "0.17.3"
features = ["rustls-tls-webpki-roots"]
version = "0.13"
default-features = false
# non-wasm-only dependencies
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
@@ -39,7 +39,7 @@ version = "1.19.1"
features = ["macros", "rt", "net", "sync", "time"]
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio-tungstenite]
version = "0.17.2"
version = "0.14"
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.credential-storage]
path = "../../credential-storage"
@@ -73,5 +73,5 @@ features = ["js"]
[features]
coconut = ["gateway-requests/coconut", "coconut-interface", "validator-client", "credentials/coconut"]
wasm = []
default = ["web3/default", "secp256k1"]
wasm = ["web3/wasm", "web3/http", "web3/signing"]
default = ["web3/default"]
@@ -23,7 +23,7 @@ use {
},
};
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
use {
credentials::token::bandwidth::TokenCredential,
crypto::asymmetric::identity,
@@ -45,7 +45,7 @@ use {
},
};
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
Contract::from_json(
web3.eth(),
@@ -58,7 +58,7 @@ pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
.expect("Invalid json abi")
}
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
pub fn eth_erc20_contract(web3: Web3<Http>) -> Contract<Http> {
Contract::from_json(
web3.eth(),
@@ -76,11 +76,11 @@ pub struct BandwidthController<St: Storage> {
storage: St,
#[cfg(feature = "coconut")]
validator_endpoints: Vec<url::Url>,
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
contract: Contract<Http>,
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
erc20_contract: Contract<Http>,
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
eth_private_key: SecretKey,
}
@@ -96,7 +96,7 @@ where
}
}
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
pub fn new(
storage: St,
eth_endpoint: String,
@@ -120,7 +120,7 @@ where
})
}
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
async fn backup_keypair(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
self.storage
.insert_erc20_credential(
@@ -132,7 +132,7 @@ where
Ok(())
}
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
async fn restore_keypair(&self) -> Result<identity::KeyPair, GatewayClientError> {
let data = self.storage.get_next_erc20_credential().await?;
let public_key = identity::PublicKey::from_base58_string(data.public_key).unwrap();
@@ -141,7 +141,7 @@ where
Ok(identity::KeyPair::from_keys(private_key, public_key))
}
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
async fn mark_keypair_as_spent(
&self,
keypair: &identity::KeyPair,
@@ -180,7 +180,7 @@ where
)?)
}
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
pub async fn prepare_token_credential(
&self,
gateway_identity: identity::PublicKey,
@@ -219,7 +219,7 @@ where
))
}
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
pub async fn buy_token_credential(
&self,
verification_key: identity::PublicKey,
@@ -348,7 +348,7 @@ where
}
}
#[cfg(all(not(target_arch = "wasm32"), not(feature = "coconut")))]
#[cfg(not(feature = "coconut"))]
#[cfg(test)]
mod tests {
use network_defaults::ETH_EVENT_NAME;
@@ -30,7 +30,7 @@ use rand::rngs::OsRng;
use std::convert::TryFrom;
use std::sync::Arc;
use std::time::Duration;
use tungstenite::Message;
use tungstenite::protocol::Message;
#[cfg(not(target_arch = "wasm32"))]
use tokio_tungstenite::connect_async;
@@ -449,7 +449,7 @@ impl GatewayClient {
.derive_destination_address();
let encrypted_address = EncryptedAddressBytes::new(&self_address, shared_key, &iv);
let msg: tungstenite::Message =
let msg =
ClientControlRequest::new_authenticate(self_address, encrypted_address, iv).into();
match self.send_websocket_message(msg).await? {
@@ -733,8 +733,6 @@ impl GatewayClient {
}
pub async fn authenticate_and_start(&mut self) -> Result<Arc<SharedKeys>, GatewayClientError> {
println!("About to establish connection to {}", self.gateway_address);
if !self.connection.is_established() {
self.establish_connection().await?;
}
@@ -489,7 +489,7 @@ impl TryFrom<ProtoContractCodeHistoryEntry> for ContractCodeHistoryEntry {
}
}
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize)]
pub struct GasInfo {
/// GasWanted is the maximum units of work we allow this tx to perform.
pub gas_wanted: Gas,
@@ -645,7 +645,7 @@ impl InstantiateOptions {
}
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct InstantiateResult {
/// The address of the newly instantiated contract
pub contract_address: AccountId,
@@ -658,7 +658,7 @@ pub struct InstantiateResult {
pub gas_info: GasInfo,
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct ChangeAdminResult {
pub logs: Vec<Log>,
@@ -668,7 +668,7 @@ pub struct ChangeAdminResult {
pub gas_info: GasInfo,
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct MigrateResult {
pub logs: Vec<Log>,
@@ -678,7 +678,7 @@ pub struct MigrateResult {
pub gas_info: GasInfo,
}
#[derive(Debug)]
#[derive(Debug, Serialize)]
pub struct ExecuteResult {
pub logs: Vec<Log>,
@@ -10,6 +10,8 @@ use crate::nymd::error::NymdError;
use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER;
use crate::nymd::wallet::DirectSecp256k1HdWallet;
use cosmrs::cosmwasm;
use cosmrs::rpc::endpoint::block::Response as BlockResponse;
use cosmrs::rpc::query::Query;
use cosmrs::rpc::Error as TendermintRpcError;
use cosmrs::rpc::HttpClientUrl;
use cosmrs::tx::Msg;
@@ -212,6 +214,10 @@ impl<C> NymdClient<C> {
&self.config
}
pub fn current_chain_details(&self) -> &ChainDetails {
&self.config.chain_details
}
pub fn set_mixnet_contract_address(&mut self, address: AccountId) {
self.config.mixnet_contract_address = Some(address);
}
@@ -355,11 +361,26 @@ impl<C> NymdClient<C> {
address: &AccountId,
) -> Result<Option<Account>, NymdError>
where
C: SigningCosmWasmClient + Sync,
C: CosmWasmClient + Sync,
{
self.client.get_account(address).await
}
pub async fn get_account_public_key(
&self,
address: &AccountId,
) -> Result<Option<cosmrs::crypto::PublicKey>, NymdError>
where
C: CosmWasmClient + Sync,
{
if let Some(account) = self.client.get_account(address).await? {
let base_account = account.try_get_base_account()?;
return Ok(base_account.pubkey);
}
Ok(None)
}
pub async fn get_current_block_timestamp(&self) -> Result<TendermintTime, NymdError>
where
C: CosmWasmClient + Sync,
@@ -377,6 +398,13 @@ impl<C> NymdClient<C> {
Ok(self.client.get_block(height).await?.block.header.time)
}
pub async fn get_block(&self, height: Option<u32>) -> Result<BlockResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.get_block(height).await
}
pub async fn get_current_block_height(&self) -> Result<Height, NymdError>
where
C: CosmWasmClient + Sync,
@@ -421,6 +449,13 @@ impl<C> NymdClient<C> {
self.client.get_balance(address, denom).await
}
pub async fn get_all_balances(&self, address: &AccountId) -> Result<Vec<Coin>, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.get_all_balances(address).await
}
pub async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NymdError>
where
C: CosmWasmClient + Sync,
@@ -428,6 +463,13 @@ impl<C> NymdClient<C> {
self.client.get_tx(id).await
}
pub async fn search_tx(&self, query: Query) -> Result<Vec<TxResponse>, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.search_tx(query).await
}
pub async fn get_total_supply(&self) -> Result<Vec<Coin>, NymdError>
where
C: CosmWasmClient + Sync,
@@ -4,7 +4,7 @@
use crate::nymd::error::NymdError;
use config::defaults;
use cosmrs::bip32::{DerivationPath, XPrv};
use cosmrs::crypto::secp256k1::SigningKey;
use cosmrs::crypto::secp256k1::{Signature, SigningKey};
use cosmrs::crypto::PublicKey;
use cosmrs::tx::SignDoc;
use cosmrs::{tx, AccountId};
@@ -105,6 +105,17 @@ impl DirectSecp256k1HdWallet {
self.secret.to_string()
}
pub fn sign_raw_with_account(
&self,
signer: &AccountData,
message: &[u8],
) -> Result<Signature, NymdError> {
signer
.private_key
.sign(message)
.map_err(|_| NymdError::SigningFailure)
}
pub fn sign_direct_with_account(
&self,
signer: &AccountData,
+32
View File
@@ -0,0 +1,32 @@
[package]
name = "nym-cli-commands"
version = "1.0.0"
authors = ["Nym Technologies SA"]
edition = "2021"
[dependencies]
base64 = "0.13.0"
bip39 = "1.0.1"
bs58 = "0.4"
comfy-table = "6.0.0"
cfg-if = "1.0.0"
clap = { version = "3.2", features = ["derive"] }
handlebars = "3.0.1"
humantime-serde = "1.0"
k256 = { version = "0.10", features = ["ecdsa", "sha256"] }
log = "0.4"
rand = {version = "0.6", features = ["std"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
thiserror = "1"
time = { version = "0.3.6", features = ["parsing", "formatting"] }
toml = "0.5.6"
url = "2.2"
cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegrant_support" }
cosmwasm-std = { version = "1.0.0" }
validator-client = { path = "../client-libs/validator-client", features = ["nymd-client"] }
network-defaults = { path = "../network-defaults" }
mixnet-contract-common = { path = "../cosmwasm-smart-contracts/mixnet-contract" }
vesting-contract-common = { path = "../cosmwasm-smart-contracts/vesting-contract" }
+13
View File
@@ -0,0 +1,13 @@
# Common `clap` Command Crate
This crate contains `clap` commands for common operations:
- account creation and queries
- block queries
- cosmwasm uploads, instantiate, execution, query, etc
- mixnet actions and queries
- sign and verify messages
- query for transactions
- create vesting schedules and query for them
For how to use this crate, please see the [Nym CLI](../../tools/nym-cli).
+4
View File
@@ -0,0 +1,4 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// TODO: add coconut commands here
+18
View File
@@ -0,0 +1,18 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ContextError {
#[error("mnemonic was not provided, pass as an argument or an env var called MNEMONIC")]
MnemonicNotProvided,
#[error("failed to parse mnemonic - {0}")]
Bip39Error(#[from] bip39::Error),
// there are lots of error that can occur in the nymd client, so just pass through their display details
// TODO: improve this to return known errors
#[error("failed to create client - {0}")]
NymdError(String),
}
+138
View File
@@ -0,0 +1,138 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use network_defaults::{
setup_env,
var_names::{API_VALIDATOR, MIXNET_CONTRACT_ADDRESS, NYMD_VALIDATOR, VESTING_CONTRACT_ADDRESS},
NymNetworkDetails,
};
use validator_client::nymd::{self, AccountId, NymdClient, QueryNymdClient, SigningNymdClient};
pub use validator_client::validator_api::Client as ValidatorApiClient;
use crate::context::errors::ContextError;
pub mod errors;
pub type SigningClient = validator_client::nymd::NymdClient<SigningNymdClient>;
pub type QueryClient = validator_client::nymd::NymdClient<QueryNymdClient>;
pub type SigningClientWithValidatorAPI = validator_client::Client<SigningNymdClient>;
pub type QueryClientWithValidatorAPI = validator_client::Client<QueryNymdClient>;
#[derive(Debug)]
pub struct ClientArgs {
pub config_env_file: Option<std::path::PathBuf>,
pub nymd_url: Option<String>,
pub validator_api_url: Option<String>,
pub mnemonic: Option<bip39::Mnemonic>,
pub mixnet_contract_address: Option<AccountId>,
pub vesting_contract_address: Option<AccountId>,
}
pub fn get_network_details(args: &ClientArgs) -> Result<NymNetworkDetails, ContextError> {
// let the network defaults crate handle setting up the env vars if the file arg is set, otherwise
// it will default to what is already in env vars, falling back to mainnet
setup_env(args.config_env_file.clone());
// override the env vars with user supplied arguments, if set
if let Some(nymd_url) = args.nymd_url.as_ref() {
std::env::set_var(NYMD_VALIDATOR, nymd_url);
}
if let Some(validator_api_url) = args.validator_api_url.as_ref() {
std::env::set_var(API_VALIDATOR, validator_api_url);
}
if let Some(mixnet_contract_address) = args.mixnet_contract_address.as_ref() {
std::env::set_var(MIXNET_CONTRACT_ADDRESS, mixnet_contract_address.to_string());
}
if let Some(vesting_contract_address) = args.vesting_contract_address.as_ref() {
std::env::set_var(
VESTING_CONTRACT_ADDRESS,
vesting_contract_address.to_string(),
);
}
Ok(NymNetworkDetails::new_from_env())
}
pub fn create_signing_client(
args: ClientArgs,
network_details: &NymNetworkDetails,
) -> Result<SigningClient, ContextError> {
let client_config = nymd::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
// get mnemonic
let mnemonic = match std::env::var("MNEMONIC") {
Ok(value) => bip39::Mnemonic::parse(value)?,
// env var MNEMONIC is not present, so try to fall back to arg --mnemonic ...
Err(_) => match args.mnemonic {
Some(value) => value,
None => return Err(ContextError::MnemonicNotProvided), // no env var or arg provided
},
};
let nymd_url = network_details
.endpoints
.first()
.expect("network details are not defined")
.nymd_url
.as_str();
match NymdClient::connect_with_mnemonic(client_config, nymd_url, mnemonic, None) {
Ok(client) => Ok(client),
Err(e) => Err(ContextError::NymdError(format!("{:?}", e))),
}
}
pub fn create_query_client(
network_details: &NymNetworkDetails,
) -> Result<QueryClient, ContextError> {
let client_config = nymd::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
let nymd_url = network_details
.endpoints
.first()
.expect("network details are not defined")
.nymd_url
.as_str();
match NymdClient::connect(client_config, nymd_url) {
Ok(client) => Ok(client),
Err(e) => Err(ContextError::NymdError(format!("{:?}", e))),
}
}
pub fn create_signing_client_with_validator_api(
args: ClientArgs,
network_details: &NymNetworkDetails,
) -> Result<SigningClientWithValidatorAPI, ContextError> {
let client_config = validator_client::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
// get mnemonic
let mnemonic = match std::env::var("MNEMONIC") {
Ok(value) => bip39::Mnemonic::parse(value)?,
// env var MNEMONIC is not present, so try to fall back to arg --mnemonic ...
Err(_) => match args.mnemonic {
Some(value) => value,
None => return Err(ContextError::MnemonicNotProvided), // no env var or arg provided
},
};
match validator_client::client::Client::new_signing(client_config, mnemonic) {
Ok(client) => Ok(client),
Err(e) => Err(ContextError::NymdError(format!("{:?}", e))),
}
}
pub fn create_query_client_with_validator_api(
network_details: &NymNetworkDetails,
) -> Result<QueryClientWithValidatorAPI, ContextError> {
let client_config = validator_client::Config::try_from_nym_network_details(network_details)
.expect("failed to construct valid validator client config with the provided network");
match validator_client::client::Client::new_query(client_config) {
Ok(client) => Ok(client),
Err(e) => Err(ContextError::NymdError(format!("{:?}", e))),
}
}
+7
View File
@@ -0,0 +1,7 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod coconut;
pub mod context;
pub mod utils;
pub mod validator;
+44
View File
@@ -0,0 +1,44 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::error::Error;
use std::fmt::Display;
use cosmwasm_std::{Coin as CosmWasmCoin, Decimal};
use log::error;
use validator_client::nymd::Coin;
pub fn pretty_coin(coin: &Coin) -> String {
let amount = Decimal::from_ratio(coin.amount, 1_000_000u128);
let denom = if coin.denom.starts_with('u') {
&coin.denom[1..]
} else {
&coin.denom
};
format!("{} {}", amount, denom)
}
pub fn pretty_cosmwasm_coin(coin: &CosmWasmCoin) -> String {
let amount = Decimal::from_ratio(coin.amount, 1_000_000u128);
let denom = if coin.denom.starts_with('u') {
&coin.denom[1..]
} else {
&coin.denom
};
format!("{} {}", amount, denom)
}
pub fn show_error<E>(e: E)
where
E: Display,
{
error!("{}", e);
}
pub fn show_error_passthrough<E>(e: E) -> E
where
E: Error + Display,
{
error!("{}", e);
e
}
@@ -0,0 +1,72 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::{error, info};
use validator_client::nymd::AccountId;
use crate::context::QueryClient;
use crate::utils::{pretty_coin, show_error};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "The account address to get the balance for")]
pub address: Option<AccountId>,
#[clap(long)]
#[clap(help = "Optional currency to show balance for")]
pub denom: Option<String>,
#[clap(long, requires = "denom")]
#[clap(help = "Optionally hide the denom")]
pub hide_denom: bool,
#[clap(long)]
#[clap(help = "Show as a raw value")]
pub raw: bool,
}
pub async fn query_balance(
args: Args,
client: &QueryClient,
address_from_mnemonic: Option<AccountId>,
) {
if args.address.is_none() && address_from_mnemonic.is_none() {
error!("Please specify an account address or a mnemonic to get the balance for");
return;
}
let address = args
.address
.unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic"));
info!("Getting balance for {}...", address);
match client.get_all_balances(&address).await {
Ok(coins) => {
if coins.is_empty() {
println!("No balance");
return;
}
let denom = args.denom.unwrap_or_else(|| "".to_string());
for coin in coins {
if denom.is_empty() || denom.eq_ignore_ascii_case(&coin.denom) {
if args.raw {
if !args.hide_denom {
println!("{}", coin);
} else {
println!("{}", coin.amount);
}
} else {
println!("{}", pretty_coin(&coin));
}
}
}
}
Err(e) => show_error(e),
}
}
@@ -0,0 +1,24 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use validator_client::nymd::wallet::DirectSecp256k1HdWallet;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
// allowed values are 12, 18 or 24
pub word_count: Option<usize>,
}
pub fn create_account(args: Args, prefix: &str) {
let word_count = args.word_count.unwrap_or(24);
let mnemonic = bip39::Mnemonic::generate(word_count).expect("failed to generate mnemonic!");
let wallet =
DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic).expect("failed to build wallet!");
// Output address and mnemonics into separate lines for easier parsing
println!("{}", wallet.mnemonic());
println!("{}", wallet.try_derive_accounts().unwrap()[0].address());
}
@@ -0,0 +1,28 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod balance;
pub mod create;
pub mod pubkey;
pub mod send;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Account {
#[clap(subcommand)]
pub command: Option<AccountCommands>,
}
#[derive(Debug, Subcommand)]
pub enum AccountCommands {
/// Create a new mnemonic - note, this account does not appear on the chain until the account id is used in a transaction
Create(crate::validator::account::create::Args),
/// Gets the balance of an account
Balance(crate::validator::account::balance::Args),
/// Gets the public key of an account
PubKey(crate::validator::account::pubkey::Args),
/// Sends tokens to another account
Send(crate::validator::account::send::Args),
}
@@ -0,0 +1,89 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::{error, info};
use validator_client::nymd::wallet::DirectSecp256k1HdWallet;
use validator_client::nymd::AccountId;
use crate::context::QueryClient;
use crate::utils::show_error;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(
help = "Optionally, show the public key for this account address, otherwise generate the account address from the mnemonic"
)]
pub address: Option<AccountId>,
#[clap(long)]
#[clap(help = "If set, get the public key from the mnemonic, rather than querying for it")]
pub from_mnemonic: bool,
}
pub async fn get_pubkey(
args: Args,
client: &QueryClient,
mnemonic: Option<bip39::Mnemonic>,
address_from_mnemonic: Option<AccountId>,
) {
if args.address.is_none() && address_from_mnemonic.is_none() {
error!("Please specify an account address or a mnemonic to get the balance for");
return;
}
let address = args
.address
.unwrap_or_else(|| address_from_mnemonic.expect("please provide a mnemonic"));
if args.from_mnemonic {
let prefix = client
.current_chain_details()
.bech32_account_prefix
.as_str();
get_pubkey_from_mnemonic(address, prefix, mnemonic.expect("mnemonic not set"));
return;
}
get_pubkey_from_chain(address, client).await;
}
pub fn get_pubkey_from_mnemonic(address: AccountId, prefix: &str, mnemonic: bip39::Mnemonic) {
match DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic) {
Ok(wallet) => match wallet.try_derive_accounts() {
Ok(accounts) => match accounts.iter().find(|a| *a.address() == address) {
Some(account) => {
println!("{}", account.public_key().to_string());
}
None => {
error!("Could not derive key that matches {}", address)
}
},
Err(e) => {
error!("Failed to derive accounts. {}", e);
}
},
Err(e) => show_error(e),
}
}
pub async fn get_pubkey_from_chain(address: AccountId, client: &QueryClient) {
info!("Getting public key for address {} from chain...", address);
match client.get_account_details(&address).await {
Ok(Some(account)) => {
if let Ok(base_account) = account.try_get_base_account() {
if let Some(pubkey) = base_account.pubkey {
println!("{}", pubkey.to_string());
} else {
println!("No account associated with address {}", address);
}
}
}
Ok(None) => {
println!("No account associated with address {}", address);
}
Err(e) => show_error(e),
}
}
@@ -0,0 +1,66 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::info;
use serde_json::json;
use validator_client::nymd::{AccountId, Coin};
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser, help = "The recipient account address")]
pub recipient: AccountId,
#[clap(
value_parser,
help = "Amount to transfer in micro denomination (e.g. unym or unyx)"
)]
pub amount: u128,
#[clap(long, help = "Override the denomination")]
pub denom: Option<String>,
#[clap(long)]
pub memo: Option<String>,
}
pub async fn send(args: Args, client: &SigningClient) {
let memo = args
.memo
.unwrap_or_else(|| "Sending tokens with nym-cli".to_owned());
let denom = args
.denom
.unwrap_or_else(|| client.current_chain_details().mix_denom.base.clone());
let coin = Coin {
denom,
amount: args.amount,
};
info!(
"Sending {} {} from {} to {}...",
coin.amount,
coin.denom,
client.address(),
args.recipient
);
let res = client
.send(&args.recipient, vec![coin], memo, None)
.await
.expect("failed to send tokens!");
info!("Sending result: {}", json!(res));
println!();
println!(
"Nodesguru: https://nym.explorers.guru/transaction/{}",
&res.hash
);
println!("Mintscan: https://www.mintscan.io/nyx/txs/{}", &res.hash);
println!("Transaction result code: {}", &res.tx_result.code.value());
println!("Transaction hash: {}", &res.hash);
}
@@ -0,0 +1,23 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use crate::context::QueryClient;
use crate::utils::show_error;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "The block height")]
pub height: u32,
}
pub async fn query_for_block_time(args: Args, client: &QueryClient) {
match client.get_block_timestamp(Some(args.height)).await {
Ok(res) => {
println!("{}", res.to_rfc3339())
}
Err(e) => show_error(e),
}
}
@@ -0,0 +1,19 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use crate::context::QueryClient;
use crate::utils::show_error;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn query_current_block_height(client: &QueryClient) {
match client.get_current_block_height().await {
Ok(res) => {
println!("Current block height:\n{}", res.value())
}
Err(e) => show_error(e),
}
}
@@ -0,0 +1,24 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use crate::context::QueryClient;
use crate::utils::show_error;
use serde_json::json;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "The block height")]
pub height: u32,
}
pub async fn query_for_block(args: Args, client: &QueryClient) {
match client.get_block(Some(args.height)).await {
Ok(res) => {
println!("{}", json!(res))
}
Err(e) => show_error(e),
}
}
@@ -0,0 +1,25 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod block_time;
pub mod current_height;
pub mod get;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Block {
#[clap(subcommand)]
pub command: Option<BlockCommands>,
}
#[derive(Debug, Subcommand)]
pub enum BlockCommands {
/// Gets a block's details and prints as JSON
Get(crate::validator::block::get::Args),
/// Gets the block time at a height
Time(crate::validator::block::block_time::Args),
/// Gets the current block height
CurrentHeight(crate::validator::block::current_height::Args),
}
@@ -0,0 +1,60 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use cosmrs::AccountId;
use log::{error, info};
use serde_json::{json, Value};
use validator_client::nymd::Coin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "The address of contract to execute")]
pub contract_address: AccountId,
#[clap(value_parser)]
#[clap(help = "JSON encoded method arguments")]
pub json_args: String,
#[clap(long)]
pub memo: Option<String>,
#[clap(
value_parser,
requires = "fundsDenom",
help = "Amount to supply as funds in micro denomination (e.g. unym or unyx)"
)]
pub funds: Option<u128>,
#[clap(long, requires = "funds", help = "Set the denomination for the funds")]
pub funds_denom: Option<String>,
}
pub async fn execute(args: Args, client: SigningClient) {
info!("Starting contract method execution!");
let json_args: Value =
serde_json::from_str(&args.json_args).expect("Unable to parse JSON args");
let memo = args
.memo
.unwrap_or_else(|| "nym-cli execute contract method".to_owned());
let funds = match args.funds {
Some(funds) => vec![Coin::new(
funds,
args.funds_denom.expect("denom for funds not set"),
)],
None => vec![],
};
match client
.execute(&args.contract_address, &json_args, None, memo, funds)
.await
{
Ok(res) => info!("SUCCESS ✅\n{}", json!(res)),
Err(e) => error!("FAILURE ❌\n{}", e),
}
}
@@ -0,0 +1,78 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use cosmrs::{AccountId, Coin as CosmosCoin};
use log::info;
use network_defaults::NymNetworkDetails;
use validator_client::nymd::cosmwasm_client::types::{ContractCodeId, InstantiateOptions};
use validator_client::nymd::Coin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
pub code_id: ContractCodeId,
#[clap(long)]
pub memo: Option<String>,
#[clap(long)]
pub label: Option<String>,
#[clap(long)]
pub init_message: String,
#[clap(long)]
pub admin: Option<AccountId>,
#[clap(
long,
requires = "fundsDenom",
help = "Amount to supply as funds in micro denomination (e.g. unym or unyx)"
)]
pub funds: Option<u128>,
#[clap(long, requires = "funds", help = "Set the denomination for the funds")]
pub funds_denom: Option<String>,
}
pub async fn init(args: Args, client: SigningClient, network_details: &NymNetworkDetails) {
info!("Starting contract instantiation!");
let memo = args
.memo
.unwrap_or_else(|| "contract instantiation".to_owned());
let label = args
.label
.unwrap_or_else(|| "Nym mixnet smart contract".to_owned());
let funds: Vec<CosmosCoin> = match args.funds {
Some(funds) => vec![Coin::new(
funds,
args.funds_denom
.unwrap_or_else(|| network_details.chain_details.mix_denom.base.to_string()),
)
.into()],
None => vec![],
};
// by default we make ourselves an admin, let me know if you don't like that behaviour
let opts = Some(InstantiateOptions {
funds,
admin: Some(args.admin.unwrap_or_else(|| client.address().clone())),
});
let msg: serde_json::Value =
serde_json::from_str(&args.init_message).expect("failed to parse init message");
// the EmptyMsg{} argument is equivalent to `--init-message='{}'`
let res = client
.instantiate(args.code_id, &msg, label, memo, opts, None)
.await
.expect("failed to instantiate the contract!");
info!("Init result: {:?}", res);
println!("{}", res.contract_address)
}
@@ -0,0 +1,51 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use crate::utils::show_error_passthrough;
use clap::Parser;
use cosmrs::AccountId;
use log::info;
use validator_client::nymd::cosmwasm_client::types::{ContractCodeId, EmptyMsg};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
pub contract_address: AccountId,
#[clap(long)]
pub code_id: ContractCodeId,
#[clap(long)]
pub memo: Option<String>,
#[clap(long)]
pub init_message: Option<String>,
}
pub async fn migrate(args: Args, client: SigningClient) {
println!("Starting contract migration!");
let memo = args.memo.unwrap_or_else(|| "contract migration".to_owned());
let contract_address = args.contract_address;
// the EmptyMsg{} argument is equivalent to `--init-message='{}'`
let res = if let Some(raw_msg) = args.init_message {
let msg: serde_json::Value =
serde_json::from_str(&raw_msg).expect("failed to parse init message");
client
.migrate(&contract_address, args.code_id, &msg, memo, None)
.await
.map_err(show_error_passthrough)
.expect("failed to migrate the contract!")
} else {
client
.migrate(&contract_address, args.code_id, &EmptyMsg {}, memo, None)
.await
.map_err(show_error_passthrough)
.expect("failed to migrate the contract!")
};
info!("Migrate result: {:?}", res);
}
@@ -0,0 +1,28 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod execute_contract;
pub mod init_contract;
pub mod migrate_contract;
pub mod upload_contract;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Cosmwasm {
#[clap(subcommand)]
pub command: Option<CosmwasmCommands>,
}
#[derive(Debug, Subcommand)]
pub enum CosmwasmCommands {
/// Upload a smart contract WASM blob
Upload(crate::validator::cosmwasm::upload_contract::Args),
/// Init a WASM smart contract
Init(crate::validator::cosmwasm::init_contract::Args),
/// Migrate a WASM smart contract
Migrate(crate::validator::cosmwasm::migrate_contract::Args),
/// Execute a WASM smart contract method
Execute(crate::validator::cosmwasm::execute_contract::Args),
}
@@ -0,0 +1,37 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use std::io::Read;
use std::path::PathBuf;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub wasm_path: PathBuf,
#[clap(long)]
pub memo: Option<String>,
}
pub async fn upload(args: Args, client: SigningClient) {
info!("Starting contract upload!");
let mut file = std::fs::File::open(args.wasm_path).expect("failed to open the wasm blob");
let mut data = Vec::new();
file.read_to_end(&mut data).unwrap();
let memo = args.memo.unwrap_or_else(|| "contract upload".to_owned());
let res = client
.upload(data, memo, None)
.await
.expect("failed to upload the contract!");
info!("Upload result: {:?}", res);
println!("{}", res.code_id)
}
@@ -0,0 +1,31 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use mixnet_contract_common::Coin;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub amount: u128,
}
pub async fn delegate_to_mixnode(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
info!("Starting delegation to mixnode");
let coin = Coin::new(args.amount, denom);
let res = client
.delegate_to_mixnode(&*args.identity_key, coin.into(), None)
.await
.expect("failed to delegate to mixnode!");
info!("delegating to mixnode: {:?}", res);
}
@@ -0,0 +1,35 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod rewards;
pub mod delegate_to_mixnode;
pub mod query_for_delegations;
pub mod undelegate_from_mixnode;
pub mod vesting_delegate_to_mixnode;
pub mod vesting_undelegate_from_mixnode;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetDelegators {
#[clap(subcommand)]
pub command: MixnetDelegatorsCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetDelegatorsCommands {
/// Lists current delegations
List(query_for_delegations::Args),
/// Manage rewards from delegations
Rewards(rewards::MixnetDelegatorsReward),
/// Delegate to a mixnode
Delegate(delegate_to_mixnode::Args),
/// Undelegate from a mixnode
Undelegate(undelegate_from_mixnode::Args),
/// Delegate to a mixnode with locked tokens
DelegateVesting(vesting_delegate_to_mixnode::Args),
/// Undelegate from a mixnode (when originally using locked tokens)
UndelegateVesting(vesting_undelegate_from_mixnode::Args),
}
@@ -0,0 +1,129 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::info;
use crate::context::SigningClientWithValidatorAPI;
use crate::utils::{pretty_cosmwasm_coin, show_error_passthrough};
use comfy_table::Table;
use mixnet_contract_common::mixnode::DelegationEvent;
use mixnet_contract_common::Delegation;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn execute(_args: Args, client: SigningClientWithValidatorAPI) {
info!(
"Getting delegations for account {}...",
client.nymd.address()
);
let delegations = client
.get_all_delegator_delegations(client.nymd.address())
.await
.map_err(show_error_passthrough);
let mixnet_contract_events = client
.nymd
.get_pending_delegation_events(client.nymd.address().to_string(), None)
.await
.map_err(show_error_passthrough);
let vesting_contract = client.nymd.vesting_contract_address();
let vesting_contract_events = client
.nymd
.get_pending_delegation_events(
client.nymd.address().to_string(),
Some(vesting_contract.to_string()),
)
.await
.map_err(show_error_passthrough);
if let Ok(res) = delegations {
println!();
if res.is_empty() {
println!("This account has not delegated any tokens to mixnodes");
} else {
println!("Delegations:");
print_delegations(res, &client).await;
}
}
if let Ok(res) = mixnet_contract_events {
if !res.is_empty() {
println!();
println!("Pending delegations (liquid tokens):");
print_delegation_events(res, &client).await;
}
}
if let Ok(res) = vesting_contract_events {
if !res.is_empty() {
println!();
println!("Pending delegations (locked tokens):");
print_delegation_events(res, &client).await;
}
}
}
async fn to_iso_timestamp(block_height: u32, client: &SigningClientWithValidatorAPI) -> String {
match client.nymd.get_block_timestamp(Some(block_height)).await {
Ok(res) => res.to_rfc3339(),
Err(_e) => "-".to_string(),
}
}
async fn print_delegations(delegations: Vec<Delegation>, client: &SigningClientWithValidatorAPI) {
let mut table = Table::new();
table.set_header(vec!["Timestamp", "Identity Key", "Delegation", "Proxy"]);
for delegation in delegations {
table.add_row(vec![
to_iso_timestamp(delegation.block_height as u32, client).await,
delegation.node_identity.to_string(),
pretty_cosmwasm_coin(&delegation.amount),
format!("{:?}", delegation.proxy),
]);
}
println!("{table}");
}
async fn print_delegation_events(
events: Vec<DelegationEvent>,
client: &SigningClientWithValidatorAPI,
) {
let mut table = Table::new();
table.set_header(vec![
"Timestamp",
"Identity Key",
"Delegation",
"Event Type",
]);
for event in events {
match event {
DelegationEvent::Delegate(delegation) => {
table.add_row(vec![
to_iso_timestamp(delegation.block_height as u32, client).await,
delegation.node_identity.to_string(),
pretty_cosmwasm_coin(&delegation.amount),
"Delegate".to_string(),
]);
}
DelegationEvent::Undelegate(undelegate) => {
table.add_row(vec![
to_iso_timestamp(undelegate.block_height() as u32, client).await,
undelegate.mix_identity().to_string(),
"-".to_string(),
"Undelegate".to_string(),
]);
}
}
}
println!("{table}");
}
@@ -0,0 +1,23 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity_key: String,
}
pub async fn claim_delegator_reward(args: Args, client: SigningClient) {
info!("Claim delegator reward");
let res = client
.execute_claim_delegator_reward(args.identity_key, None)
.await
.expect("failed to claim delegator-reward");
info!("Claiming delegator reward: {:?}", res)
}
@@ -0,0 +1,22 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod claim_delegator_reward;
pub mod vesting_claim_delegator_reward;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetDelegatorsReward {
#[clap(subcommand)]
pub command: MixnetDelegatorsRewardCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetDelegatorsRewardCommands {
/// Claim rewards accumulated during the delegation of unlocked tokens
Claim(claim_delegator_reward::Args),
/// Claim rewards accumulated during the delegation of locked tokens
VestingClaim(vesting_claim_delegator_reward::Args),
}
@@ -0,0 +1,23 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity: String,
}
pub async fn vesting_claim_delegator_reward(args: Args, client: SigningClient) {
info!("Claim vesting delegator reward");
let res = client
.execute_vesting_claim_delegator_reward(args.identity, None)
.await
.expect("failed to claim vesting delegator-reward");
info!("Claiming vesting delegator reward: {:?}", res)
}
@@ -0,0 +1,23 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity_key: String,
}
pub async fn undelegate_from_mixnode(args: Args, client: SigningClient) {
info!("removing stake from mix-node");
let res = client
.remove_mixnode_delegation(&*args.identity_key, None)
.await
.expect("failed to remove stake from mixnode!");
info!("removing stake from mixnode: {:?}", res)
}
@@ -0,0 +1,34 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::info;
use mixnet_contract_common::Coin;
use validator_client::nymd::VestingSigningClient;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub amount: u128,
}
pub async fn vesting_delegate_to_mixnode(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
info!("Starting vesting delegation to mixnode");
let coin = Coin::new(args.amount, denom);
let res = client
.vesting_delegate_to_mixnode(&*args.identity_key, coin.into(), None)
.await
.expect("failed to delegate to mixnode!");
info!("vesting delegating to mixnode: {:?}", res);
}
@@ -0,0 +1,26 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::info;
use validator_client::nymd::VestingSigningClient;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub identity_key: String,
}
pub async fn vesting_undelegate_from_mixnode(args: Args, client: SigningClient) {
info!("removing stake from vesting mix-node");
let res = client
.vesting_undelegate_from_mixnode(&*args.identity_key, None)
.await
.expect("failed to remove stake from vesting account on mixnode!");
info!("removing stake from vesting mixnode: {:?}", res)
}
@@ -0,0 +1,25 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod delegators;
pub mod operators;
pub mod query;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Mixnet {
#[clap(subcommand)]
pub command: MixnetCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetCommands {
/// Query the mixnet directory
Query(query::MixnetQuery),
/// Manage your delegations
Delegators(delegators::MixnetDelegators),
/// Manage a mixnode or gateway you operate
Operators(operators::MixnetOperators),
}
@@ -0,0 +1,77 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::{info, warn};
use mixnet_contract_common::Coin;
use network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
#[clap(short, long)]
pub force: bool,
}
pub async fn bond_gateway(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
info!("Starting gateway bonding!");
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let gateway = mixnet_contract_common::Gateway {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
location: args
.location
.unwrap_or_else(|| "secret gateway location".to_owned()),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
};
let coin = Coin::new(args.amount, denom);
let res = client
.bond_gateway(gateway, args.signature, coin.into(), None)
.await
.expect("failed to bond gateway!");
info!("Bonding result: {:?}", res)
}
@@ -0,0 +1,28 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod bond_gateway;
pub mod unbond_gateway;
pub mod vesting_bond_gateway;
pub mod vesting_unbond_gateway;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsGateway {
#[clap(subcommand)]
pub command: MixnetOperatorsGatewayCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsGatewayCommands {
/// Bond to a gateway
Bond(bond_gateway::Args),
/// Unbound from a gateway
Unbound(unbond_gateway::Args),
/// Bond to a gateway with locked tokens
VestingBond(vesting_bond_gateway::Args),
/// Unbound from a gateway (when originally using locked tokens)
VestingUnbound(vesting_unbond_gateway::Args),
}
@@ -0,0 +1,20 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn unbond_gateway(client: SigningClient) {
info!("Starting gateway unbonding!");
let res = client
.unbond_gateway(None)
.await
.expect("failed to unbond gateway!");
info!("Unbonding result: {:?}", res)
}
@@ -0,0 +1,76 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::{info, warn};
use mixnet_contract_common::{Coin, Gateway};
use network_defaults::{DEFAULT_CLIENT_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT};
use validator_client::nymd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub clients_port: Option<u16>,
#[clap(long)]
pub location: Option<String>,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
#[clap(short, long)]
pub force: bool,
}
pub async fn vesting_bond_gateway(client: SigningClient, args: Args, denom: &str) {
info!("Starting vesting gateway bonding!");
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let gateway = Gateway {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
clients_port: args.clients_port.unwrap_or(DEFAULT_CLIENT_LISTENING_PORT),
location: args
.location
.unwrap_or_else(|| "secret gateway location".to_owned()),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
};
let coin = Coin::new(args.amount, denom);
let res = client
.vesting_bond_gateway(gateway, &*args.signature, coin.into(), None)
.await
.expect("failed to bond gateway!");
info!("Vesting bonding gateway result: {:?}", res)
}
@@ -0,0 +1,20 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn vesting_unbond_gateway(client: SigningClient) {
info!("Starting vesting gateway unbonding!");
let res = client
.unbond_gateway(None)
.await
.expect("failed to unbond vesting gateway!");
info!("Unbonding vesting result: {:?}", res)
}
@@ -0,0 +1,85 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::{info, warn};
use mixnet_contract_common::Coin;
use network_defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
};
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub verloc_port: Option<u16>,
#[clap(long)]
pub http_api_port: Option<u16>,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(long)]
pub profit_margin_percent: Option<u8>,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
#[clap(short, long)]
pub force: bool,
}
pub async fn bond_mixnode(args: Args, client: SigningClient) {
let denom = client.current_chain_details().mix_denom.base.as_str();
info!("Starting mixnode bonding!");
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let mixnode = mixnet_contract_common::MixNode {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
verloc_port: args.verloc_port.unwrap_or(DEFAULT_VERLOC_LISTENING_PORT),
http_api_port: args
.http_api_port
.unwrap_or(DEFAULT_HTTP_API_LISTENING_PORT),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
profit_margin_percent: args.profit_margin_percent.unwrap_or(10),
};
let coin = Coin::new(args.amount, denom);
let res = client
.bond_mixnode(mixnode, args.signature, coin.into(), None)
.await
.expect("failed to bond mixnode!");
info!("Bonding result: {:?}", res)
}
@@ -0,0 +1,17 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(short, long)]
pub key: String,
}
pub fn decode_mixnode_key(args: Args) {
let b64_decoded = base64::decode(args.key).expect("failed to decode base64 string");
let b58_encoded = bs58::encode(&b64_decoded).into_string();
println!("{}", b58_encoded)
}
@@ -0,0 +1,19 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod decode_mixnode_key;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsMixnodeKeys {
#[clap(subcommand)]
pub command: MixnetOperatorsMixnodeKeysCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsMixnodeKeysCommands {
/// Decode a mixnode key
DecodeMixnodeKey(decode_mixnode_key::Args),
}
@@ -0,0 +1,37 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod bond_mixnode;
pub mod keys;
pub mod rewards;
pub mod settings;
pub mod unbond_mixnode;
pub mod vesting_bond_mixnode;
pub mod vesting_unbond_mixnode;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsMixnode {
#[clap(subcommand)]
pub command: MixnetOperatorsMixnodeCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsMixnodeCommands {
/// Operations for mixnode keys
Keys(keys::MixnetOperatorsMixnodeKeys),
/// Manage your mixnode operator rewards
Rewards(rewards::MixnetOperatorsMixnodeRewards),
/// Manage your mixnode settings stored in the directory
Settings(settings::MixnetOperatorsMixnodeSettings),
/// Bond to a mixnode
Bond(bond_mixnode::Args),
/// Unbound from a mixnode
Unbound(unbond_mixnode::Args),
/// Bond to a mixnode with locked tokens
BondVesting(vesting_bond_mixnode::Args),
/// Unbound from a mixnode (when originally using locked tokens)
UnboundVesting(vesting_unbond_mixnode::Args),
}
@@ -0,0 +1,20 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn claim_operator_reward(_args: Args, client: SigningClient) {
info!("Claim operator reward");
let res = client
.execute_claim_operator_reward(None)
.await
.expect("failed to claim operator reward");
info!("Claiming operator reward: {:?}", res)
}
@@ -0,0 +1,22 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod claim_operator_reward;
pub mod vesting_claim_operator_reward;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsMixnodeRewards {
#[clap(subcommand)]
pub command: MixnetOperatorsMixnodeRewardsCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsMixnodeRewardsCommands {
/// Claim rewards
Claim(claim_operator_reward::Args),
/// Claim rewards for a mixnode bonded with locked tokens
VestingClaim(vesting_claim_operator_reward::Args),
}
@@ -0,0 +1,23 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub gas: Option<u64>,
}
pub async fn vesting_claim_operator_reward(client: SigningClient) {
info!("Claim vesting operator reward");
let res = client
.execute_vesting_claim_operator_reward(None)
.await
.expect("failed to claim vesting operator reward");
info!("Claiming vesting operator reward: {:?}", res)
}
@@ -0,0 +1,22 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod update_profit_percent;
pub mod vesting_update_profit_percent;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperatorsMixnodeSettings {
#[clap(subcommand)]
pub command: MixnetOperatorsMixnodeSettingsCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsMixnodeSettingsCommands {
/// Update profit percentage
UpdateProfitPercentage(update_profit_percent::Args),
/// Update profit percentage for a mixnode bonded with locked tokens
VestingUpdateProfitPercentage(vesting_update_profit_percent::Args),
}
@@ -0,0 +1,24 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub profit_percent: u8,
}
pub async fn update_profit_percent(args: Args, client: SigningClient) {
info!("Update mix node profit percent - get those rewards!");
//profit percent between 1-100
let res = client
.update_mixnode_config(args.profit_percent, None)
.await
.expect("updating mix-node profit percent");
info!("profit percentage updated: {:?}", res)
}
@@ -0,0 +1,28 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use validator_client::nymd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub profit_percent: u8,
#[clap(long)]
pub gas: Option<u64>,
}
pub async fn vesting_update_profit_percent(client: SigningClient, args: Args) {
info!("Update vesting mix node profit percent - get those rewards!");
//profit percent between 1-100
let res = client
.vesting_update_mixnode_config(args.profit_percent, None)
.await
.expect("updating vesting mix-node profit percent");
info!("profit percentage updated: {:?}", res)
}
@@ -0,0 +1,21 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::info;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {}
pub async fn unbond_mixnode(_args: Args, client: SigningClient) {
info!("Starting mixnode unbonding!");
let res = client
.unbond_mixnode(None)
.await
.expect("failed to unbond mixnode!");
info!("Unbonding result: {:?}", res)
}
@@ -0,0 +1,86 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::{info, warn};
use mixnet_contract_common::Coin;
use mixnet_contract_common::MixNode;
use network_defaults::{
DEFAULT_HTTP_API_LISTENING_PORT, DEFAULT_MIX_LISTENING_PORT, DEFAULT_VERLOC_LISTENING_PORT,
};
use validator_client::nymd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub host: String,
#[clap(long)]
pub signature: String,
#[clap(long)]
pub mix_port: Option<u16>,
#[clap(long)]
pub verloc_port: Option<u16>,
#[clap(long)]
pub http_api_port: Option<u16>,
#[clap(long)]
pub sphinx_key: String,
#[clap(long)]
pub identity_key: String,
#[clap(long)]
pub version: String,
#[clap(long)]
pub profit_margin_percent: Option<u8>,
#[clap(
long,
help = "bonding amount in current DENOMINATION (so it would be 'unym', rather than 'nym')"
)]
pub amount: u128,
#[clap(long)]
pub gas: Option<u64>,
#[clap(short, long)]
pub force: bool,
}
pub async fn vesting_bond_mixnode(client: SigningClient, args: Args, denom: &str) {
info!("Starting vesting mixnode bonding!");
// if we're trying to bond less than 1 token
if args.amount < 1_000_000 && !args.force {
warn!("You're trying to bond only {}{} which is less than 1 full token. Are you sure that's what you want? If so, run with `--force` or `-f` flag", args.amount, denom);
return;
}
let mixnode = MixNode {
host: args.host,
mix_port: args.mix_port.unwrap_or(DEFAULT_MIX_LISTENING_PORT),
verloc_port: args.verloc_port.unwrap_or(DEFAULT_VERLOC_LISTENING_PORT),
http_api_port: args
.http_api_port
.unwrap_or(DEFAULT_HTTP_API_LISTENING_PORT),
sphinx_key: args.sphinx_key,
identity_key: args.identity_key,
version: args.version,
profit_margin_percent: args.profit_margin_percent.unwrap_or(10),
};
let coin = Coin::new(args.amount, denom);
let res = client
.vesting_bond_mixnode(mixnode, &*args.signature, coin.into(), None)
.await
.expect("failed to bond vesting mixnode!");
info!("Bonding vesting result: {:?}", res)
}
@@ -0,0 +1,24 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::context::SigningClient;
use clap::Parser;
use log::info;
use validator_client::nymd::VestingSigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub gas: Option<u64>,
}
pub async fn vesting_unbond_mixnode(client: SigningClient) {
info!("Starting vesting mixnode unbonding!");
let res = client
.vesting_unbond_mixnode(None)
.await
.expect("failed to unbond vesting mixnode!");
info!("Unbonding vesting result: {:?}", res)
}
@@ -0,0 +1,22 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod gateway;
pub mod mixnode;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetOperators {
#[clap(subcommand)]
pub command: MixnetOperatorsCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetOperatorsCommands {
/// Manage your mixnode
Mixnode(mixnode::MixnetOperatorsMixnode),
/// Manage your gateway
Gateway(gateway::MixnetOperatorsGateway),
}
@@ -0,0 +1,22 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod query_all_gateways;
pub mod query_all_mixnodes;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct MixnetQuery {
#[clap(subcommand)]
pub command: MixnetQueryCommands,
}
#[derive(Debug, Subcommand)]
pub enum MixnetQueryCommands {
/// Query mixnodes
Mixnodes(query_all_mixnodes::Args),
/// Query gateways
Gateways(query_all_gateways::Args),
}
@@ -0,0 +1,52 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use comfy_table::Table;
use crate::context::QueryClientWithValidatorAPI;
use crate::utils::{pretty_cosmwasm_coin, show_error};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "Optionally, the gateway to display")]
pub identity_key: Option<String>,
}
pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) {
match client.validator_api.get_gateways().await {
Ok(res) => match args.identity_key {
Some(identity_key) => {
let node = res.iter().find(|node| {
node.gateway
.identity_key
.to_string()
.eq_ignore_ascii_case(&identity_key)
});
println!(
"{}",
::serde_json::to_string_pretty(&node).expect("json formatting error")
);
}
None => {
let mut table = Table::new();
table.set_header(vec!["Identity Key", "Owner", "Host", "Bond", "Version"]);
for node in res {
table.add_row(vec![
node.gateway.identity_key.to_string(),
node.owner.to_string(),
node.gateway.host.to_string(),
pretty_cosmwasm_coin(&node.pledge_amount),
node.gateway.version,
]);
}
println!("The gateways in the directory are:");
println!("{table}");
}
},
Err(e) => show_error(e),
}
}
@@ -0,0 +1,60 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use comfy_table::Table;
use crate::context::QueryClientWithValidatorAPI;
use crate::utils::{pretty_cosmwasm_coin, show_error};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "Optionally, the mixnode to display")]
pub identity_key: Option<String>,
}
pub async fn query(args: Args, client: &QueryClientWithValidatorAPI) {
match client.validator_api.get_mixnodes().await {
Ok(res) => match args.identity_key {
Some(identity_key) => {
let node = res.iter().find(|node| {
node.mix_node
.identity_key
.to_string()
.eq_ignore_ascii_case(&identity_key)
});
println!(
"{}",
::serde_json::to_string_pretty(&node).expect("json formatting error")
);
}
None => {
let mut table = Table::new();
table.set_header(vec![
"Identity Key",
"Owner",
"Host",
"Bond",
"Total Delegations",
"Version",
]);
for node in res {
table.add_row(vec![
node.mix_node.identity_key.to_string(),
node.owner.to_string(),
node.mix_node.host.to_string(),
pretty_cosmwasm_coin(&node.pledge_amount),
pretty_cosmwasm_coin(&node.total_delegation()),
node.mix_node.version,
]);
}
println!("The mixnodes in the directory are:");
println!("{table}");
}
},
Err(e) => show_error(e),
}
}
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod account;
pub mod block;
pub mod cosmwasm;
pub mod mixnet;
pub mod signature;
pub mod transactions;
pub mod vesting;
@@ -0,0 +1,13 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Errors {
#[error("signature error - {0}")]
SignatureError(#[from] k256::ecdsa::signature::Error),
#[error("{0}")]
CosmrsError(#[from] cosmrs::ErrorReport),
}
@@ -0,0 +1,90 @@
use std::str::FromStr;
use cosmrs::crypto::secp256k1::{Signature, VerifyingKey};
use cosmrs::crypto::PublicKey;
use k256::ecdsa::signature::Verifier;
use crate::validator::signature::errors::Errors;
pub fn secp256k1_verify_with_public_key(
public_key_as_bytes: &[u8],
signature_as_hex: String,
message: String,
) -> Result<(), k256::ecdsa::signature::Error> {
let verifying_key = VerifyingKey::from_sec1_bytes(public_key_as_bytes)?;
let signature = Signature::from_str(&signature_as_hex)?;
let message_as_bytes = message.into_bytes();
verifying_key.verify(&message_as_bytes, &signature)
}
pub fn secp256k1_verify_with_public_key_json(
public_key_as_json: String,
signature_as_hex: String,
message: String,
) -> Result<(), Errors> {
let public_key = PublicKey::from_json(&public_key_as_json)?;
let verifying_key = VerifyingKey::from_sec1_bytes(&public_key.to_bytes())?;
let signature = Signature::from_str(&signature_as_hex)?;
let message_as_bytes = message.into_bytes();
Ok(verifying_key.verify(&message_as_bytes, &signature)?)
}
#[cfg(test)]
mod test_secp256k1 {
use crate::validator::signature::helpers::{
secp256k1_verify_with_public_key, secp256k1_verify_with_public_key_json,
};
use cosmrs::crypto::PublicKey;
#[test]
fn test_verify_with_json_public_key_with_valid_signature() {
let json_public_key = r#"{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A4FdhUMasPmNhRZjtpKlmjNbq7EEUgPxfdI+E3vSajvc"}"#;
let signature_as_hex = "E3AA5AC0DA1B7DEBB7808000F719D8ACB9A0BE10AFA2756A788516268EB246A1257EC1097C5E364EF916145B01641DEDFE955994CB340BDAFA99A65BCA3F6F28".to_string();
let message = "test 1234".to_string();
let public_key = PublicKey::from_json(json_public_key).unwrap();
let public_key_bytes = public_key.to_bytes();
let result = secp256k1_verify_with_public_key(&public_key_bytes, signature_as_hex, message);
assert!(result.is_ok());
}
#[test]
fn test_verify_with_json_public_key_with_invalid_signature() {
let json_public_key = r#"{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A4FdhUMasPmNhRZjtpKlmjNbq7EEUgPxfdI+E3vSajvc"}"#;
let signature_as_hex = "E3AA5AC0DA1B7DEBB7808000F719D8ACB9A0BE10AFA2756A788516268EB246A1257EC1097C5E364EF916145B01641DEDFE955994CB340BDAFA99A65BCA3F6F28".to_string();
let message = "abcdef".to_string();
let public_key = PublicKey::from_json(json_public_key).unwrap();
let public_key_bytes = public_key.to_bytes();
let result = secp256k1_verify_with_public_key(&public_key_bytes, signature_as_hex, message);
assert!(result.is_err());
}
#[test]
fn test_valid_json_public_key_succeeds() {
let json_public_key = r#"{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A4FdhUMasPmNhRZjtpKlmjNbq7EEUgPxfdI+E3vSajvc"}"#.to_string();
let signature_as_hex = "E3AA5AC0DA1B7DEBB7808000F719D8ACB9A0BE10AFA2756A788516268EB246A1257EC1097C5E364EF916145B01641DEDFE955994CB340BDAFA99A65BCA3F6F28".to_string();
let message = "test 1234".to_string();
let result =
secp256k1_verify_with_public_key_json(json_public_key, signature_as_hex, message);
assert!(result.is_ok());
}
#[test]
fn test_json_public_key_fails_with_error() {
let bad_json_public_key = r#"This is not JSON ☠️"#.to_string();
let signature_as_hex = "E3AA5AC0DA1B7DEBB7808000F719D8ACB9A0BE10AFA2756A788516268EB246A1257EC1097C5E364EF916145B01641DEDFE955994CB340BDAFA99A65BCA3F6F28".to_string();
let message = "abcdef".to_string();
let result =
secp256k1_verify_with_public_key_json(bad_json_public_key, signature_as_hex, message);
assert!(result.is_err());
}
}
@@ -0,0 +1,24 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod errors;
pub mod helpers;
pub mod sign;
pub mod verify;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Signature {
#[clap(subcommand)]
pub command: Option<SignatureCommands>,
}
#[derive(Debug, Subcommand)]
pub enum SignatureCommands {
/// Sign a message
Sign(crate::validator::signature::sign::Args),
/// Verify a message
Verify(crate::validator::signature::verify::Args),
}
@@ -0,0 +1,68 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::utils::show_error;
use clap::Parser;
use cosmrs::crypto::PublicKey;
use log::error;
use serde::Serialize;
use serde_json::json;
use validator_client::nymd::wallet::DirectSecp256k1HdWallet;
#[derive(Debug, Serialize)]
pub struct SignatureOutputJson {
pub account_id: String,
pub public_key: PublicKey,
pub signature: String,
}
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "The message to sign")]
pub message: String,
}
pub fn sign(args: Args, prefix: &str, mnemonic: Option<bip39::Mnemonic>) {
if args.message.trim().is_empty() {
error!("Message is empty or contains only whitespace");
return;
}
if mnemonic.is_none() {
error!(
"Please provide the mnemonic as an argument or using the MNEMONIC environment variable"
);
return;
}
match DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic.expect("mnemonic not set")) {
Ok(wallet) => match wallet.try_derive_accounts() {
Ok(accounts) => match accounts.first() {
Some(account) => {
let msg = args.message.into_bytes();
match wallet.sign_raw_with_account(account, &msg) {
Ok(signature) => {
let output = SignatureOutputJson {
account_id: account.address().to_string(),
public_key: account.public_key(),
signature: signature.to_string(),
};
println!("{}", json!(output));
}
Err(e) => {
error!("Failed to sign message. {}", e);
}
}
}
None => {
error!("Could not derive an account key from the mnemonic",)
}
},
Err(e) => {
error!("Failed to derive accounts. {}", e);
}
},
Err(e) => show_error(e),
}
}
@@ -0,0 +1,89 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::str::FromStr;
use clap::Parser;
use cosmrs::crypto::PublicKey;
use log::{error, info};
use serde_json::json;
use validator_client::nymd::AccountId;
use crate::context::QueryClient;
use crate::validator::signature::helpers::secp256k1_verify_with_public_key;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(
help = "The public key of the account, or the account id to query for a public key (NOTE: the account must have signed a message stored on the chain for the public key record to exist)"
)]
pub public_key_or_address: String,
#[clap(value_parser)]
#[clap(help = "The signature to verify as hex")]
pub signature_as_hex: String,
#[clap(value_parser)]
#[clap(help = "The message to verify as a string")]
pub message: String,
}
pub async fn verify(args: Args, client: &QueryClient) {
if args.public_key_or_address.trim().is_empty() {
error!("Please ensure the public key or address is not empty or whitespace");
return;
}
let public_key = match AccountId::from_str(&args.public_key_or_address) {
Ok(address) => {
info!("Found account address instead of public key, so looking up public key for {} from chain", address);
match client.get_account_public_key(&address).await.ok() {
Some(public_key) => {
if let Some(k) = public_key {
info!("Found public key {}", json!(k));
}
public_key
}
None => {
error!(
"Address {} does not have a public key recorded on the chain. This is probably because the account has never signed a transaction.",
address
);
None
}
}
}
Err(_) => match PublicKey::from_json(&args.public_key_or_address) {
Ok(parsed) => Some(parsed),
Err(e) => {
error!("Public key should be JSON. Unable to parse: {}", e);
None
}
},
};
match public_key {
Some(public_key) => {
if public_key.type_url() != PublicKey::SECP256K1_TYPE_URL {
error!("Sorry, we only support secp256k1 public keys at the moment");
return;
}
match secp256k1_verify_with_public_key(
&public_key.to_bytes(),
args.signature_as_hex,
args.message,
) {
Ok(()) => println!("SUCCESS ✅ signature verified"),
Err(e) => {
error!("FAILURE ❌ Signature verification failed: {}", e);
}
}
}
None => {
error!("Unable to verify, as unable to get the public key");
}
}
}
@@ -0,0 +1,28 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use std::str::FromStr;
use crate::context::QueryClient;
use crate::utils::show_error;
use cosmrs::tx::Hash;
use serde_json::json;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "The transaction hash")]
pub tx_hash: String,
}
pub async fn get(args: Args, client: &QueryClient) {
let hash = Hash::from_str(&args.tx_hash).expect("could not parse transaction hash");
match client.get_tx(hash).await {
Ok(res) => {
println!("{}", json!(res))
}
Err(e) => show_error(e),
}
}
@@ -0,0 +1,22 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod get_transaction;
pub mod query_transactions;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct Transactions {
#[clap(subcommand)]
pub command: Option<TransactionsCommands>,
}
#[derive(Debug, Subcommand)]
pub enum TransactionsCommands {
/// Get a transaction by hash or block height
Get(crate::validator::transactions::get_transaction::Args),
/// Query for transactions
Query(crate::validator::transactions::query_transactions::Args),
}
@@ -0,0 +1,30 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::str::FromStr;
use clap::Parser;
use cosmrs::rpc::query::Query;
use serde_json::json;
use crate::context::QueryClient;
use crate::utils::show_error;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "The query to execute")]
pub query: String,
}
pub async fn query(args: Args, client: &QueryClient) {
match Query::from_str(&args.query) {
Ok(query) => match client.search_tx(query).await {
Ok(res) => {
println!("{}", json!(res))
}
Err(e) => show_error(e),
},
Err(e) => show_error(e),
}
}
@@ -0,0 +1,55 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use cosmrs::AccountId;
use log::info;
use validator_client::nymd::{Coin, VestingQueryClient};
use crate::context::SigningClient;
use crate::utils::show_error;
use crate::utils::{pretty_coin, pretty_cosmwasm_coin};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "Optionally, the account address to get the balance for")]
pub address: Option<AccountId>,
}
pub async fn balance(args: Args, client: SigningClient) {
let account_id = args.address.unwrap_or_else(|| client.address().clone());
let vesting_address = account_id.to_string();
let denom = client.current_chain_details().mix_denom.base.as_str();
info!(
"Getting vesting schedule information for {}...",
&vesting_address
);
let original_vesting = client.original_vesting(&vesting_address).await;
match original_vesting {
Ok(res) => {
let spendable_coins = client
.spendable_coins(&vesting_address, None)
.await
.unwrap_or_else(|_| Coin::new(0u128, denom));
let liquid_account_balance = client
.get_balance(&account_id, denom.to_string())
.await
.unwrap_or(None)
.unwrap_or_else(|| Coin::new(0u128, denom));
println!(
"Account {} has\n{} vested with\n{} available to be withdrawn to the main account (balance {})",
&account_id,
pretty_cosmwasm_coin(&res.amount),
pretty_coin(&spendable_coins),
pretty_coin(&liquid_account_balance),
);
}
Err(e) => show_error(e),
}
}
@@ -0,0 +1,83 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::str::FromStr;
use clap::Parser;
use log::info;
use mixnet_contract_common::Coin;
use network_defaults::NymNetworkDetails;
use validator_client::nymd::AccountId;
use validator_client::nymd::VestingSigningClient;
use validator_client::nymd::{CosmosCoin, Denom};
use vesting_contract_common::messages::VestingSpecification;
use crate::context::SigningClient;
#[derive(Debug, Parser)]
pub struct Args {
#[clap(long)]
pub periods_seconds: Option<u64>,
#[clap(long)]
pub number_of_periods: Option<u64>,
#[clap(long)]
pub start_time: Option<u64>,
#[clap(long)]
pub address: String,
#[clap(long)]
pub amount: u64,
#[clap(long)]
pub staking_address: Option<String>,
}
pub async fn create(args: Args, client: SigningClient, network_details: &NymNetworkDetails) {
info!("Creating vesting schedule!");
let vesting = VestingSpecification::new(
args.start_time,
args.periods_seconds,
args.number_of_periods,
);
let denom = network_details.chain_details.mix_denom.base.to_string();
let coin = Coin::new(args.amount.into(), &denom);
let res = client
.create_periodic_vesting_account(
&*args.address,
args.staking_address,
Some(vesting),
coin.into(),
None,
)
.await
.expect("creating vesting schedule for the user!");
//send 1 coin
let coin_amount: u64 = 1_000_000;
let coin = CosmosCoin {
denom: Denom::from_str(&denom).unwrap(),
amount: coin_amount.into(),
};
let send_coin_response = client
.send(
&AccountId::from_str(&*args.address).unwrap(),
vec![coin.into()],
"payment made :)",
None,
)
.await
.unwrap();
info!("Vesting result: {:?}", res);
info!("Coin send result: {:?}", send_coin_response);
}
@@ -0,0 +1,28 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::{Args, Subcommand};
pub mod balance;
pub mod create_vesting_schedule;
pub mod query_vesting_schedule;
pub mod withdraw_vested;
#[derive(Debug, Args)]
#[clap(args_conflicts_with_subcommands = true, subcommand_required = true)]
pub struct VestingSchedule {
#[clap(subcommand)]
pub command: Option<VestingScheduleCommands>,
}
#[derive(Debug, Subcommand)]
pub enum VestingScheduleCommands {
/// Creates a vesting schedule
Create(crate::validator::vesting::create_vesting_schedule::Args),
/// Query for vesting schedule
Query(crate::validator::vesting::query_vesting_schedule::Args),
/// Get the amount that has vested and is free for withdrawal, delegation or bonding
VestedBalance(crate::validator::vesting::balance::Args),
/// Withdraw vested tokens (note: the available amount excludes anything delegated or bonded before or after vesting)
WithdrawVested(crate::validator::vesting::withdraw_vested::Args),
}
@@ -0,0 +1,149 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use cosmrs::AccountId;
use cosmwasm_std::Coin as CosmWasmCoin;
use log::info;
use validator_client::nymd::{Coin, VestingQueryClient};
use crate::context::SigningClient;
use crate::utils::show_error;
use crate::utils::{pretty_coin, pretty_cosmwasm_coin};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "Optionally, the account address to get the balance for")]
pub address: Option<AccountId>,
}
pub async fn query(args: Args, client: SigningClient) {
let account_id = args.address.unwrap_or_else(|| client.address().clone());
let vesting_address = account_id.to_string();
let denom = client.current_chain_details().mix_denom.base.as_str();
info!(
"Getting vesting schedule information for {}...",
&vesting_address
);
let liquid_account_balance = client
.get_balance(&account_id, denom.to_string())
.await
.unwrap_or(None)
.unwrap_or_else(|| Coin::new(0u128, denom));
let original_vesting = client.original_vesting(&vesting_address).await;
let start_time = client.vesting_start_time(&vesting_address).await;
let end_time = client.vesting_end_time(&vesting_address).await;
let vested_coins = client.vested_coins(&vesting_address, None).await;
let spendable_coins = client.spendable_coins(&vesting_address, None).await;
let locked_coins = client.locked_coins(&vesting_address, None).await;
// TODO: get better copy text for what these are
let vesting_coins = client.vesting_coins(&vesting_address, None).await;
let delegated_vesting = client.delegated_vesting(&vesting_address, None).await;
let delegated_free = client.delegated_free(&vesting_address, None).await;
original_vesting.as_ref().map_or_else(show_error, |res| {
println!(
"Amount: {} ({})",
pretty_cosmwasm_coin(&res.amount),
res.amount
);
println!("No of periods: {}", res.number_of_periods);
println!(
"Duration each: {}",
time::Duration::seconds(res.period_duration as i64)
);
});
start_time.as_ref().map_or_else(show_error, |res| {
println!(
"Start date: {}",
time::OffsetDateTime::from_unix_timestamp(res.seconds() as i64)
.expect("unable to parse vesting start timestamp")
.date()
);
});
end_time.map_or_else(show_error, |res| {
println!(
"End date: {}",
time::OffsetDateTime::from_unix_timestamp(res.seconds() as i64)
.expect("unable to parse vesting end timestamp")
.date()
);
});
vested_coins.map_or_else(show_error, |res| {
println!("Vested balance: {} ({})", pretty_coin(&res), res);
});
if let Ok(res) = original_vesting {
if let Ok(start) = start_time {
let amount_in_each_period = res.amount.amount.u128() / res.number_of_periods as u128;
let coin_in_each_period = CosmWasmCoin::new(amount_in_each_period, denom);
println!();
println!("Vesting schedule:");
for period in 1..(res.number_of_periods as u64 + 1) {
let date = time::OffsetDateTime::from_unix_timestamp(
(start.seconds() + period * res.period_duration) as i64,
)
.expect("unable to parse vesting start timestamp")
.date();
let amount_in_vested =
period as u128 * res.amount.amount.u128() / res.number_of_periods as u128;
let coin_in_vested = CosmWasmCoin::new(amount_in_vested, denom);
println!(
"{}. {} {} => {}",
period,
date,
pretty_cosmwasm_coin(&coin_in_each_period),
pretty_cosmwasm_coin(&coin_in_vested),
);
}
}
}
spendable_coins.map_or_else(show_error, |res| {
println!();
println!("This account has the following vested tokens available either to be withdrawn to the main account, or to be delegated:");
println!("Spendable coins: {} ({})", pretty_coin(&res), res);
});
locked_coins.map_or_else(show_error, |res| {
println!();
if res.amount > 0 {
println!("This account has delegated more than the current cap, so the following balance is unavailable for bonding or delegation:");
println!("Locked balance: {} ({})", pretty_coin(&res), res);
} else {
println!("This account is not capped and can use the spendable balance for bonding or delegations:");
println!("Locked balance: {} ({})", pretty_coin(&res), res);
}
});
println!();
println!("The following are shown for information (more help text will follow soon):");
vesting_coins.map_or_else(show_error, |res| {
println!("Vesting coins: {} ({})", pretty_coin(&res), res);
});
delegated_vesting.map_or_else(show_error, |res| {
println!("Delegated vesting: {} ({})", pretty_coin(&res), res);
});
delegated_free.map_or_else(show_error, |res| {
println!("Delegation free: {} ({})", pretty_coin(&res), res);
});
println!();
println!(
"The main account {} also has a regular balance of:",
&account_id
);
println!(
"{} ({})",
pretty_coin(&liquid_account_balance),
&liquid_account_balance
);
}
@@ -0,0 +1,112 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use clap::Parser;
use log::info;
use validator_client::nymd::{Coin, VestingQueryClient, VestingSigningClient};
use crate::context::SigningClient;
use crate::utils::show_error;
use crate::utils::{pretty_coin, pretty_cosmwasm_coin};
#[derive(Debug, Parser)]
pub struct Args {
#[clap(value_parser)]
#[clap(help = "Amount to transfer in micro denomination (e.g. unym or unyx)")]
pub amount: u128,
}
pub async fn execute(args: Args, client: SigningClient) {
let account_id = client.address();
let vesting_address = account_id.to_string();
let denom = client.current_chain_details().mix_denom.base.as_str();
info!(
"Getting vesting schedule information for {}...",
&vesting_address
);
let original_vesting = client.original_vesting(&vesting_address).await;
match original_vesting {
Ok(res) => {
let spendable_coins = client
.spendable_coins(&vesting_address, None)
.await
.unwrap_or_else(|_| Coin::new(0u128, denom));
let liquid_account_balance = client
.get_balance(account_id, denom.to_string())
.await
.unwrap_or(None)
.unwrap_or_else(|| Coin::new(0u128, denom));
println!(
"Account {} has\n{} vested with {} available to be withdrawn to the main account (balance {})",
&account_id,
pretty_cosmwasm_coin(&res.amount),
pretty_coin(&spendable_coins),
pretty_coin(&liquid_account_balance),
);
println!();
// execute withdraw
let amount = Coin {
amount: args.amount,
denom: denom.to_string(),
};
info!(
"Withdrawing {} ({}) from {}...",
pretty_coin(&amount),
&amount,
&account_id
);
match client.withdraw_vested_coins(amount, None).await {
Ok(res) => {
println!();
println!("SUCCESS ✅");
println!(
"Nodesguru: https://nym.explorers.guru/transaction/{}",
&res.transaction_hash
);
println!(
"Mintscan: https://www.mintscan.io/nyx/txs/{}",
&res.transaction_hash
);
println!("Transaction hash: {}", &res.transaction_hash);
println!("Gas used: {}", &res.gas_info.gas_used);
println!();
}
Err(e) => show_error(e),
}
// query for balances again
let res = client
.original_vesting(&vesting_address)
.await
.expect("vesting account does not exist");
let spendable_coins = client
.spendable_coins(&vesting_address, None)
.await
.unwrap_or_else(|_| Coin::new(0u128, denom));
let liquid_account_balance = client
.get_balance(account_id, denom.to_string())
.await
.unwrap_or(None)
.unwrap_or_else(|| Coin::new(0u128, denom));
println!(
"After withdrawal, account {} has\n{} vested with {} available to be withdrawn to the main account (balance {})",
&account_id,
pretty_cosmwasm_coin(&res.amount),
pretty_coin(&spendable_coins),
pretty_coin(&liquid_account_balance),
);
}
Err(e) => show_error(e),
}
}
@@ -119,6 +119,11 @@ pub enum ExecuteMsg {
UpdateLockedPledgeCap {
amount: Uint128,
},
MigrateHeightsToTimestamps {
account_id: u32,
mix_identity: String,
height_timestamp_map: Vec<(u64, u64)>,
},
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
+20
View File
@@ -0,0 +1,20 @@
CONFIGURED=true
RUST_LOG=info
RUST_BACKTRACE=1
BECH32_PREFIX=n
MIX_DENOM=unym
MIX_DENOM_DISPLAY=nym
STAKE_DENOM=unyx
STAKE_DENOM_DISPLAY=nyx
DENOMS_EXPONENT=6
MIXNET_CONTRACT_ADDRESS=n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g
VESTING_CONTRACT_ADDRESS=n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
MULTISIG_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
REWARDING_VALIDATOR_ADDRESS=n10yyd98e2tuwu0f7ypz9dy3hhjw7v772q6287gy
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://127.0.0.1:8090"
NYMD_VALIDATOR="https://rpc.nyx.nodes.guru/"
API_VALIDATOR="https://validator.nymtech.net/api/"
+20
View File
@@ -0,0 +1,20 @@
CONFIGURED=true
RUST_LOG=info
RUST_BACKTRACE=1
BECH32_PREFIX=n
MIX_DENOM=unym
MIX_DENOM_DISPLAY=nym
STAKE_DENOM=unyx
STAKE_DENOM_DISPLAY=nyx
DENOMS_EXPONENT=6
MIXNET_CONTRACT_ADDRESS=n1suhgf5svhu4usrurvxzlgn54ksxmn8gljarjtxqnapv8kjnp4nrsd3qaep
VESTING_CONTRACT_ADDRESS=n1xr3rq8yvd7qplsw5yx90ftsr2zdhg4e9z60h5duusgxpv72hud3sjkxkav
BANDWIDTH_CLAIM_CONTRACT_ADDRESS=n19lc9u84cz0yz3fww5283nucc9yvr8gsjmgeul0
COCONUT_BANDWIDTH_CONTRACT_ADDRESS=n1ghd753shjuwexxywmgs4xz7x2q732vcn7ty4yw
MULTISIG_CONTRACT_ADDRESS=n17p9rzwnnfxcjp32un9ug7yhhzgtkhvl988qccs
REWARDING_VALIDATOR_ADDRESS=n1tfzd4qz3a45u8p4mr5zmzv66457uwjgcl05jdq
STATISTICS_SERVICE_DOMAIN_ADDRESS="http://0.0.0.0"
NYMD_VALIDATOR="https://qa-validator.nymtech.net"
API_VALIDATOR="https://qa-validator-api.nymtech.net/api"
+1 -1
View File
@@ -89,7 +89,7 @@ impl Node {
}
pub fn clients_address(&self) -> String {
format!("wss://{}:{}", self.host, self.clients_port)
format!("ws://{}:{}", self.host, self.clients_port)
}
}
+2 -2
View File
@@ -9,12 +9,12 @@ edition = "2021"
[dependencies]
futures = "0.3"
js-sys = "^0.3.51"
wasm-bindgen = "=0.2.83"
wasm-bindgen = "=0.2.78"
wasm-bindgen-futures = "0.4"
# we don't want entire tokio-tungstenite, tungstenite itself is just fine - we just want message and error enums
[dependencies.tungstenite]
version = "0.17.3"
version = "0.13"
default-features = false
[dependencies.web-sys]
-5
View File
@@ -85,11 +85,6 @@ pub struct JSWebsocket {
impl JSWebsocket {
pub fn new(url: &str) -> Result<Self, JsValue> {
console_log!(
"Attempting to establish wasm websocket connection to {}",
url
);
let ws = WebSocket::new(url)?;
// we don't want to ever have to deal with blobs
ws.set_binary_type(web_sys::BinaryType::Arraybuffer);
+38 -2
View File
@@ -1,8 +1,9 @@
use crate::errors::ContractError;
use crate::queued_migrations::migrate_config_from_env;
use crate::storage::{
account_from_address, locked_pledge_cap, update_locked_pledge_cap, BlockTimestampSecs, ADMIN,
DELEGATIONS, MIXNET_CONTRACT_ADDRESS, MIX_DENOM,
account_from_address, locked_pledge_cap, remove_delegation, save_delegation,
update_locked_pledge_cap, BlockTimestampSecs, ADMIN, DELEGATIONS, MIXNET_CONTRACT_ADDRESS,
MIX_DENOM,
};
use crate::traits::{
DelegatingAccount, GatewayBondingAccount, MixnodeBondingAccount, VestingAccount,
@@ -129,6 +130,17 @@ pub fn execute(
ExecuteMsg::UpdateStakingAddress { to_address } => {
try_update_staking_address(to_address, info, deps)
}
ExecuteMsg::MigrateHeightsToTimestamps {
account_id,
mix_identity,
height_timestamp_map,
} => try_migrate_heights_to_timestamps(
account_id,
mix_identity,
height_timestamp_map,
info,
deps,
),
}
}
@@ -240,6 +252,30 @@ fn try_update_staking_address(
}
}
pub fn try_migrate_heights_to_timestamps(
account_id: u32,
mix_identity: String,
height_timestamp_map: Vec<(u64, u64)>,
info: MessageInfo,
deps: DepsMut<'_>,
) -> Result<Response, ContractError> {
if info.sender != ADMIN.load(deps.storage)? {
return Err(ContractError::NotAdmin(info.sender.as_str().to_string()));
}
for (height, timestamp) in height_timestamp_map {
let amount = DELEGATIONS.load(deps.storage, (account_id, mix_identity.clone(), height))?;
remove_delegation((account_id, mix_identity.clone(), height), deps.storage)?;
save_delegation(
(account_id, mix_identity.clone(), timestamp),
amount,
deps.storage,
)?;
}
Ok(Response::default())
}
// Owner or staking
pub fn try_bond_gateway(
gateway: Gateway,
+95 -2
View File
@@ -36,8 +36,8 @@ pub fn populate_vesting_periods(
#[cfg(test)]
mod tests {
use crate::contract::execute;
use crate::storage::load_account;
use crate::contract::*;
use crate::storage::*;
use crate::support::tests::helpers::{
init_contract, vesting_account_mid_fixture, vesting_account_new_fixture, TEST_COIN_DENOM,
};
@@ -925,4 +925,97 @@ mod tests {
// the 50M delegation wasn't a thing here for VESTING tokens either
assert_eq!(delegated_vesting.amount, Uint128::zero());
}
#[test]
fn migrate_heights_to_timestamps() {
let mut deps = init_contract();
let mut env = mock_env();
let account = vesting_account_new_fixture(&mut deps.storage, &env);
let mix_identity = String::from("identity");
let mut curr_block = env.block.clone();
let mut delegation_blocks = std::iter::from_fn(move || {
curr_block.height += 1;
curr_block.time = curr_block.time.plus_seconds(5);
Some(curr_block.clone())
})
.take(100)
.collect::<Vec<_>>();
for block in delegation_blocks.iter() {
DELEGATIONS
.save(
&mut deps.storage,
(account.storage_key(), mix_identity.clone(), block.height),
&Uint128::new(90_000_000_000),
)
.unwrap();
}
let delegations = try_get_delegation_times(
deps.as_ref(),
account.owner_address().as_str(),
mix_identity.clone(),
)
.unwrap();
assert_eq!(
delegations.delegation_timestamps.len(),
delegation_blocks.len()
);
for (heights, delegation_block) in delegations
.delegation_timestamps
.iter()
.zip(delegation_blocks.iter())
{
assert_eq!(*heights, delegation_block.height);
}
let height_timestamp_map = delegation_blocks
.iter()
.map(|block| (block.height, block.time.seconds()))
.collect();
let admin = ADMIN.load(&deps.storage).unwrap();
try_migrate_heights_to_timestamps(
account.storage_key(),
mix_identity.clone(),
height_timestamp_map,
mock_info(&admin, &[]),
deps.as_mut(),
)
.unwrap();
// Some new delegation appears during migration
env.block.height += 200;
env.block.time = env.block.time.plus_seconds(1000);
delegation_blocks.push(env.block.clone());
account
.try_delegate_to_mixnode(
String::from("identity"),
Coin {
amount: Uint128::new(90_000_000_000),
denom: TEST_COIN_DENOM.to_string(),
},
&env,
&mut deps.storage,
)
.unwrap();
let delegations = try_get_delegation_times(
deps.as_ref(),
account.owner_address().as_str(),
mix_identity.clone(),
)
.unwrap();
assert_eq!(
delegations.delegation_timestamps.len(),
delegation_blocks.len()
);
for (timestamp, delegation_block) in delegations
.delegation_timestamps
.iter()
.zip(delegation_blocks.iter())
{
assert_eq!(*timestamp, delegation_block.time.seconds());
}
}
}
+9
View File
@@ -0,0 +1,9 @@
# Examples
This directory contains examples of using the libraries in this repo:
### CLI
#### CLI Commands
- [Use an account public key to verify a signature](./cli/commands/verify-signature)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,9 @@
[package]
name = "example-verify-signature"
version = "0.1.0"
edition = "2021"
[workspace]
[dependencies]
nym-cli-commands = { git = "https://github.com/nymtech/nym", branch = "develop" }
@@ -0,0 +1,38 @@
# Example code to verify a signature
This is an example app that shows how to verify a signature signed by an account key.
Inputs to the app are:
- **signature** - bytes represented as a hex string
- **public key** - in JSON format (you can query any Cosmos chain for account's public key as JSON, however it will need to have sent a signed transaction to the chain for this to be present)
- **message** - the string message to verify
## Running locally
Run the example by changning to this directory and running:
```
cargo run
```
And you should see the output:
```
Nym signature verification example
public key: {"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A4FdhUMasPmNhRZjtpKlmjNbq7EEUgPxfdI+E3vSajvc"}
signature: E3AA5AC0DA1B7DEBB7808000F719D8ACB9A0BE10AFA2756A788516268EB246A1257EC1097C5E364EF916145B01641DEDFE955994CB340BDAFA99A65BCA3F6F28
message: test 1234
Verify the correct message:
SUCCESS ✅ signature is valid
Verify another message:
FAILURE ❌ signature is not valid: signature error
```
@@ -0,0 +1,43 @@
use nym_cli_commands::validator::signature::helpers::secp256k1_verify_with_public_key_json;
fn main() {
println!("\nNym signature verification example\n\n");
// the public key in JSON format (because Cosmos supports secp256k1 and ed25519 - NB: the helper only supports secp256k1)
let public_key_as_json = r#"{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A4FdhUMasPmNhRZjtpKlmjNbq7EEUgPxfdI+E3vSajvc"}"#;
// the signature as a string of hex characters to represent the bytes in the signature
let signature_as_hex = "E3AA5AC0DA1B7DEBB7808000F719D8ACB9A0BE10AFA2756A788516268EB246A1257EC1097C5E364EF916145B01641DEDFE955994CB340BDAFA99A65BCA3F6F28";
// the original message as a string to verify
let message = "test 1234".to_string();
println!("public key: {}", &public_key_as_json);
println!("signature: {}", &signature_as_hex);
println!("message: {}", &message);
println!();
// this will pass, because the signature was signed for this message
println!("\nVerify the correct message:\n");
do_verify(
public_key_as_json.to_string(),
signature_as_hex.to_string(),
message,
);
// this will fail, because the signature is for another message
println!("\n\nVerify another message:\n");
do_verify(
public_key_as_json.to_string(),
signature_as_hex.to_string(),
"another message that will fail".to_string(),
);
}
fn do_verify(public_key_as_json: String, signature_as_hex: String, message: String) {
match secp256k1_verify_with_public_key_json(public_key_as_json, signature_as_hex, message) {
Ok(()) => println!("SUCCESS ✅ signature is valid"),
Err(e) => println!("FAILURE ❌ signature is not valid: {}", e),
}
}
+1 -1
View File
@@ -33,7 +33,7 @@ subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
thiserror = "1"
tokio = { version = "1.19.1", features = [ "rt-multi-thread", "net", "signal", "fs" ] }
tokio-stream = { version = "0.1.9", features = [ "fs" ] }
tokio-tungstenite = "0.17"
tokio-tungstenite = "0.14"
tokio-util = { version = "0.7.3", features = [ "codec" ] }
url = { version = "2.2", features = [ "serde" ] }
web3 = "0.17.0"
+1 -1
View File
@@ -29,6 +29,6 @@ credentials = { path = "../../common/credentials" }
coconut = ["coconut-interface", "credentials/coconut"]
[dependencies.tungstenite]
version = "0.17.3"
version = "0.13.0"
default-features = false
@@ -9,8 +9,7 @@ pub use self::shared_key::{SharedKeySize, SharedKeys};
use crypto::asymmetric::identity;
use futures::{Sink, Stream};
use rand::{CryptoRng, RngCore};
use tungstenite::error::Error as WsError;
use tungstenite::protocol::Message as WsMessage;
use tungstenite::{Error as WsError, Message as WsMessage};
pub(crate) type WsItem = Result<WsMessage, WsError>;
+9 -8
View File
@@ -18,6 +18,7 @@ use std::{
convert::{TryFrom, TryInto},
fmt::{self, Error, Formatter},
};
use tungstenite::protocol::Message;
#[cfg(feature = "coconut")]
use coconut_interface::Credential;
@@ -191,12 +192,12 @@ impl ClientControlRequest {
}
}
impl From<ClientControlRequest> for tungstenite::Message {
impl From<ClientControlRequest> for Message {
fn from(req: ClientControlRequest) -> Self {
// it should be safe to call `unwrap` here as the message is generated by the server
// so if it fails (and consequently panics) it's a bug that should be resolved
let str_req = serde_json::to_string(&req).unwrap();
tungstenite::Message::Text(str_req)
Message::Text(str_req)
}
}
@@ -257,12 +258,12 @@ impl ServerResponse {
}
}
impl From<ServerResponse> for tungstenite::Message {
impl From<ServerResponse> for Message {
fn from(res: ServerResponse) -> Self {
// it should be safe to call `unwrap` here as the message is generated by the server
// so if it fails (and consequently panics) it's a bug that should be resolved
let str_res = serde_json::to_string(&res).unwrap();
tungstenite::Message::Text(str_res)
Message::Text(str_res)
}
}
@@ -313,8 +314,8 @@ impl BinaryRequest {
BinaryRequest::ForwardSphinx(mix_packet)
}
pub fn into_ws_message(self, shared_key: &SharedKeys) -> tungstenite::Message {
tungstenite::Message::Binary(self.into_encrypted_tagged_bytes(shared_key))
pub fn into_ws_message(self, shared_key: &SharedKeys) -> Message {
Message::Binary(self.into_encrypted_tagged_bytes(shared_key))
}
}
@@ -366,8 +367,8 @@ impl BinaryResponse {
BinaryResponse::PushedMixMessage(msg)
}
pub fn into_ws_message(self, shared_key: &SharedKeys) -> tungstenite::Message {
tungstenite::Message::Binary(self.into_encrypted_tagged_bytes(shared_key))
pub fn into_ws_message(self, shared_key: &SharedKeys) -> Message {
Message::Binary(self.into_encrypted_tagged_bytes(shared_key))
}
}

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