Remove eth feature, and BBBC related code (#1612)
* Remove eth feature, and BBBC related code * Burn some more, especially clients
This commit is contained in:
Generated
+177
-194
@@ -81,6 +81,15 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
@@ -92,9 +101,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.64"
|
||||
version = "1.0.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9a8f622bcf6ff3df478e9deba3e03e4e04b300f8e6a139e192c05fa3490afc7"
|
||||
checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
@@ -173,23 +182,6 @@ dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "attohttpc"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e69e13a99a7e6e070bb114f7ff381e58c7ccc188630121fc4c2fe4bcf24cd072"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"http",
|
||||
"log",
|
||||
"native-tls",
|
||||
"openssl",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
"wildmatch",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@@ -218,9 +210,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.5.15"
|
||||
version = "0.5.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9de18bc5f2e9df8f52da03856bf40e29b747de5a84e43aefff90e3dc4a21529b"
|
||||
checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
@@ -247,9 +239,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4f44a0e6200e9d11a1cdc989e4b358f6e3d354fbf48478f345a17f4e43f8635"
|
||||
checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
@@ -257,6 +249,8 @@ dependencies = [
|
||||
"http",
|
||||
"http-body",
|
||||
"mime",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -548,10 +542,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.20"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6127248204b9aba09a362f6c930ef6a78f2c1b2215f8a7b398c06e1083f17af0"
|
||||
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
@@ -593,9 +588,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.20"
|
||||
version = "3.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd"
|
||||
checksum = "1ed5341b2301a26ab80be5cbdced622e80ed808483c52e45e3310a877d3b37d7"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
@@ -610,11 +605,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_complete"
|
||||
version = "3.2.4"
|
||||
version = "3.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4179da71abd56c26b54dd0c248cc081c1f43b0a1a7e8448e28e57a29baa993d"
|
||||
checksum = "3f7a2e0a962c45ce25afce14220bc24f9dade0a1787f185cecf96bfba7847cd8"
|
||||
dependencies = [
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -623,7 +618,7 @@ version = "3.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed37b4c0c1214673eba6ad8ea31666626bf72be98ffb323067d973c48b4964b9"
|
||||
dependencies = [
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
"clap_complete",
|
||||
]
|
||||
|
||||
@@ -736,7 +731,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"handlebars",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"serde",
|
||||
@@ -965,7 +959,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"bip39",
|
||||
"cfg-if 0.1.10",
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
"coconut-interface",
|
||||
"config",
|
||||
"credential-storage",
|
||||
@@ -988,7 +982,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"log",
|
||||
"nymcoconut",
|
||||
"sqlx 0.5.13",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@@ -1139,7 +1132,6 @@ dependencies = [
|
||||
"blake3",
|
||||
"bs58",
|
||||
"cipher 0.4.3",
|
||||
"config",
|
||||
"ctr 0.9.1",
|
||||
"digest 0.10.3",
|
||||
"ed25519-dalek",
|
||||
@@ -1606,7 +1598,16 @@ version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2953d1df47ac0eb70086ccabf0275aa8da8591a28bd358ee2b52bd9f9e3ff9e9"
|
||||
dependencies = [
|
||||
"enum-iterator-derive",
|
||||
"enum-iterator-derive 0.8.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-iterator"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45a0ac4aeb3a18f92eaf09c6bb9b3ac30ff61ca95514fc58cbead1c9a6bf5401"
|
||||
dependencies = [
|
||||
"enum-iterator-derive 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1620,6 +1621,17 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-iterator-derive"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "828de45d0ca18782232dfb8f3ea9cc428e8ced380eb26a520baaacfc70de39ce"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
@@ -1704,7 +1716,7 @@ name = "explorer-api"
|
||||
version = "1.0.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
"humantime-serde",
|
||||
"isocountry",
|
||||
"itertools",
|
||||
@@ -1874,11 +1886,10 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.0.1"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
|
||||
checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
@@ -2048,7 +2059,6 @@ dependencies = [
|
||||
"futures",
|
||||
"gateway-requests",
|
||||
"getrandom 0.2.7",
|
||||
"json",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"nymsphinx",
|
||||
@@ -2176,9 +2186,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.15.0"
|
||||
version = "0.14.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1"
|
||||
checksum = "d0155506aab710a86160ddb504a480d2964d7ab5b9e62419be69e0032bc5931c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
@@ -2244,7 +2254,7 @@ dependencies = [
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
@@ -2579,6 +2589,20 @@ dependencies = [
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "237a0714f28b1ee39ccec0770ccb544eb02c9ef2c82bb096230eefcffa6468b0"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
@@ -2723,9 +2747,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.3"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
||||
checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
@@ -2760,12 +2784,6 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "json"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-core"
|
||||
version = "18.0.0"
|
||||
@@ -2821,9 +2839,9 @@ checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.14.0+1.5.0"
|
||||
version = "0.13.4+1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47a00859c70c8a4f7218e6d1cc32875c4b55f6799445b842b0d8ed5e4c3d959b"
|
||||
checksum = "d0fa6563431ede25f5cc7f6d803c6afbc1c5d3ad3d4925d12c882bf2b526f5d1"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -2995,7 +3013,7 @@ dependencies = [
|
||||
"log",
|
||||
"nymsphinx",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3021,11 +3039,9 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"crypto",
|
||||
"dashmap",
|
||||
"futures",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"nonexhaustive-delayqueue",
|
||||
"nymsphinx-acknowledgements",
|
||||
"nymsphinx-addressing",
|
||||
"nymsphinx-forwarding",
|
||||
@@ -3036,7 +3052,7 @@ dependencies = [
|
||||
"serde",
|
||||
"task",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
"url",
|
||||
"validator-client",
|
||||
"version-checker",
|
||||
@@ -3058,7 +3074,7 @@ dependencies = [
|
||||
"mime",
|
||||
"spin 0.9.4",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
@@ -3121,7 +3137,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3191,7 +3207,7 @@ dependencies = [
|
||||
"base64",
|
||||
"bip39",
|
||||
"bs58",
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
"clap_complete",
|
||||
"clap_complete_fig",
|
||||
"dotenv",
|
||||
@@ -3213,7 +3229,7 @@ dependencies = [
|
||||
"bip39",
|
||||
"bs58",
|
||||
"cfg-if 1.0.0",
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
"comfy-table",
|
||||
"cosmrs",
|
||||
"cosmwasm-std",
|
||||
@@ -3238,7 +3254,7 @@ dependencies = [
|
||||
name = "nym-client"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
"client-core",
|
||||
"coconut-interface",
|
||||
"config",
|
||||
@@ -3264,7 +3280,7 @@ dependencies = [
|
||||
"topology",
|
||||
"url",
|
||||
"validator-client",
|
||||
"vergen",
|
||||
"vergen 5.1.17",
|
||||
"version-checker",
|
||||
"websocket-requests",
|
||||
]
|
||||
@@ -3275,10 +3291,9 @@ version = "1.0.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"bandwidth-claim-contract",
|
||||
"bip39",
|
||||
"bs58",
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
"coconut-interface",
|
||||
"colored",
|
||||
"config",
|
||||
@@ -3288,7 +3303,6 @@ dependencies = [
|
||||
"dirs",
|
||||
"dotenv",
|
||||
"futures",
|
||||
"gateway-client",
|
||||
"gateway-requests",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
@@ -3308,13 +3322,12 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-tungstenite 0.14.0",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
"url",
|
||||
"validator-api-requests",
|
||||
"validator-client",
|
||||
"vergen",
|
||||
"vergen 5.1.17",
|
||||
"version-checker",
|
||||
"web3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3323,7 +3336,7 @@ version = "1.0.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bs58",
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
"colored",
|
||||
"config",
|
||||
"crypto",
|
||||
@@ -3348,12 +3361,12 @@ dependencies = [
|
||||
"sysinfo",
|
||||
"task",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
"toml",
|
||||
"topology",
|
||||
"url",
|
||||
"validator-client",
|
||||
"vergen",
|
||||
"vergen 5.1.17",
|
||||
"version-checker",
|
||||
]
|
||||
|
||||
@@ -3362,7 +3375,7 @@ name = "nym-network-requester"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
"dirs",
|
||||
"futures",
|
||||
"ipnetwork",
|
||||
@@ -3377,7 +3390,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"socks5-requests",
|
||||
"sqlx 0.6.1",
|
||||
"sqlx 0.6.2",
|
||||
"statistics-common",
|
||||
"task",
|
||||
"thiserror",
|
||||
@@ -3399,14 +3412,13 @@ dependencies = [
|
||||
"statistics-common",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-tungstenite 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nym-socks5-client"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
"client-core",
|
||||
"coconut-interface",
|
||||
"config",
|
||||
@@ -3434,7 +3446,7 @@ dependencies = [
|
||||
"topology",
|
||||
"url",
|
||||
"validator-client",
|
||||
"vergen",
|
||||
"vergen 5.1.17",
|
||||
"version-checker",
|
||||
]
|
||||
|
||||
@@ -3470,9 +3482,8 @@ version = "1.0.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"attohttpc",
|
||||
"cfg-if 1.0.0",
|
||||
"clap 3.2.20",
|
||||
"clap 3.2.21",
|
||||
"coconut-bandwidth-contract-common",
|
||||
"coconut-interface",
|
||||
"config",
|
||||
@@ -3504,12 +3515,10 @@ dependencies = [
|
||||
"rocket",
|
||||
"rocket_cors",
|
||||
"rocket_okapi",
|
||||
"rocket_sync_db_pools",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx 0.5.13",
|
||||
"sqlx 0.6.1",
|
||||
"sqlx 0.6.2",
|
||||
"tap",
|
||||
"task",
|
||||
"thiserror",
|
||||
@@ -3521,7 +3530,7 @@ dependencies = [
|
||||
"url",
|
||||
"validator-api-requests",
|
||||
"validator-client",
|
||||
"vergen",
|
||||
"vergen 7.4.2",
|
||||
"version-checker",
|
||||
]
|
||||
|
||||
@@ -3664,7 +3673,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"nymsphinx-params",
|
||||
"nymsphinx-types",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3953,15 +3962,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b0560d531d1febc25a3c9398a62a71256c0178f2e3443baedd9ad4bb8c9deb4"
|
||||
checksum = "cb779fcf4bb850fbbb0edc96ff6cf34fd90c4b1a112ce042653280d9a7364048"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
@@ -3969,9 +3978,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "905708f7f674518498c1f8d644481440f476d39ca6ecae83319bba7c6c12da91"
|
||||
checksum = "502b62a6d0245378b04ffe0a7fb4f4419a4815fce813bd8a0ec89a56e07d67b1"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
@@ -3979,9 +3988,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5803d8284a629cc999094ecd630f55e91b561a1d1ba75e233b00ae13b91a69ad"
|
||||
checksum = "451e629bf49b750254da26132f1a5a9d11fd8a95a3df51d15c4abd1ba154cb6c"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
@@ -3992,13 +4001,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.3.0"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1538eb784f07615c6d9a8ab061089c6c54a344c5b4301db51990ca1c241e8c04"
|
||||
checksum = "bcec162c71c45e269dfc3fc2916eaeb97feab22993a21bcce4721d08cd7801a6"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
"sha-1 0.10.0",
|
||||
"sha1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4065,9 +4074,9 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "716b4eeb6c4a1d3ecc956f75b43ec2e8e8ba80026413e70a3f41fd3313d3492b"
|
||||
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"plotters-backend",
|
||||
@@ -4267,7 +4276,7 @@ dependencies = [
|
||||
"task",
|
||||
"tokio",
|
||||
"tokio-test",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4305,17 +4314,6 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r2d2"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
|
||||
dependencies = [
|
||||
"log",
|
||||
"parking_lot 0.12.1",
|
||||
"scheduled-thread-pool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.6.2"
|
||||
@@ -4740,7 +4738,7 @@ dependencies = [
|
||||
"time 0.3.14",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
"ubyte",
|
||||
"version_check",
|
||||
"yansi",
|
||||
@@ -4833,29 +4831,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket_sync_db_pools"
|
||||
version = "0.1.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fa48b6ab25013e9812f1b0c592741900b3a2a83c0936292e0565c0ac842f558"
|
||||
dependencies = [
|
||||
"r2d2",
|
||||
"rocket",
|
||||
"rocket_sync_db_pools_codegen",
|
||||
"serde",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rocket_sync_db_pools_codegen"
|
||||
version = "0.1.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "280ef2d232923e69cb93da156972eb5476a7cce5ba44843f6608f46a4abf7aab"
|
||||
dependencies = [
|
||||
"devise",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hex"
|
||||
version = "2.1.0"
|
||||
@@ -4877,7 +4852,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver 1.0.13",
|
||||
"semver 1.0.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4957,15 +4932,6 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scheduled-thread-pool"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "977a7519bff143a44f842fd07e80ad1329295bd71686457f18e496736f4bf9bf"
|
||||
dependencies = [
|
||||
"parking_lot 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.10"
|
||||
@@ -5097,9 +5063,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.13"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711"
|
||||
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
@@ -5423,7 +5389,6 @@ name = "socks5-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nymsphinx-addressing",
|
||||
"ordered-buffer",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -5501,6 +5466,17 @@ dependencies = [
|
||||
"unicode_categories",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlformat"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f87e292b4291f154971a43c3774364e2cbcaec599d3f5bf6fa9d122885dbc38a"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"nom",
|
||||
"unicode_categories",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.5.13"
|
||||
@@ -5513,12 +5489,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "788841def501aabde58d3666fcea11351ec3962e6ea75dbcd05c84a71d68bcd1"
|
||||
checksum = "9249290c05928352f71c077cc44a464d880c63f26f7534728cca008e135c0428"
|
||||
dependencies = [
|
||||
"sqlx-core 0.6.1",
|
||||
"sqlx-macros 0.6.1",
|
||||
"sqlx-core 0.6.2",
|
||||
"sqlx-macros 0.6.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5557,7 +5533,7 @@ dependencies = [
|
||||
"rustls 0.19.1",
|
||||
"sha2 0.10.5",
|
||||
"smallvec 1.9.0",
|
||||
"sqlformat",
|
||||
"sqlformat 0.1.8",
|
||||
"sqlx-rt 0.5.13",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
@@ -5569,9 +5545,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-core"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c21d3b5e7cadfe9ba7cdc1295f72cc556c750b4419c27c219c0693198901f8e"
|
||||
checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"atoi 1.0.0",
|
||||
@@ -5605,8 +5581,8 @@ dependencies = [
|
||||
"rustls-pemfile",
|
||||
"sha2 0.10.5",
|
||||
"smallvec 1.9.0",
|
||||
"sqlformat",
|
||||
"sqlx-rt 0.6.1",
|
||||
"sqlformat 0.2.0",
|
||||
"sqlx-rt 0.6.2",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"tokio-stream",
|
||||
@@ -5635,9 +5611,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4adfd2df3557bddd3b91377fc7893e8fa899e9b4061737cbade4e1bb85f1b45c"
|
||||
checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9"
|
||||
dependencies = [
|
||||
"dotenvy",
|
||||
"either",
|
||||
@@ -5646,8 +5622,8 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"sha2 0.10.5",
|
||||
"sqlx-core 0.6.1",
|
||||
"sqlx-rt 0.6.1",
|
||||
"sqlx-core 0.6.2",
|
||||
"sqlx-rt 0.6.2",
|
||||
"syn",
|
||||
"url",
|
||||
]
|
||||
@@ -5665,9 +5641,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-rt"
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7be52fc7c96c136cedea840ed54f7d446ff31ad670c9dea95ebcb998530971a3"
|
||||
checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"tokio",
|
||||
@@ -5846,7 +5822,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5985,18 +5960,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.34"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c1b05ca9d106ba7d2e31a9dab4a64e7be2cce415321966ea3132c49a656e252"
|
||||
checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.34"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e8f2591983642de85c921015f3f070c665a197ed69e417af436115e3a1407487"
|
||||
checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -6063,9 +6038,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.21.0"
|
||||
version = "1.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89797afd69d206ccd11fb0ea560a44bbb87731d020670e79416d442919257d42"
|
||||
checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95"
|
||||
dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
"bytes",
|
||||
@@ -6145,7 +6120,7 @@ dependencies = [
|
||||
"futures-core",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6203,9 +6178,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45"
|
||||
checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
@@ -6227,9 +6202,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tonic"
|
||||
version = "0.8.0"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "498f271adc46acce75d66f639e4d35b31b2394c295c82496727dafa16d465dd2"
|
||||
checksum = "11cd56bdb54ef93935a6a79dbd1d91f1ebd4c64150fd61654031fd6b8b775c91"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
@@ -6249,7 +6224,7 @@ dependencies = [
|
||||
"prost-derive 0.11.0",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
@@ -6285,7 +6260,7 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
"tokio-util 0.7.4",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
@@ -6548,9 +6523,9 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.3"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -6563,15 +6538,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.9.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
||||
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.9"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
@@ -6603,13 +6578,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.2"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
|
||||
checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
]
|
||||
@@ -6697,7 +6671,7 @@ dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if 1.0.0",
|
||||
"chrono",
|
||||
"enum-iterator",
|
||||
"enum-iterator 0.8.1",
|
||||
"getset",
|
||||
"git2",
|
||||
"rustc_version 0.4.0",
|
||||
@@ -6705,6 +6679,23 @@ dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vergen"
|
||||
version = "7.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ba753d713ec3844652ad2cb7eb56bc71e34213a14faddac7852a10ba88f61e"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cfg-if 1.0.0",
|
||||
"enum-iterator 1.1.3",
|
||||
"getset",
|
||||
"git2",
|
||||
"rustc_version 0.4.0",
|
||||
"rustversion",
|
||||
"thiserror",
|
||||
"time 0.3.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version-checker"
|
||||
version = "0.1.0"
|
||||
@@ -6735,9 +6726,7 @@ dependencies = [
|
||||
name = "vesting-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"config",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"mixnet-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
@@ -6971,12 +6960,6 @@ dependencies = [
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wildmatch"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee583bdc5ff1cf9db20e9db5bb3ff4c3089a8f6b8b31aff265c9aba85812db86"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
||||
@@ -2,10 +2,10 @@ test: clippy-all cargo-test wasm fmt
|
||||
test-all: test cargo-test-expensive
|
||||
no-clippy: build cargo-test wasm fmt
|
||||
happy: fmt clippy-happy test
|
||||
clippy-all: clippy-all-main clippy-all-contracts clippy-all-wallet clippy-all-connect
|
||||
clippy-all: clippy-main clippy-coconut clippy-all-contracts clippy-all-wallet clippy-all-connect
|
||||
clippy-happy: clippy-happy-main clippy-happy-contracts clippy-happy-wallet clippy-happy-connect
|
||||
cargo-test: test-main test-contracts test-wallet test-connect
|
||||
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive
|
||||
cargo-test: test-main test-contracts test-wallet test-connect test-coconut
|
||||
cargo-test-expensive: test-main-expensive test-contracts-expensive test-wallet-expensive test-connect-expensive test-coconut-expensive
|
||||
build: build-contracts build-wallet build-main build-connect
|
||||
fmt: fmt-main fmt-contracts fmt-wallet fmt-connect
|
||||
|
||||
@@ -21,8 +21,15 @@ clippy-happy-wallet:
|
||||
clippy-happy-connect:
|
||||
cargo clippy --manifest-path nym-connect/Cargo.toml
|
||||
|
||||
clippy-all-main:
|
||||
cargo clippy --workspace --all-features -- -D warnings
|
||||
clippy-main:
|
||||
cargo clippy --workspace -- -D warnings
|
||||
|
||||
clippy-coconut:
|
||||
cargo clippy --workspace --features coconut -- -D warnings
|
||||
|
||||
clippy-wasm:
|
||||
cargo clippy --workspace --features wasm -- -D warnings
|
||||
|
||||
|
||||
clippy-all-contracts:
|
||||
cargo clippy --workspace --manifest-path contracts/Cargo.toml --all-features --target wasm32-unknown-unknown -- -D warnings
|
||||
@@ -34,10 +41,20 @@ clippy-all-connect:
|
||||
cargo clippy --workspace --manifest-path nym-connect/Cargo.toml --all-features -- -D warnings
|
||||
|
||||
test-main:
|
||||
cargo test --all-features --workspace
|
||||
cargo test --workspace
|
||||
|
||||
test-coconut:
|
||||
cargo test --workspace --features coconut
|
||||
|
||||
test-wasm:
|
||||
cargo test --workspace --features wasm
|
||||
|
||||
|
||||
test-main-expensive:
|
||||
cargo test --all-features --workspace -- --ignored
|
||||
cargo test --workspace -- --ignored
|
||||
|
||||
test-coconut-expensive:
|
||||
cargo test --workspace --features coconut -- --ignored
|
||||
|
||||
test-contracts:
|
||||
cargo test --manifest-path contracts/Cargo.toml --all-features
|
||||
|
||||
@@ -121,16 +121,6 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.gateway_endpoint.gateway_id = id.into();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn with_eth_private_key<S: Into<String>>(&mut self, eth_private_key: S) {
|
||||
self.client.eth_private_key = eth_private_key.into();
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn with_eth_endpoint<S: Into<String>>(&mut self, eth_endpoint: S) {
|
||||
self.client.eth_endpoint = eth_endpoint.into();
|
||||
}
|
||||
|
||||
pub fn set_custom_validator_apis(&mut self, validator_api_urls: Vec<Url>) {
|
||||
self.client.validator_api_urls = validator_api_urls;
|
||||
}
|
||||
@@ -209,16 +199,6 @@ impl<T: NymConfig> Config<T> {
|
||||
self.client.database_path.clone()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn get_eth_endpoint(&self) -> String {
|
||||
self.client.eth_endpoint.clone()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn get_eth_private_key(&self) -> String {
|
||||
self.client.eth_private_key.clone()
|
||||
}
|
||||
|
||||
// Debug getters
|
||||
pub fn get_average_packet_delay(&self) -> Duration {
|
||||
self.debug.average_packet_delay
|
||||
@@ -342,14 +322,6 @@ pub struct Client<T> {
|
||||
/// Path to the database containing bandwidth credentials of this client.
|
||||
database_path: PathBuf,
|
||||
|
||||
/// Ethereum private key.
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_private_key: String,
|
||||
|
||||
/// Address to an Ethereum full node.
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// nym_home_directory specifies absolute path to the home nym Clients directory.
|
||||
/// It is expected to use default value and hence .toml file should not redefine this field.
|
||||
nym_root_directory: PathBuf,
|
||||
@@ -375,10 +347,6 @@ impl<T: NymConfig> Default for Client<T> {
|
||||
reply_encryption_key_store_path: Default::default(),
|
||||
gateway_endpoint: Default::default(),
|
||||
database_path: Default::default(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_private_key: "".to_string(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_endpoint: "".to_string(),
|
||||
nym_root_directory: T::default_root_directory(),
|
||||
super_struct: Default::default(),
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ websocket-requests = { path = "websocket-requests" }
|
||||
|
||||
[features]
|
||||
coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-requests/coconut", "gateway-client/coconut", "client-core/coconut"]
|
||||
eth = []
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0" # for the "textsend" example
|
||||
|
||||
@@ -49,12 +49,6 @@ reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
||||
# Path to the database containing bandwidth credentials
|
||||
database_path = '{{ client.database_path }}'
|
||||
|
||||
# Ethereum private key.
|
||||
eth_private_key = '{{ client.eth_private_key }}'
|
||||
|
||||
# Addess to an Ethereum full node.
|
||||
eth_endpoint = '{{ client.eth_endpoint }}'
|
||||
|
||||
##### additional client config options #####
|
||||
|
||||
# A gateway specific, optional, base58 stringified shared key used for
|
||||
|
||||
@@ -190,8 +190,6 @@ impl NymClient {
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||
.await,
|
||||
self.config.get_base().get_eth_endpoint(),
|
||||
self.config.get_base().get_eth_private_key(),
|
||||
)
|
||||
.expect("Could not create bandwidth controller");
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@ use crate::{
|
||||
commands::{override_config, OverrideConfig},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
use crate::commands::{DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY};
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Init {
|
||||
/// Id of the nym-mixnet-client we want to create config for.
|
||||
@@ -47,27 +44,9 @@ pub(crate) struct Init {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
/// tokens. If you don't want to set this value, use --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_ENDPOINT))
|
||||
)]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead")
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_PRIVATE_KEY))
|
||||
)]
|
||||
eth_private_key: String,
|
||||
}
|
||||
|
||||
impl From<Init> for OverrideConfig {
|
||||
@@ -78,14 +57,8 @@ impl From<Init> for OverrideConfig {
|
||||
port: init_config.port,
|
||||
fastmode: init_config.fastmode,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Some(init_config.eth_private_key),
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Some(init_config.eth_endpoint),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,6 @@
|
||||
use crate::client::config::{Config, SocketType};
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) const DEFAULT_ETH_ENDPOINT: &str =
|
||||
"https://rinkeby.infura.io/v3/00000000000000000000000000000000";
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) const DEFAULT_ETH_PRIVATE_KEY: &str =
|
||||
"0000000000000000000000000000000000000000000000000000000000000001";
|
||||
|
||||
pub(crate) mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
@@ -78,14 +71,8 @@ pub(crate) struct OverrideConfig {
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Option<String>,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: &Cli) {
|
||||
@@ -117,32 +104,13 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
config = config.with_port(port);
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "eth"), not(feature = "coconut")))]
|
||||
{
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT.to_string());
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
|
||||
}
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
{
|
||||
if args.enabled_credentials_mode {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
{
|
||||
if let Some(eth_endpoint) = args.eth_endpoint {
|
||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||
}
|
||||
if let Some(eth_private_key) = args.eth_private_key {
|
||||
config.get_base_mut().with_eth_private_key(eth_private_key);
|
||||
}
|
||||
}
|
||||
|
||||
if args.fastmode {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
}
|
||||
|
||||
@@ -36,21 +36,9 @@ pub(crate) struct Run {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
/// tokens. If you don't want to set this value, use --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_endpoint: Option<String>,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead")
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_private_key: Option<String>,
|
||||
}
|
||||
|
||||
impl From<Run> for OverrideConfig {
|
||||
@@ -60,15 +48,8 @@ impl From<Run> for OverrideConfig {
|
||||
disable_socket: run_config.disable_socket,
|
||||
port: run_config.port,
|
||||
fastmode: false,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: run_config.eth_private_key,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: run_config.eth_endpoint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +49,6 @@ reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
|
||||
# Path to the database containing bandwidth credentials
|
||||
database_path = '{{ client.database_path }}'
|
||||
|
||||
# Ethereum private key.
|
||||
eth_private_key = '{{ client.eth_private_key }}'
|
||||
|
||||
# Addess to an Ethereum full node.
|
||||
eth_endpoint = '{{ client.eth_endpoint }}'
|
||||
|
||||
##### additional client config options #####
|
||||
|
||||
# A gateway specific, optional, base58 stringified shared key used for
|
||||
|
||||
@@ -191,8 +191,6 @@ impl NymClient {
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_base().get_database_path())
|
||||
.await,
|
||||
self.config.get_base().get_eth_endpoint(),
|
||||
self.config.get_base().get_eth_private_key(),
|
||||
)
|
||||
.expect("Could not create bandwidth controller");
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@ use crate::{
|
||||
commands::{override_config, OverrideConfig},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
use crate::commands::{DEFAULT_ETH_ENDPOINT, DEFAULT_ETH_PRIVATE_KEY};
|
||||
|
||||
#[derive(Args, Clone)]
|
||||
pub(crate) struct Init {
|
||||
/// Id of the nym-mixnet-client we want to create config for.
|
||||
@@ -47,27 +44,9 @@ pub(crate) struct Init {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
/// tokens. If you don't want to set this value, use --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_ENDPOINT))
|
||||
)]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead")
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(
|
||||
long,
|
||||
default_value_if("enabled-credentials-mode", None, Some(DEFAULT_ETH_PRIVATE_KEY))
|
||||
)]
|
||||
eth_private_key: String,
|
||||
}
|
||||
|
||||
impl From<Init> for OverrideConfig {
|
||||
@@ -76,15 +55,8 @@ impl From<Init> for OverrideConfig {
|
||||
validators: init_config.validators,
|
||||
port: init_config.port,
|
||||
fastmode: init_config.fastmode,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Some(init_config.eth_private_key),
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Some(init_config.eth_endpoint),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,6 @@ pub mod init;
|
||||
pub(crate) mod run;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) const DEFAULT_ETH_ENDPOINT: &str =
|
||||
"https://rinkeby.infura.io/v3/00000000000000000000000000000000";
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) const DEFAULT_ETH_PRIVATE_KEY: &str =
|
||||
"0000000000000000000000000000000000000000000000000000000000000001";
|
||||
|
||||
fn long_version() -> String {
|
||||
format!(
|
||||
r#"
|
||||
@@ -78,14 +71,8 @@ pub(crate) struct OverrideConfig {
|
||||
port: Option<u16>,
|
||||
fastmode: bool,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: Option<String>,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: &Cli) {
|
||||
@@ -111,31 +98,12 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
config = config.with_port(port);
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "eth"), not(feature = "coconut")))]
|
||||
{
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT.to_string());
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY.to_string());
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
{
|
||||
if args.enabled_credentials_mode {
|
||||
config.get_base_mut().with_disabled_credentials(false)
|
||||
}
|
||||
}
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
{
|
||||
if let Some(eth_endpoint) = args.eth_endpoint {
|
||||
config.get_base_mut().with_eth_endpoint(eth_endpoint);
|
||||
}
|
||||
if let Some(eth_private_key) = args.eth_private_key {
|
||||
config.get_base_mut().with_eth_private_key(eth_private_key);
|
||||
}
|
||||
}
|
||||
|
||||
if args.fastmode {
|
||||
config.get_base_mut().set_high_default_traffic_volume();
|
||||
|
||||
@@ -40,21 +40,9 @@ pub(crate) struct Run {
|
||||
|
||||
/// Set this client to work in a enabled credentials mode that would attempt to use gateway
|
||||
/// with bandwidth credential requirement.
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: bool,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20
|
||||
/// tokens. If you don't want to set this value, use --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_endpoint: Option<String>,
|
||||
|
||||
/// Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens. If you don't
|
||||
/// want to set this value, use --enabled-credentials-mode instead
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_private_key: Option<String>,
|
||||
}
|
||||
|
||||
impl From<Run> for OverrideConfig {
|
||||
@@ -64,14 +52,8 @@ impl From<Run> for OverrideConfig {
|
||||
port: run_config.port,
|
||||
fastmode: false,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_private_key: run_config.eth_private_key,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: run_config.eth_endpoint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,12 @@ edition = "2021"
|
||||
# TODO: (for this and other crates), similarly to 'tokio', import only required "futures" modules rather than
|
||||
# the entire crate
|
||||
futures = "0.3"
|
||||
json = "0.12.4"
|
||||
log = "0.4"
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||
secp256k1 = "0.20.3"
|
||||
web3 = { version = "0.17.0", default-features = false }
|
||||
web3 = { version = "0.17.0", default-features = false, optional = true }
|
||||
async-trait = { version = "0.1.51" }
|
||||
|
||||
# internal
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::GatewayClientError;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use crate::wasm_storage::Storage;
|
||||
@@ -15,6 +14,8 @@ use crate::wasm_storage::StorageError;
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "coconut"))]
|
||||
use credential_storage::error::StorageError;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
use std::str::FromStr;
|
||||
#[cfg(feature = "coconut")]
|
||||
use {
|
||||
coconut_interface::Base58,
|
||||
@@ -23,65 +24,12 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use {
|
||||
credentials::token::bandwidth::TokenCredential,
|
||||
crypto::asymmetric::identity,
|
||||
network_defaults::{
|
||||
eth_contract::ETH_ERC20_JSON_ABI, eth_contract::ETH_JSON_ABI, BANDWIDTH_VALUE,
|
||||
ETH_BURN_FUNCTION_NAME, ETH_CONTRACT_ADDRESS, ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
ETH_ERC20_CONTRACT_ADDRESS, ETH_MIN_BLOCK_DEPTH, TOKENS_TO_BURN, UTOKENS_TO_BURN,
|
||||
},
|
||||
pemstore::traits::PemStorableKeyPair,
|
||||
rand::rngs::OsRng,
|
||||
secp256k1::SecretKey,
|
||||
web3::{
|
||||
contract::{Contract, Options},
|
||||
ethabi::Token,
|
||||
signing::{Key, SecretKeyRef},
|
||||
transports::Http,
|
||||
types::{Address, U256, U64},
|
||||
Web3,
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn eth_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
Contract::from_json(
|
||||
web3.eth(),
|
||||
Address::from(ETH_CONTRACT_ADDRESS),
|
||||
json::parse(ETH_JSON_ABI)
|
||||
.expect("Invalid json abi")
|
||||
.dump()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect("Invalid json abi")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn eth_erc20_contract(web3: Web3<Http>) -> Contract<Http> {
|
||||
Contract::from_json(
|
||||
web3.eth(),
|
||||
Address::from(ETH_ERC20_CONTRACT_ADDRESS),
|
||||
json::parse(ETH_ERC20_JSON_ABI)
|
||||
.expect("Invalid json abi")
|
||||
.dump()
|
||||
.as_bytes(),
|
||||
)
|
||||
.expect("Invalid json abi")
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BandwidthController<St: Storage> {
|
||||
#[allow(dead_code)]
|
||||
storage: St,
|
||||
#[cfg(feature = "coconut")]
|
||||
validator_endpoints: Vec<url::Url>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
contract: Contract<Http>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_contract: Contract<Http>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_private_key: SecretKey,
|
||||
}
|
||||
|
||||
impl<St> BandwidthController<St>
|
||||
@@ -97,60 +45,8 @@ where
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn new(
|
||||
storage: St,
|
||||
eth_endpoint: String,
|
||||
eth_private_key: String,
|
||||
) -> Result<Self, GatewayClientError> {
|
||||
// Fail early, on invalid url
|
||||
let transport =
|
||||
Http::new(ð_endpoint).map_err(|_| GatewayClientError::InvalidURL(eth_endpoint))?;
|
||||
let web3 = web3::Web3::new(transport);
|
||||
// Fail early, on invalid abi
|
||||
let contract = eth_contract(web3.clone());
|
||||
let erc20_contract = eth_erc20_contract(web3);
|
||||
let eth_private_key = secp256k1::SecretKey::from_str(ð_private_key)
|
||||
.map_err(|_| GatewayClientError::InvalidEthereumPrivateKey)?;
|
||||
|
||||
Ok(BandwidthController {
|
||||
storage,
|
||||
contract,
|
||||
erc20_contract,
|
||||
eth_private_key,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
async fn backup_keypair(&self, keypair: &identity::KeyPair) -> Result<(), GatewayClientError> {
|
||||
self.storage
|
||||
.insert_erc20_credential(
|
||||
keypair.public_key().to_base58_string(),
|
||||
keypair.private_key().to_base58_string(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[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();
|
||||
let private_key = identity::PrivateKey::from_base58_string(data.private_key).unwrap();
|
||||
|
||||
Ok(identity::KeyPair::from_keys(private_key, public_key))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
async fn mark_keypair_as_spent(
|
||||
&self,
|
||||
keypair: &identity::KeyPair,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
self.storage
|
||||
.consume_erc20_credential(keypair.public_key().to_base58_string())
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
pub fn new(storage: St) -> Result<Self, GatewayClientError> {
|
||||
Ok(BandwidthController { storage })
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
@@ -179,206 +75,4 @@ where
|
||||
&verification_key,
|
||||
)?)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub async fn prepare_token_credential(
|
||||
&self,
|
||||
gateway_identity: identity::PublicKey,
|
||||
gateway_owner: String,
|
||||
) -> Result<TokenCredential, GatewayClientError> {
|
||||
let kp = match self.restore_keypair().await {
|
||||
Ok(kp) => kp,
|
||||
Err(_) => {
|
||||
let mut rng = OsRng;
|
||||
let kp = identity::KeyPair::new(&mut rng);
|
||||
self.backup_keypair(&kp).await?;
|
||||
kp
|
||||
}
|
||||
};
|
||||
|
||||
let verification_key = *kp.public_key();
|
||||
let signed_verification_key = kp.private_key().sign(&verification_key.to_bytes());
|
||||
self.buy_token_credential(verification_key, signed_verification_key, gateway_owner)
|
||||
.await?;
|
||||
|
||||
self.mark_keypair_as_spent(&kp).await?;
|
||||
|
||||
let message: Vec<u8> = verification_key
|
||||
.to_bytes()
|
||||
.iter()
|
||||
.chain(gateway_identity.to_bytes().iter())
|
||||
.copied()
|
||||
.collect();
|
||||
let signature = kp.private_key().sign(&message);
|
||||
|
||||
Ok(TokenCredential::new(
|
||||
verification_key,
|
||||
gateway_identity,
|
||||
BANDWIDTH_VALUE,
|
||||
signature,
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub async fn buy_token_credential(
|
||||
&self,
|
||||
verification_key: identity::PublicKey,
|
||||
signed_verification_key: identity::Signature,
|
||||
gateway_owner: String,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
let confirmations = if cfg!(debug_assertions) {
|
||||
1
|
||||
} else {
|
||||
ETH_MIN_BLOCK_DEPTH
|
||||
};
|
||||
// 15 seconds per confirmation block + 10 seconds of network overhead + 20 seconds of wait for kill
|
||||
log::info!(
|
||||
"Waiting for Ethereum transaction. This should take about {} seconds",
|
||||
(confirmations + 1) * 15 + 30
|
||||
);
|
||||
let mut options = Options::default();
|
||||
let estimation = self
|
||||
.erc20_contract
|
||||
.estimate_gas(
|
||||
ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
(
|
||||
Token::Address(Address::from(ETH_CONTRACT_ADDRESS)),
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
),
|
||||
SecretKeyRef::from(&self.eth_private_key).address(),
|
||||
options.clone(),
|
||||
)
|
||||
.await?;
|
||||
options.gas = Some(estimation);
|
||||
log::info!("Calling ERC20 approve in 10 seconds with an estimated gas of {}. Kill the process if you want to abort", estimation);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if let Err(err) = fluvio_wasm_timer::Delay::new(std::time::Duration::from_secs(10)).await {
|
||||
log::error!(
|
||||
"the timer has gone away while waiting for possible kill! - {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
let recipt = self
|
||||
.erc20_contract
|
||||
.signed_call_with_confirmations(
|
||||
ETH_ERC20_APPROVE_FUNCTION_NAME,
|
||||
(
|
||||
Token::Address(Address::from(ETH_CONTRACT_ADDRESS)),
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
),
|
||||
options,
|
||||
1, // One confirmation is enough, as we'll be consuming the approved token next anyway
|
||||
&self.eth_private_key,
|
||||
)
|
||||
.await?;
|
||||
if Some(U64::from(0u64)) == recipt.status {
|
||||
return Err(GatewayClientError::BurnTokenError(
|
||||
web3::Error::InvalidResponse(format!(
|
||||
"Approve transaction status is 0 (failure): {:?}",
|
||||
recipt.logs,
|
||||
)),
|
||||
));
|
||||
} else {
|
||||
log::info!(
|
||||
"Approved {} tokens for bandwidth use on Ethereum",
|
||||
TOKENS_TO_BURN
|
||||
);
|
||||
}
|
||||
|
||||
let mut options = Options::default();
|
||||
let estimation = self
|
||||
.contract
|
||||
.estimate_gas(
|
||||
ETH_BURN_FUNCTION_NAME,
|
||||
(
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
Token::Uint(U256::from(&verification_key.to_bytes())),
|
||||
Token::Bytes(signed_verification_key.to_bytes().to_vec()),
|
||||
Token::String(gateway_owner.clone()),
|
||||
),
|
||||
SecretKeyRef::from(&self.eth_private_key).address(),
|
||||
options.clone(),
|
||||
)
|
||||
.await?;
|
||||
options.gas = Some(estimation);
|
||||
log::info!("Generating bandwidth on ETH contract in 10 seconds with an estimated gas of {}. \
|
||||
Kill the process if you want to abort. Keep in mind that if you abort now, you'll still have \
|
||||
some tokens approved for bandwidth spending from the previous action. \
|
||||
If you don't want that, you'll need to manually decreaseAllowance to revert the approval.", estimation);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if let Err(err) = fluvio_wasm_timer::Delay::new(std::time::Duration::from_secs(10)).await {
|
||||
log::error!(
|
||||
"the timer has gone away while waiting for possible kill! - {}",
|
||||
err
|
||||
);
|
||||
}
|
||||
let recipt = self
|
||||
.contract
|
||||
.signed_call_with_confirmations(
|
||||
ETH_BURN_FUNCTION_NAME,
|
||||
(
|
||||
Token::Uint(U256::from(UTOKENS_TO_BURN)),
|
||||
Token::Uint(U256::from(&verification_key.to_bytes())),
|
||||
Token::Bytes(signed_verification_key.to_bytes().to_vec()),
|
||||
Token::String(gateway_owner),
|
||||
),
|
||||
options,
|
||||
confirmations,
|
||||
&self.eth_private_key,
|
||||
)
|
||||
.await?;
|
||||
if Some(U64::from(0u64)) == recipt.status {
|
||||
Err(GatewayClientError::BurnTokenError(
|
||||
web3::Error::InvalidResponse(format!(
|
||||
"Transaction status is 0 (failure): {:?}",
|
||||
recipt.logs,
|
||||
)),
|
||||
))
|
||||
} else {
|
||||
log::info!(
|
||||
"Bought bandwidth on Ethereum: {} MB",
|
||||
BANDWIDTH_VALUE / 1024 / 1024
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use network_defaults::ETH_EVENT_NAME;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_contract() {
|
||||
let transport =
|
||||
Http::new("https://rinkeby.infura.io/v3/00000000000000000000000000000000").unwrap();
|
||||
let web3 = web3::Web3::new(transport);
|
||||
// test no panic occurs
|
||||
eth_contract(web3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_erc20_contract() {
|
||||
let transport =
|
||||
Http::new("https://rinkeby.infura.io/v3/00000000000000000000000000000000").unwrap();
|
||||
let web3 = web3::Web3::new(transport);
|
||||
// test no panic occurs
|
||||
eth_erc20_contract(web3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_event_name_constant_against_abi() {
|
||||
let transport =
|
||||
Http::new("https://rinkeby.infura.io/v3/00000000000000000000000000000000").unwrap();
|
||||
let web3 = web3::Web3::new(transport);
|
||||
let contract = eth_contract(web3);
|
||||
assert!(contract.abi().event(ETH_EVENT_NAME).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ use crate::wasm_storage::PersistentStorage;
|
||||
use coconut_interface::Credential;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use credential_storage::PersistentStorage;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use credentials::token::bandwidth::TokenCredential;
|
||||
use crypto::asymmetric::identity;
|
||||
use futures::{FutureExt, SinkExt, StreamExt};
|
||||
use gateway_requests::authentication::encrypted_address::EncryptedAddressBytes;
|
||||
@@ -554,30 +552,6 @@ impl GatewayClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
async fn claim_token_bandwidth(
|
||||
&mut self,
|
||||
credential: TokenCredential,
|
||||
) -> Result<(), GatewayClientError> {
|
||||
let mut rng = OsRng;
|
||||
|
||||
let iv = IV::new_random(&mut rng);
|
||||
|
||||
let msg = ClientControlRequest::new_enc_token_bandwidth_credential(
|
||||
&credential,
|
||||
self.shared_key.as_ref().unwrap(),
|
||||
iv,
|
||||
)
|
||||
.into();
|
||||
self.bandwidth_remaining = match self.send_websocket_message(msg).await? {
|
||||
ServerResponse::Bandwidth { available_total } => Ok(available_total),
|
||||
ServerResponse::Error { message } => Err(GatewayClientError::GatewayError(message)),
|
||||
_ => Err(GatewayClientError::UnexpectedResponse),
|
||||
}?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn try_claim_testnet_bandwidth(&mut self) -> Result<(), GatewayClientError> {
|
||||
let msg = ClientControlRequest::ClaimFreeTestnetBandwidth.into();
|
||||
self.bandwidth_remaining = match self.send_websocket_message(msg).await? {
|
||||
@@ -616,17 +590,10 @@ impl GatewayClient {
|
||||
.prepare_coconut_credential()
|
||||
.await?;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let credential = self
|
||||
.bandwidth_controller
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.prepare_token_credential(self.gateway_identity, _gateway_owner)
|
||||
.await?;
|
||||
return self.try_claim_testnet_bandwidth().await;
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
return self.claim_coconut_bandwidth(credential).await;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
return self.claim_token_bandwidth(credential).await;
|
||||
}
|
||||
|
||||
fn estimate_required_bandwidth(&self, packets: &[MixPacket]) -> i64 {
|
||||
|
||||
@@ -26,13 +26,6 @@ pub struct CoconutCredential {
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
pub struct ERC20Credential {
|
||||
pub id: i64,
|
||||
pub public_key: String,
|
||||
pub private_key: String,
|
||||
pub consumed: bool,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait Storage: Send + Sync {
|
||||
async fn insert_coconut_credential(
|
||||
@@ -47,16 +40,6 @@ pub trait Storage: Send + Sync {
|
||||
async fn get_next_coconut_credential(&self) -> Result<CoconutCredential, StorageError>;
|
||||
|
||||
async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
|
||||
|
||||
async fn insert_erc20_credential(
|
||||
&self,
|
||||
public_key: String,
|
||||
private_key: String,
|
||||
) -> Result<(), StorageError>;
|
||||
|
||||
async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, StorageError>;
|
||||
|
||||
async fn consume_erc20_credential(&self, public_key: String) -> Result<(), StorageError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -79,20 +62,4 @@ impl Storage for PersistentStorage {
|
||||
async fn remove_coconut_credential(&self, _id: i64) -> Result<(), StorageError> {
|
||||
Err(StorageError::WasmNotSupported)
|
||||
}
|
||||
|
||||
async fn insert_erc20_credential(
|
||||
&self,
|
||||
_public_key: String,
|
||||
_private_key: String,
|
||||
) -> Result<(), StorageError> {
|
||||
Err(StorageError::WasmNotSupported)
|
||||
}
|
||||
|
||||
async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, StorageError> {
|
||||
Err(StorageError::WasmNotSupported)
|
||||
}
|
||||
|
||||
async fn consume_erc20_credential(&self, _public_key: String) -> Result<(), StorageError> {
|
||||
Err(StorageError::WasmNotSupported)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ edition = "2021"
|
||||
[dependencies]
|
||||
cfg-if = "1.0.0"
|
||||
handlebars = "3.0.1"
|
||||
humantime-serde = "1.0"
|
||||
log = "0.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = "0.5.6"
|
||||
|
||||
@@ -18,7 +18,7 @@ fixed = { version = "1.1", features = ["serde"] }
|
||||
az = "1.1"
|
||||
log = "0.4.14"
|
||||
time = { version = "0.3.6", features = ["parsing", "formatting"] }
|
||||
ts-rs = "6.1.2"
|
||||
ts-rs = {version = "6.1.2", optional = true}
|
||||
|
||||
contracts-common = { path = "../contracts-common" }
|
||||
|
||||
@@ -27,4 +27,4 @@ time = { version = "0.3.5", features = ["serde", "macros"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
generate-ts = []
|
||||
generate-ts = ['ts-rs']
|
||||
|
||||
@@ -3,16 +3,12 @@ name = "vesting-contract-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = "1.0.0"
|
||||
mixnet-contract-common = { path = "../mixnet-contract" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
schemars = "0.8"
|
||||
cw-storage-plus = "0.13.4"
|
||||
config = { path = "../../config" }
|
||||
ts-rs = "6.1.2"
|
||||
ts-rs = {version = "6.1.2", optional = true}
|
||||
|
||||
[features]
|
||||
generate-ts = []
|
||||
generate-ts = ["ts-rs"]
|
||||
|
||||
@@ -7,7 +7,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
async-trait = { version = "0.1.51" }
|
||||
nymcoconut = { path = "../nymcoconut" }
|
||||
|
||||
log = "0.4"
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]}
|
||||
|
||||
@@ -17,55 +17,4 @@ impl ERC20CredentialManager {
|
||||
pub(crate) fn new(connection_pool: sqlx::SqlitePool) -> Self {
|
||||
ERC20CredentialManager { connection_pool }
|
||||
}
|
||||
|
||||
/// Inserts provided signature into the database.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `public_key`: Base58 representation of a public key.
|
||||
/// * `private_key`: Base58 representation of a private key.
|
||||
pub(crate) async fn insert_erc20_credential(
|
||||
&self,
|
||||
public_key: String,
|
||||
private_key: String,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO erc20_credentials(public_key, private_key, consumed) VALUES (?, ?, ?)",
|
||||
public_key,
|
||||
private_key,
|
||||
false,
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tries to retrieve one of the stored, unused credentials.
|
||||
pub(crate) async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
ERC20Credential,
|
||||
"SELECT * FROM erc20_credentials WHERE consumed = false"
|
||||
)
|
||||
.fetch_one(&self.connection_pool)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Mark a credential as being consumed.
|
||||
pub(crate) async fn consume_erc20_credential(
|
||||
&self,
|
||||
public_key: String,
|
||||
) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
UPDATE erc20_credentials
|
||||
SET consumed = true
|
||||
WHERE public_key = ?
|
||||
"#,
|
||||
public_key
|
||||
)
|
||||
.execute(&self.connection_pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,18 +4,16 @@
|
||||
*/
|
||||
|
||||
use crate::coconut::CoconutCredentialManager;
|
||||
use crate::erc20::ERC20CredentialManager;
|
||||
use crate::error::StorageError;
|
||||
use crate::storage::Storage;
|
||||
|
||||
use crate::models::{CoconutCredential, ERC20Credential};
|
||||
use crate::models::CoconutCredential;
|
||||
use async_trait::async_trait;
|
||||
use log::{debug, error};
|
||||
use sqlx::ConnectOptions;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
mod coconut;
|
||||
mod erc20;
|
||||
pub mod error;
|
||||
mod models;
|
||||
pub mod storage;
|
||||
@@ -24,7 +22,6 @@ pub mod storage;
|
||||
#[derive(Clone)]
|
||||
pub struct PersistentStorage {
|
||||
coconut_credential_manager: CoconutCredentialManager,
|
||||
erc20_credential_manager: ERC20CredentialManager,
|
||||
}
|
||||
|
||||
impl PersistentStorage {
|
||||
@@ -60,7 +57,6 @@ impl PersistentStorage {
|
||||
|
||||
Ok(PersistentStorage {
|
||||
coconut_credential_manager: CoconutCredentialManager::new(connection_pool.clone()),
|
||||
erc20_credential_manager: ERC20CredentialManager::new(connection_pool),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -104,36 +100,6 @@ impl Storage for PersistentStorage {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn insert_erc20_credential(
|
||||
&self,
|
||||
public_key: String,
|
||||
private_key: String,
|
||||
) -> Result<(), StorageError> {
|
||||
self.erc20_credential_manager
|
||||
.insert_erc20_credential(public_key, private_key)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, StorageError> {
|
||||
let credential = self
|
||||
.erc20_credential_manager
|
||||
.get_next_erc20_credential()
|
||||
.await?;
|
||||
|
||||
Ok(credential)
|
||||
}
|
||||
|
||||
async fn consume_erc20_credential(&self, public_key: String) -> Result<(), StorageError> {
|
||||
let credential = self
|
||||
.erc20_credential_manager
|
||||
.consume_erc20_credential(public_key)
|
||||
.await?;
|
||||
|
||||
Ok(credential)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn initialise_storage(path: PathBuf) -> PersistentStorage {
|
||||
|
||||
@@ -10,11 +10,3 @@ pub struct CoconutCredential {
|
||||
pub binding_number: String,
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
pub struct ERC20Credential {
|
||||
#[allow(dead_code)]
|
||||
pub id: i64,
|
||||
pub public_key: String,
|
||||
pub private_key: String,
|
||||
pub consumed: bool,
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::models::{CoconutCredential, ERC20Credential};
|
||||
use crate::models::CoconutCredential;
|
||||
use crate::StorageError;
|
||||
|
||||
#[async_trait]
|
||||
@@ -31,22 +31,4 @@ pub trait Storage: Send + Sync {
|
||||
///
|
||||
/// * `signature`: Coconut credential in the form of a signature.
|
||||
async fn remove_coconut_credential(&self, id: i64) -> Result<(), StorageError>;
|
||||
|
||||
/// Inserts provided signature into the database.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `public_key`: Base58 representation of a public key.
|
||||
/// * `private_key`: Base58 representation of a private key.
|
||||
async fn insert_erc20_credential(
|
||||
&self,
|
||||
public_key: String,
|
||||
private_key: String,
|
||||
) -> Result<(), StorageError>;
|
||||
|
||||
/// Tries to retrieve one of the stored, unused credential data.
|
||||
async fn get_next_erc20_credential(&self) -> Result<ERC20Credential, StorageError>;
|
||||
|
||||
/// Mark a credential as being consumed.
|
||||
async fn consume_erc20_credential(&self, public_key: String) -> Result<(), StorageError>;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
|
||||
# internal
|
||||
nymsphinx-types = { path = "../nymsphinx/types" }
|
||||
pemstore = { path = "../../common/pemstore" }
|
||||
config = { path="../../common/config" }
|
||||
|
||||
[dev-dependencies]
|
||||
rand_chacha = "0.2"
|
||||
|
||||
@@ -8,7 +8,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.0"
|
||||
dashmap = "4.0"
|
||||
futures = "0.3"
|
||||
humantime-serde = "1.0"
|
||||
log = "0.4"
|
||||
@@ -19,7 +18,6 @@ tokio-util = { version = "0.7.3", features = ["codec"] }
|
||||
url = "2.2"
|
||||
|
||||
crypto = { path = "../crypto" }
|
||||
nonexhaustive-delayqueue = { path = "../nonexhaustive-delayqueue" }
|
||||
nymsphinx-acknowledgements = { path = "../nymsphinx/acknowledgements" }
|
||||
nymsphinx-addressing = { path = "../nymsphinx/addressing" }
|
||||
nymsphinx-forwarding = { path = "../nymsphinx/forwarding" }
|
||||
|
||||
@@ -7,7 +7,6 @@ use std::{env::var, path::PathBuf};
|
||||
use url::Url;
|
||||
|
||||
pub mod all;
|
||||
pub mod eth_contract;
|
||||
pub mod mainnet;
|
||||
pub mod qa;
|
||||
pub mod sandbox;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
Ordered Buffer
|
||||
==============
|
||||
|
||||
**CURRENTLY UNUSED**
|
||||
|
||||
This crate takes care of reliably and speedily turning bytes into a series of ordered message fragments on one side, and of reliably reassembling the fragments into the original message on the other.
|
||||
|
||||
|
||||
@@ -8,5 +8,4 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
nymsphinx-addressing = { path = "../../../common/nymsphinx/addressing" }
|
||||
ordered-buffer = {path = "../ordered-buffer"}
|
||||
thiserror = "1"
|
||||
|
||||
@@ -8,7 +8,6 @@ edition = "2021"
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal"] }
|
||||
tokio-util = { version = "0.7.3", features = ["codec"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.19.1", features = ["rt-multi-thread", "net", "signal", "test-util", "macros"] }
|
||||
|
||||
Generated
+2
-83
@@ -2,12 +2,6 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "Inflector"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.7.5"
|
||||
@@ -45,18 +39,6 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f771a5d1f5503f7f4279a30f3643d3421ba149848b89ecaaec0ea2acf04a5ac4"
|
||||
|
||||
[[package]]
|
||||
name = "bandwidth-claim"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"bandwidth-claim-contract",
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bandwidth-claim-contract"
|
||||
version = "0.1.0"
|
||||
@@ -267,7 +249,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"handlebars",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"serde",
|
||||
@@ -367,7 +348,6 @@ name = "crypto"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bs58",
|
||||
"config",
|
||||
"ed25519-dalek",
|
||||
"nymsphinx-types",
|
||||
"pemstore",
|
||||
@@ -894,22 +874,6 @@ dependencies = [
|
||||
"digest 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "humantime-serde"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac34a56cfd4acddb469cc7fff187ed5ac36f498ba085caf8bbc725e3ff474058"
|
||||
dependencies = [
|
||||
"humantime",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.3"
|
||||
@@ -1050,7 +1014,7 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||
|
||||
[[package]]
|
||||
name = "mixnet-contract"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"az",
|
||||
"bs58",
|
||||
@@ -1087,7 +1051,6 @@ dependencies = [
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"time 0.3.6",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1660,15 +1623,6 @@ dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
@@ -1741,29 +1695,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ts-rs"
|
||||
version = "6.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc59f479df54269b400dd95bc3b7e81623b3e4b9c70c8ca7125ab8341eafa64e"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
"ts-rs-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ts-rs-macros"
|
||||
version = "6.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f807fdb3151fee75df7485b901a89624358cd07a67a8fb1a5831bf5a07681ff"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
@@ -1852,7 +1783,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vesting-contract"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
@@ -1867,13 +1798,10 @@ dependencies = [
|
||||
name = "vesting-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"config",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"mixnet-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1958,15 +1886,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["bandwidth-claim", "coconut-bandwidth", "mixnet", "vesting", "multisig/cw3-flex-multisig", "multisig/cw4-group", "coconut-test"]
|
||||
members = ["coconut-bandwidth", "mixnet", "vesting", "multisig/cw3-flex-multisig", "multisig/cw4-group", "coconut-test"]
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
[package]
|
||||
name = "bandwidth-claim"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dev-dependencies]
|
||||
[dependencies]
|
||||
bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" }
|
||||
|
||||
cosmwasm-std = "1.0.0"
|
||||
cosmwasm-storage = "1.0.0"
|
||||
|
||||
schemars = "0.8"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
thiserror = "1.0.23"
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{StdError, VerificationError};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Custom errors for contract failure conditions.
|
||||
///
|
||||
/// Add any other custom errors you like here.
|
||||
/// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details.
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
pub enum ContractError {
|
||||
#[error("{0}")]
|
||||
Std(#[from] StdError),
|
||||
|
||||
#[error("Invalid size for signature items")]
|
||||
InvalidSignatureSize,
|
||||
|
||||
#[error("This payment has already been claimed by someone")]
|
||||
PaymentAlreadyClaimed,
|
||||
|
||||
#[error("Error while verifying ed25519 signature - {0}")]
|
||||
VerificationError(#[from] VerificationError),
|
||||
|
||||
#[error("The payment is not properly signed")]
|
||||
BadSignature,
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod error;
|
||||
mod queries;
|
||||
mod storage;
|
||||
mod support;
|
||||
mod transactions;
|
||||
|
||||
use cosmwasm_std::{
|
||||
entry_point, to_binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response,
|
||||
};
|
||||
|
||||
use crate::error::ContractError;
|
||||
use bandwidth_claim_contract::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg};
|
||||
|
||||
/// Instantiate the contract.
|
||||
///
|
||||
/// `deps` contains Storage, API and Querier
|
||||
/// `env` contains block, message and contract info
|
||||
/// `msg` is the contract initialization message, sort of like a constructor call.
|
||||
#[entry_point]
|
||||
pub fn instantiate(
|
||||
_deps: DepsMut<'_>,
|
||||
_env: Env,
|
||||
_info: MessageInfo,
|
||||
_msg: InstantiateMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
/// Handle an incoming message
|
||||
#[entry_point]
|
||||
pub fn execute(
|
||||
deps: DepsMut<'_>,
|
||||
env: Env,
|
||||
info: MessageInfo,
|
||||
msg: ExecuteMsg,
|
||||
) -> Result<Response, ContractError> {
|
||||
match msg {
|
||||
ExecuteMsg::LinkPayment { data } => transactions::link_payment(deps, env, info, data),
|
||||
}
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn query(deps: Deps<'_>, _env: Env, msg: QueryMsg) -> Result<QueryResponse, ContractError> {
|
||||
let query_res = match msg {
|
||||
QueryMsg::GetPayments { start_after, limit } => {
|
||||
to_binary(&queries::query_payments_paged(deps, start_after, limit)?)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(query_res?)
|
||||
}
|
||||
|
||||
#[entry_point]
|
||||
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
|
||||
Ok(Default::default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use bandwidth_claim_contract::payment::PagedPaymentResponse;
|
||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
|
||||
use cosmwasm_std::{coins, from_binary};
|
||||
|
||||
const TEST_MIX_DENOM: &str = "unym";
|
||||
|
||||
#[test]
|
||||
fn initialize_contract() {
|
||||
let mut deps = mock_dependencies();
|
||||
let env = mock_env();
|
||||
let msg = InstantiateMsg {};
|
||||
let info = mock_info("creator", &[]);
|
||||
|
||||
let res = instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
|
||||
assert_eq!(0, res.messages.len());
|
||||
|
||||
// payments should be empty after initialization
|
||||
let res = query(
|
||||
deps.as_ref(),
|
||||
env.clone(),
|
||||
QueryMsg::GetPayments {
|
||||
start_after: None,
|
||||
limit: Option::from(2),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let page: PagedPaymentResponse = from_binary(&res).unwrap();
|
||||
assert_eq!(0, page.payments.len()); // there are no payments in the list when it's just been initialized
|
||||
|
||||
// Contract balance should match what we initialized it as
|
||||
assert_eq!(
|
||||
coins(0, TEST_MIX_DENOM),
|
||||
vec![deps
|
||||
.as_ref()
|
||||
.querier
|
||||
.query_balance(env.contract.address, TEST_MIX_DENOM)
|
||||
.unwrap()]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{Deps, Order, StdResult};
|
||||
|
||||
use crate::storage::payments_read;
|
||||
use bandwidth_claim_contract::keys::PublicKey;
|
||||
use bandwidth_claim_contract::payment::{PagedPaymentResponse, Payment};
|
||||
|
||||
const PAYMENT_PAGE_MAX_LIMIT: u32 = 100;
|
||||
const PAYMENT_PAGE_DEFAULT_LIMIT: u32 = 50;
|
||||
|
||||
/// Adds a 0 byte to terminate the `start_after` value given. This allows CosmWasm
|
||||
/// to get the succeeding key as the start of the next page.
|
||||
fn calculate_start_value<B: AsRef<[u8]>>(start_after: Option<B>) -> Option<Vec<u8>> {
|
||||
start_after.as_ref().map(|identity| {
|
||||
identity
|
||||
.as_ref()
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(std::iter::once(0))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn query_payments_paged(
|
||||
deps: Deps<'_>,
|
||||
start_after: Option<PublicKey>,
|
||||
limit: Option<u32>,
|
||||
) -> StdResult<PagedPaymentResponse> {
|
||||
let limit = limit
|
||||
.unwrap_or(PAYMENT_PAGE_DEFAULT_LIMIT)
|
||||
.min(PAYMENT_PAGE_MAX_LIMIT) as usize;
|
||||
let start = calculate_start_value(start_after);
|
||||
|
||||
let payments = payments_read(deps.storage)
|
||||
.range(start.as_deref(), None, Order::Ascending)
|
||||
.take(limit)
|
||||
.map(|res| res.map(|item| item.1))
|
||||
.collect::<StdResult<Vec<Payment>>>()?;
|
||||
|
||||
let start_next_after = payments.last().map(|payment| payment.verification_key());
|
||||
|
||||
Ok(PagedPaymentResponse::new(payments, limit, start_next_after))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::storage::payments;
|
||||
use crate::support::tests::helpers;
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[test]
|
||||
fn payments_empty_on_init() {
|
||||
let deps = helpers::init_contract();
|
||||
let response = query_payments_paged(deps.as_ref(), None, Option::from(2)).unwrap();
|
||||
assert_eq!(0, response.payments.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payments_paged_retrieval_obeys_limits() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let storage = deps.as_mut().storage;
|
||||
let limit = 2;
|
||||
for n in 0u32..10000 {
|
||||
let bytes: Vec<u8> = std::iter::repeat(n.to_be_bytes())
|
||||
.take(8)
|
||||
.flatten()
|
||||
.collect();
|
||||
let verification_key = PublicKey::new(bytes.try_into().unwrap());
|
||||
let payment = helpers::payment_fixture();
|
||||
payments(storage)
|
||||
.save(&verification_key.to_bytes(), &payment)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let page1 = query_payments_paged(deps.as_ref(), None, Option::from(limit)).unwrap();
|
||||
assert_eq!(limit, page1.payments.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payments_paged_retrieval_has_default_limit() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let storage = deps.as_mut().storage;
|
||||
for n in 0u32..100 {
|
||||
let bytes: Vec<u8> = std::iter::repeat(n.to_be_bytes())
|
||||
.take(8)
|
||||
.flatten()
|
||||
.collect();
|
||||
let verification_key = PublicKey::new(bytes.try_into().unwrap());
|
||||
let payment = helpers::payment_fixture();
|
||||
payments(storage)
|
||||
.save(&verification_key.to_bytes(), &payment)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// query without explicitly setting a limit
|
||||
let page1 = query_payments_paged(deps.as_ref(), None, None).unwrap();
|
||||
|
||||
assert_eq!(PAYMENT_PAGE_DEFAULT_LIMIT, page1.payments.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payments_paged_retrieval_has_max_limit() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let storage = deps.as_mut().storage;
|
||||
for n in 0u32..10000 {
|
||||
let bytes: Vec<u8> = std::iter::repeat(n.to_be_bytes())
|
||||
.take(8)
|
||||
.flatten()
|
||||
.collect();
|
||||
let verification_key = PublicKey::new(bytes.try_into().unwrap());
|
||||
let payment = helpers::payment_fixture();
|
||||
payments(storage)
|
||||
.save(&verification_key.to_bytes(), &payment)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// query with a crazily high limit in an attempt to use too many resources
|
||||
let crazy_limit = 1000;
|
||||
let page1 = query_payments_paged(deps.as_ref(), None, Option::from(crazy_limit)).unwrap();
|
||||
|
||||
// we default to a decent sized upper bound instead
|
||||
assert_eq!(PAYMENT_PAGE_MAX_LIMIT, page1.payments.len() as u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn payments_pagination_works() {
|
||||
let key1 = PublicKey::new([1; 32]);
|
||||
let key2 = PublicKey::new([2; 32]);
|
||||
let key3 = PublicKey::new([3; 32]);
|
||||
let key4 = PublicKey::new([4; 32]);
|
||||
|
||||
let mut deps = helpers::init_contract();
|
||||
let payment = helpers::payment_fixture();
|
||||
payments(&mut deps.storage)
|
||||
.save(&key1.to_bytes(), &payment)
|
||||
.unwrap();
|
||||
|
||||
let per_page = 2;
|
||||
let page1 = query_payments_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
|
||||
|
||||
// page should have 1 result on it
|
||||
assert_eq!(1, page1.payments.len());
|
||||
|
||||
// save another
|
||||
payments(&mut deps.storage)
|
||||
.save(&key2.to_bytes(), &payment)
|
||||
.unwrap();
|
||||
|
||||
// page1 should have 2 results on it
|
||||
let page1 = query_payments_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
|
||||
assert_eq!(2, page1.payments.len());
|
||||
|
||||
payments(&mut deps.storage)
|
||||
.save(&key3.to_bytes(), &payment)
|
||||
.unwrap();
|
||||
|
||||
// page1 still has 2 results
|
||||
let page1 = query_payments_paged(deps.as_ref(), None, Option::from(per_page)).unwrap();
|
||||
assert_eq!(2, page1.payments.len());
|
||||
|
||||
// retrieving the next page should start after the last key on this page
|
||||
let start_after = key2;
|
||||
let page2 = query_payments_paged(
|
||||
deps.as_ref(),
|
||||
Option::from(start_after),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(1, page2.payments.len());
|
||||
|
||||
// save another one
|
||||
payments(&mut deps.storage)
|
||||
.save(&key4.to_bytes(), &payment)
|
||||
.unwrap();
|
||||
|
||||
let start_after = key2;
|
||||
let page2 = query_payments_paged(
|
||||
deps.as_ref(),
|
||||
Option::from(start_after),
|
||||
Option::from(per_page),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// now we have 2 pages, with 2 results on the second page
|
||||
assert_eq!(2, page2.payments.len());
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::Storage;
|
||||
use cosmwasm_storage::{bucket, bucket_read, Bucket, ReadonlyBucket};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use bandwidth_claim_contract::payment::Payment;
|
||||
|
||||
// buckets
|
||||
const PREFIX_PAYMENTS: &[u8] = b"payments";
|
||||
const PREFIX_STATUS: &[u8] = b"status";
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize, JsonSchema)]
|
||||
pub enum Status {
|
||||
Unchecked,
|
||||
Checked,
|
||||
Spent,
|
||||
}
|
||||
|
||||
pub fn payments(storage: &mut dyn Storage) -> Bucket<'_, Payment> {
|
||||
bucket(storage, PREFIX_PAYMENTS)
|
||||
}
|
||||
|
||||
pub fn payments_read(storage: &dyn Storage) -> ReadonlyBucket<'_, Payment> {
|
||||
bucket_read(storage, PREFIX_PAYMENTS)
|
||||
}
|
||||
|
||||
pub fn status(storage: &mut dyn Storage) -> Bucket<'_, Status> {
|
||||
bucket(storage, PREFIX_STATUS)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::support::tests::helpers;
|
||||
use bandwidth_claim_contract::keys::PublicKey;
|
||||
use cosmwasm_std::testing::MockStorage;
|
||||
|
||||
#[test]
|
||||
fn payments_single_read_retrieval() {
|
||||
let mut storage = MockStorage::new();
|
||||
let key1 = PublicKey::new([1; 32]);
|
||||
let key2 = PublicKey::new([2; 32]);
|
||||
let payment1 = helpers::payment_fixture();
|
||||
let payment2 = helpers::payment_fixture();
|
||||
payments(&mut storage)
|
||||
.save(key1.as_ref(), &payment1)
|
||||
.unwrap();
|
||||
payments(&mut storage)
|
||||
.save(key2.as_ref(), &payment2)
|
||||
.unwrap();
|
||||
|
||||
let res1 = payments_read(&storage).load(key1.as_ref()).unwrap();
|
||||
let res2 = payments_read(&storage).load(key2.as_ref()).unwrap();
|
||||
assert_eq!(payment1, res1);
|
||||
assert_eq!(payment2, res2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn status_single_read_retrieval() {
|
||||
let mut storage = MockStorage::new();
|
||||
let key1 = PublicKey::new([1; 32]);
|
||||
let key2 = PublicKey::new([2; 32]);
|
||||
let status_value = Status::Unchecked;
|
||||
status(&mut storage)
|
||||
.save(key1.as_ref(), &status_value)
|
||||
.unwrap();
|
||||
status(&mut storage)
|
||||
.save(key2.as_ref(), &status_value)
|
||||
.unwrap();
|
||||
|
||||
let res1 = status(&mut storage).load(key1.as_ref()).unwrap();
|
||||
assert_eq!(status_value, res1);
|
||||
let res2 = status(&mut storage).load(key2.as_ref()).unwrap();
|
||||
assert_eq!(status_value, res2);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
pub mod tests;
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod helpers {
|
||||
use crate::instantiate;
|
||||
use bandwidth_claim_contract::keys::PublicKey;
|
||||
use bandwidth_claim_contract::msg::InstantiateMsg;
|
||||
use bandwidth_claim_contract::payment::Payment;
|
||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
|
||||
use cosmwasm_std::{Empty, MemoryStorage, OwnedDeps};
|
||||
|
||||
pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
|
||||
let mut deps = mock_dependencies();
|
||||
let msg = InstantiateMsg {};
|
||||
let env = mock_env();
|
||||
let info = mock_info("creator", &[]);
|
||||
instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
|
||||
return deps;
|
||||
}
|
||||
|
||||
pub fn payment_fixture() -> Payment {
|
||||
let public_key = PublicKey::new([1; 32]);
|
||||
let gateway_identity = PublicKey::new([2; 32]);
|
||||
let bandwidth = 42;
|
||||
Payment::new(public_key, gateway_identity, bandwidth)
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
|
||||
|
||||
use crate::error::ContractError;
|
||||
use crate::storage::{payments, status, Status};
|
||||
use bandwidth_claim_contract::payment::{LinkPaymentData, Payment};
|
||||
|
||||
pub(crate) fn link_payment(
|
||||
deps: DepsMut<'_>,
|
||||
_env: Env,
|
||||
_info: MessageInfo,
|
||||
data: LinkPaymentData,
|
||||
) -> Result<Response, ContractError> {
|
||||
let mut status_bucket = status(deps.storage);
|
||||
|
||||
let verification_key = data.verification_key.to_bytes();
|
||||
let gateway_identity = data.gateway_identity.to_bytes();
|
||||
let message: Vec<u8> = verification_key
|
||||
.iter()
|
||||
.chain(gateway_identity.iter())
|
||||
.copied()
|
||||
.collect();
|
||||
let signature = data.signature.to_bytes();
|
||||
|
||||
if let Ok(Some(_)) = status_bucket.may_load(&verification_key) {
|
||||
return Err(ContractError::PaymentAlreadyClaimed);
|
||||
}
|
||||
|
||||
if !deps
|
||||
.api
|
||||
.ed25519_verify(&message, &signature, &verification_key)?
|
||||
{
|
||||
return Err(ContractError::BadSignature);
|
||||
}
|
||||
|
||||
status_bucket.save(&verification_key, &Status::Unchecked)?;
|
||||
payments(deps.storage).save(
|
||||
&verification_key,
|
||||
&Payment::new(data.verification_key, data.gateway_identity, data.bandwidth),
|
||||
)?;
|
||||
|
||||
Ok(Response::default())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::storage::payments_read;
|
||||
use crate::support::tests::helpers;
|
||||
use bandwidth_claim_contract::keys::PublicKey;
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
|
||||
#[test]
|
||||
fn bad_signature_payment() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let info = mock_info("owner", &[]);
|
||||
|
||||
let payment_data = LinkPaymentData::new([1; 32], [2; 32], 42, [3; 64]);
|
||||
|
||||
assert_eq!(
|
||||
link_payment(deps.as_mut(), env, info, payment_data),
|
||||
Err(ContractError::BadSignature)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn good_payment() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let info = mock_info("owner", &[]);
|
||||
|
||||
let verification_key = [
|
||||
78, 142, 213, 13, 39, 169, 76, 205, 242, 206, 129, 208, 190, 51, 139, 206, 245, 199,
|
||||
120, 151, 181, 250, 192, 153, 123, 104, 129, 139, 60, 254, 243, 98,
|
||||
];
|
||||
let gateway_identity = [
|
||||
106, 76, 76, 238, 214, 177, 233, 112, 56, 33, 21, 201, 89, 42, 69, 196, 175, 56, 6,
|
||||
110, 184, 167, 203, 63, 1, 167, 134, 102, 165, 215, 3, 212,
|
||||
];
|
||||
let bandwidth = 42;
|
||||
let signature = [
|
||||
200, 134, 156, 198, 113, 180, 129, 90, 70, 28, 176, 201, 35, 208, 145, 28, 15, 16, 9,
|
||||
110, 148, 188, 193, 75, 157, 201, 206, 211, 128, 215, 66, 207, 175, 155, 48, 24, 171,
|
||||
254, 9, 37, 108, 205, 143, 37, 77, 189, 162, 52, 44, 130, 173, 60, 220, 22, 193, 3,
|
||||
111, 90, 123, 147, 206, 8, 137, 1,
|
||||
];
|
||||
|
||||
let payment_data =
|
||||
LinkPaymentData::new(verification_key, gateway_identity, bandwidth, signature);
|
||||
|
||||
assert!(link_payment(deps.as_mut(), env, info, payment_data).is_ok());
|
||||
|
||||
assert_eq!(
|
||||
payments_read(&deps.storage)
|
||||
.load(&verification_key)
|
||||
.unwrap(),
|
||||
Payment::new(
|
||||
PublicKey::new(verification_key),
|
||||
PublicKey::new(gateway_identity),
|
||||
bandwidth
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
status(&mut deps.storage).load(&verification_key).unwrap(),
|
||||
Status::Unchecked
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_spend_protection() {
|
||||
let mut deps = helpers::init_contract();
|
||||
let env = mock_env();
|
||||
let info = mock_info("owner", &[]);
|
||||
|
||||
let verification_key = [
|
||||
78, 142, 213, 13, 39, 169, 76, 205, 242, 206, 129, 208, 190, 51, 139, 206, 245, 199,
|
||||
120, 151, 181, 250, 192, 153, 123, 104, 129, 139, 60, 254, 243, 98,
|
||||
];
|
||||
let gateway_identity = [
|
||||
106, 76, 76, 238, 214, 177, 233, 112, 56, 33, 21, 201, 89, 42, 69, 196, 175, 56, 6,
|
||||
110, 184, 167, 203, 63, 1, 167, 134, 102, 165, 215, 3, 212,
|
||||
];
|
||||
let bandwidth = 42;
|
||||
let signature = [
|
||||
200, 134, 156, 198, 113, 180, 129, 90, 70, 28, 176, 201, 35, 208, 145, 28, 15, 16, 9,
|
||||
110, 148, 188, 193, 75, 157, 201, 206, 211, 128, 215, 66, 207, 175, 155, 48, 24, 171,
|
||||
254, 9, 37, 108, 205, 143, 37, 77, 189, 162, 52, 44, 130, 173, 60, 220, 22, 193, 3,
|
||||
111, 90, 123, 147, 206, 8, 137, 1,
|
||||
];
|
||||
|
||||
let payment_data =
|
||||
LinkPaymentData::new(verification_key, gateway_identity, bandwidth, signature);
|
||||
|
||||
link_payment(deps.as_mut(), env.clone(), info.clone(), payment_data).unwrap();
|
||||
|
||||
// Only the verification key is used for double spending protection, the other data is irrelevant
|
||||
let second_payment_data = LinkPaymentData::new(verification_key, [1; 32], 10, [2; 64]);
|
||||
assert_eq!(
|
||||
link_payment(deps.as_mut(), env, info, second_payment_data),
|
||||
Err(ContractError::PaymentAlreadyClaimed)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
node_modules
|
||||
../.env
|
||||
.to_do.md
|
||||
|
||||
#Hardhat files
|
||||
cache
|
||||
artifacts
|
||||
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
skipFiles: [
|
||||
'CosmosToken.sol',
|
||||
'Gravity.sol'
|
||||
]
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
# Basic Bandwidth Credential Generator
|
||||
|
||||
This directory contains the contract and unit tests for the `BandwidthGenerator` smart contract.
|
||||
|
||||
This contract allows users to generate Basic Bandwidth Credentials (BBCs) on the Nym cosmos blockchain using ERC20 representations of NYM as payment, utilising the Cosmos Gravity Bridge for cross-chain payment.
|
||||
|
||||
BBCs are credentials that will be presented to Gateways by a Nym Client, and represent a certain amount of bandwidth which can be sent through the Nym Mixnet.
|
||||
|
||||
By default 1 NYM = 1 GB of bandwidth. The ratio of NYM - bandwidth is denominated in bytes, and represented in the smart contract by the `BytesPerToken` variable. This variable can be adjusted by the contract owner.
|
||||
|
||||
The amount of bandwidth bought is calculated according to the following formula:
|
||||
`(Token amount in 'wei' * BytesPerToken) / 10**18`
|
||||
|
||||
## Usage
|
||||
* `npm install`
|
||||
* `npx hardhat compile`
|
||||
* `npx hardhat test`
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"_format": "hh-sol-dbg-1",
|
||||
"buildInfo": "../../../../../build-info/1f95016aa87f8376998977fedfcae017.json"
|
||||
}
|
||||
-297
File diff suppressed because one or more lines are too long
-4
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"_format": "hh-sol-dbg-1",
|
||||
"buildInfo": "../../../../../build-info/1f95016aa87f8376998977fedfcae017.json"
|
||||
}
|
||||
-194
@@ -1,194 +0,0 @@
|
||||
{
|
||||
"_format": "hh-sol-artifact-1",
|
||||
"contractName": "IERC20",
|
||||
"sourceName": "@openzeppelin/contracts/token/ERC20/IERC20.sol",
|
||||
"abi": [
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Approval",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Transfer",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "allowance",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "approve",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "account",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "balanceOf",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "totalSupply",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "recipient",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transfer",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "sender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "recipient",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transferFrom",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"bytecode": "0x",
|
||||
"deployedBytecode": "0x",
|
||||
"linkReferences": {},
|
||||
"deployedLinkReferences": {}
|
||||
}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"_format": "hh-sol-dbg-1",
|
||||
"buildInfo": "../../../../../../build-info/1f95016aa87f8376998977fedfcae017.json"
|
||||
}
|
||||
-233
@@ -1,233 +0,0 @@
|
||||
{
|
||||
"_format": "hh-sol-artifact-1",
|
||||
"contractName": "IERC20Metadata",
|
||||
"sourceName": "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol",
|
||||
"abi": [
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Approval",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "from",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "to",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "value",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "Transfer",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "allowance",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "spender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "approve",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "account",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"name": "balanceOf",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "decimals",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint8",
|
||||
"name": "",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "name",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "symbol",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "totalSupply",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "recipient",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transfer",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "sender",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "recipient",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "amount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "transferFrom",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"bytecode": "0x",
|
||||
"deployedBytecode": "0x",
|
||||
"linkReferences": {},
|
||||
"deployedLinkReferences": {}
|
||||
}
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"_format": "hh-sol-dbg-1",
|
||||
"buildInfo": "../../../../build-info/1f95016aa87f8376998977fedfcae017.json"
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"_format": "hh-sol-artifact-1",
|
||||
"contractName": "Context",
|
||||
"sourceName": "@openzeppelin/contracts/utils/Context.sol",
|
||||
"abi": [],
|
||||
"bytecode": "0x",
|
||||
"deployedBytecode": "0x",
|
||||
"linkReferences": {},
|
||||
"deployedLinkReferences": {}
|
||||
}
|
||||
@@ -1,549 +0,0 @@
|
||||
{
|
||||
"_format": "hh-sol-cache-2",
|
||||
"files": {
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/BandwidthGenerator.sol": {
|
||||
"lastModificationDate": 1643129770695,
|
||||
"contentHash": "642b72e2d50d565db7bc994cace3f5a0",
|
||||
"sourceName": "contracts/BandwidthGenerator.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"./CosmosToken.sol",
|
||||
"./Gravity.sol",
|
||||
"@openzeppelin/contracts/access/Ownable.sol",
|
||||
"@openzeppelin/contracts/utils/math/SafeMath.sol"
|
||||
],
|
||||
"versionPragmas": [
|
||||
"0.8.10"
|
||||
],
|
||||
"artifacts": [
|
||||
"BandwidthGenerator"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/CosmosToken.sol": {
|
||||
"lastModificationDate": 1642773899716,
|
||||
"contentHash": "fc5dd09fe73bc6cfece970f702a3aba1",
|
||||
"sourceName": "contracts/CosmosToken.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"@openzeppelin/contracts/token/ERC20/ERC20.sol"
|
||||
],
|
||||
"versionPragmas": [
|
||||
"0.8.10"
|
||||
],
|
||||
"artifacts": [
|
||||
"CosmosERC20"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/Gravity.sol": {
|
||||
"lastModificationDate": 1642773899716,
|
||||
"contentHash": "0d6dae561f7b541bafb892b8593a08ac",
|
||||
"sourceName": "contracts/Gravity.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"@openzeppelin/contracts/token/ERC20/IERC20.sol",
|
||||
"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol",
|
||||
"@openzeppelin/contracts/security/ReentrancyGuard.sol",
|
||||
"@openzeppelin/contracts/utils/Address.sol",
|
||||
"@openzeppelin/contracts/utils/cryptography/ECDSA.sol",
|
||||
"./CosmosToken.sol"
|
||||
],
|
||||
"versionPragmas": [
|
||||
"0.8.10"
|
||||
],
|
||||
"artifacts": [
|
||||
"Gravity"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/access/Ownable.sol": {
|
||||
"lastModificationDate": 1641812554274,
|
||||
"contentHash": "4fe56b59ced59d87df6b796758f62895",
|
||||
"sourceName": "@openzeppelin/contracts/access/Ownable.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"../utils/Context.sol"
|
||||
],
|
||||
"versionPragmas": [
|
||||
"^0.8.0"
|
||||
],
|
||||
"artifacts": [
|
||||
"Ownable"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/math/SafeMath.sol": {
|
||||
"lastModificationDate": 1641812554378,
|
||||
"contentHash": "5365090efc586b728719e562ebfed0d6",
|
||||
"sourceName": "@openzeppelin/contracts/utils/math/SafeMath.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [],
|
||||
"versionPragmas": [
|
||||
"^0.8.0"
|
||||
],
|
||||
"artifacts": [
|
||||
"SafeMath"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
|
||||
"lastModificationDate": 1641812553614,
|
||||
"contentHash": "2cd550cedf51b8d294607dad5023d717",
|
||||
"sourceName": "@openzeppelin/contracts/token/ERC20/ERC20.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"./IERC20.sol",
|
||||
"./extensions/IERC20Metadata.sol",
|
||||
"../../utils/Context.sol"
|
||||
],
|
||||
"versionPragmas": [
|
||||
"^0.8.0"
|
||||
],
|
||||
"artifacts": [
|
||||
"ERC20"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
|
||||
"lastModificationDate": 1641812554110,
|
||||
"contentHash": "0eac3e1a83ee62326ca007811285b274",
|
||||
"sourceName": "@openzeppelin/contracts/token/ERC20/IERC20.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [],
|
||||
"versionPragmas": [
|
||||
"^0.8.0"
|
||||
],
|
||||
"artifacts": [
|
||||
"IERC20"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
|
||||
"lastModificationDate": 1641812554118,
|
||||
"contentHash": "aa1be06992a99bb7393b26a6af3c4dfb",
|
||||
"sourceName": "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"../IERC20.sol"
|
||||
],
|
||||
"versionPragmas": [
|
||||
"^0.8.0"
|
||||
],
|
||||
"artifacts": [
|
||||
"IERC20Metadata"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/Context.sol": {
|
||||
"lastModificationDate": 1641812553346,
|
||||
"contentHash": "851485d5b925529b1a2f34a0be077891",
|
||||
"sourceName": "@openzeppelin/contracts/utils/Context.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [],
|
||||
"versionPragmas": [
|
||||
"^0.8.0"
|
||||
],
|
||||
"artifacts": [
|
||||
"Context"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
|
||||
"lastModificationDate": 1641812554370,
|
||||
"contentHash": "d37406082a74a9b6b114de522fcb6349",
|
||||
"sourceName": "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"../IERC20.sol",
|
||||
"../../../utils/Address.sol"
|
||||
],
|
||||
"versionPragmas": [
|
||||
"^0.8.0"
|
||||
],
|
||||
"artifacts": [
|
||||
"SafeERC20"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol": {
|
||||
"lastModificationDate": 1641812554322,
|
||||
"contentHash": "53fbff678f378956efcb207fa748eaa6",
|
||||
"sourceName": "@openzeppelin/contracts/security/ReentrancyGuard.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [],
|
||||
"versionPragmas": [
|
||||
"^0.8.0"
|
||||
],
|
||||
"artifacts": [
|
||||
"ReentrancyGuard"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/Address.sol": {
|
||||
"lastModificationDate": 1641812553270,
|
||||
"contentHash": "c5f6c4e4df069c789e7d84b4c3011913",
|
||||
"sourceName": "@openzeppelin/contracts/utils/Address.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [],
|
||||
"versionPragmas": [
|
||||
"^0.8.0"
|
||||
],
|
||||
"artifacts": [
|
||||
"Address"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol": {
|
||||
"lastModificationDate": 1641812553414,
|
||||
"contentHash": "197dbfaf7146845fa76331a4c9980e9f",
|
||||
"sourceName": "@openzeppelin/contracts/utils/cryptography/ECDSA.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [],
|
||||
"versionPragmas": [
|
||||
"^0.8.0"
|
||||
],
|
||||
"artifacts": [
|
||||
"ECDSA"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/test-contracts/TestCosmosToken.sol": {
|
||||
"lastModificationDate": 1642773899716,
|
||||
"contentHash": "957b6079ee0d5a6e048fce8555c14dee",
|
||||
"sourceName": "contracts/test-contracts/TestCosmosToken.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"@openzeppelin/contracts/token/ERC20/ERC20.sol"
|
||||
],
|
||||
"versionPragmas": [
|
||||
"0.8.10"
|
||||
],
|
||||
"artifacts": [
|
||||
"TestCosmosERC20"
|
||||
]
|
||||
},
|
||||
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/test-contracts/TestGravity.sol": {
|
||||
"lastModificationDate": 1642773899716,
|
||||
"contentHash": "f24299c3acb20aff23914e16b7ba95e1",
|
||||
"sourceName": "contracts/test-contracts/TestGravity.sol",
|
||||
"solcConfig": {
|
||||
"version": "0.8.10",
|
||||
"settings": {
|
||||
"optimizer": {
|
||||
"enabled": true,
|
||||
"runs": 200
|
||||
},
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
"abi",
|
||||
"evm.bytecode",
|
||||
"evm.deployedBytecode",
|
||||
"evm.methodIdentifiers"
|
||||
],
|
||||
"": [
|
||||
"ast"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"@openzeppelin/contracts/token/ERC20/IERC20.sol",
|
||||
"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol",
|
||||
"@openzeppelin/contracts/security/ReentrancyGuard.sol",
|
||||
"@openzeppelin/contracts/utils/Address.sol",
|
||||
"@openzeppelin/contracts/utils/cryptography/ECDSA.sol",
|
||||
"./TestCosmosToken.sol"
|
||||
],
|
||||
"versionPragmas": [
|
||||
"0.8.10"
|
||||
],
|
||||
"artifacts": [
|
||||
"TestGravity"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"rinkeby":
|
||||
{"NYM_ERC20":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B",
|
||||
"BANDWIDTH_GENERATOR":"0xfa2714Bf14EB5Bb887e4A54984C6F7A7e3E6c84b",
|
||||
"GRAVITY":"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B"},
|
||||
"mainnet":
|
||||
{"NYM_ERC20":"0x525A8F6F3Ba4752868cde25164382BfbaE3990e1",
|
||||
"NYMT":"0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F",
|
||||
"BANDWIDTH_GENERATOR":"0x3FfEb99acca159A182f35F9944dAf3BF41Ae8165",
|
||||
"BANDWIDTH_GENERATOR_NYMT":"0xB3BF30DD53044c9050B7309031Bbf26b2cecF3be",
|
||||
"GRAVITY":"0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906"}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
pragma solidity 0.8.10;
|
||||
|
||||
import "./CosmosToken.sol";
|
||||
import "./Gravity.sol";
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
|
||||
|
||||
/**
|
||||
* @title BandwidthGenerator
|
||||
* @dev Contract for generating Basic Bandwidth Credentials (BBCs) on the Nym cosmos blockchain,
|
||||
* using ERC20 representations of NYM as payment. Utilises the Gravity Bridge for cross-chain payment.
|
||||
*
|
||||
* Credential generation can be switched on/off by the contract owner.
|
||||
*
|
||||
* Credentials represent a certain amount of bandwidth which can be sent through the Nym Mixnet.
|
||||
* By default 1 NYM = 1 GB of bandwidth. The `BytesPerToken` amount can be adjusted by the contract owner.
|
||||
* The amount of bandwidth bought is calculated according to the following formula:
|
||||
* `(Token amount in 'wei' / 10**18) * BytesPerToken`
|
||||
*/
|
||||
contract BandwidthGenerator is Ownable {
|
||||
|
||||
using SafeMath for uint256;
|
||||
|
||||
CosmosERC20 public erc20;
|
||||
Gravity public gravityBridge;
|
||||
uint256 public BytesPerToken;
|
||||
bool public credentialGenerationEnabled;
|
||||
|
||||
event BBCredentialPurchased(
|
||||
uint256 Bandwidth,
|
||||
uint256 indexed VerificationKey,
|
||||
bytes SignedVerificationKey,
|
||||
string CosmosRecipient
|
||||
);
|
||||
|
||||
event RatioChanged(
|
||||
uint256 indexed NewBytesPerToken
|
||||
);
|
||||
|
||||
event CredentialGenerationSwitch(
|
||||
bool Enabled
|
||||
);
|
||||
|
||||
modifier checkEnabled() {
|
||||
require(credentialGenerationEnabled, "BandwidthGenerator: credential generation isn't currently enabled");
|
||||
_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param _erc20 Address of the erc20NYM deployed through the Gravity Bridge.
|
||||
* @param _gravityBridge Address of the deployed Gravity Bridge.
|
||||
*/
|
||||
constructor(CosmosERC20 _erc20, Gravity _gravityBridge) {
|
||||
require(address(_erc20) != address(0), "BandwidthGenerator: erc20 address cannot be null");
|
||||
require(address(_gravityBridge) != address(0), "BandwidthGenerator: gravity bridge address cannot be null");
|
||||
erc20 = _erc20;
|
||||
gravityBridge = _gravityBridge;
|
||||
BytesPerToken = 1073741824; // default amount set at deployment: 1 erc20NYM = 1073741824 Bytes = 1GB
|
||||
credentialGenerationEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Changes amount of Bytes each erc20NYM is tradable for. Can only be called by Owner.
|
||||
* @param _newBytesPerTokenAmount Amount of Bytes BBC is worth per 1 erc20NYM token.
|
||||
*/
|
||||
function changeRatio(uint256 _newBytesPerTokenAmount) public onlyOwner {
|
||||
require(_newBytesPerTokenAmount != 0, "BandwidthGenerator: price cannot be 0");
|
||||
BytesPerToken = _newBytesPerTokenAmount;
|
||||
emit RatioChanged(_newBytesPerTokenAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Switches credential generation on/off. Can only be called by Owner.
|
||||
* @param _generation Whether credential generation is turned on/off.
|
||||
*/
|
||||
function credentialGenerationSwitch(bool _generation) public onlyOwner {
|
||||
credentialGenerationEnabled = _generation;
|
||||
emit CredentialGenerationSwitch(_generation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Function to create a BBC for account owning the verification key on the Nym Cosmos Blockchain
|
||||
* by transfering erc20NYM via the Gravity Bridge.
|
||||
* @param _amount Amount of erc20NYM tokens to spend on BBC - denominated in wei.
|
||||
* @param _verificationKey Verification key of account on Nym blockchain who is purchasing BBC.
|
||||
* @param _signedVerificationKey Number of erc20NYMs to spend signed by _verificationKey for auth on Cosmos Blockchain.
|
||||
* @param _cosmosRecipient Address of the recipient of payment on Nym Cosmos Blockchain.
|
||||
*/
|
||||
function generateBasicBandwidthCredential(uint256 _amount, uint256 _verificationKey, bytes memory _signedVerificationKey, string calldata _cosmosRecipient) public checkEnabled {
|
||||
require(_signedVerificationKey.length == 64, "BandwidthGenerator: Signature doesn't have 64 bytes");
|
||||
erc20.transferFrom(msg.sender, address(this), _amount);
|
||||
erc20.approve(address(gravityBridge), _amount);
|
||||
gravityBridge.sendToCosmos(
|
||||
address(erc20),
|
||||
_cosmosRecipient,
|
||||
_amount
|
||||
);
|
||||
uint256 bandwidth = bandwidthFromToken(_amount);
|
||||
emit BBCredentialPurchased(
|
||||
bandwidth,
|
||||
_verificationKey,
|
||||
_signedVerificationKey,
|
||||
_cosmosRecipient
|
||||
);
|
||||
}
|
||||
|
||||
function bandwidthFromToken(uint256 _amount) public view returns (uint256) {
|
||||
uint256 amountMulBytes = _amount.mul(BytesPerToken);
|
||||
return amountMulBytes.div(10**18);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
//SPDX-License-Identifier: Apache-2.0
|
||||
pragma solidity 0.8.10;
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
contract CosmosERC20 is ERC20 {
|
||||
uint256 MAX_UINT = 2**256 - 1;
|
||||
uint8 private cosmosDecimals;
|
||||
address private gravityAddress;
|
||||
|
||||
// This override ensures we return the proper number of decimals
|
||||
// for the cosmos token
|
||||
function decimals() public view virtual override returns (uint8) {
|
||||
return cosmosDecimals;
|
||||
}
|
||||
|
||||
// This is not an accurate total supply. Instead this is the total supply
|
||||
// of the given cosmos asset on Ethereum at this moment in time. Keeping
|
||||
// a totally accurate supply would require constant updates from the Cosmos
|
||||
// side, while in theory this could be piggy-backed on some existing bridge
|
||||
// operation it's a lot of complextiy to add so we chose to forgoe it.
|
||||
function totalSupply() public view virtual override returns (uint256) {
|
||||
return MAX_UINT - balanceOf(gravityAddress);
|
||||
}
|
||||
|
||||
constructor(
|
||||
address _gravityAddress,
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
uint8 _decimals
|
||||
) ERC20(_name, _symbol) {
|
||||
cosmosDecimals = _decimals;
|
||||
gravityAddress = _gravityAddress;
|
||||
_mint(_gravityAddress, MAX_UINT);
|
||||
}
|
||||
}
|
||||
@@ -1,663 +0,0 @@
|
||||
//SPDX-License-Identifier: Apache-2.0
|
||||
pragma solidity 0.8.10;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
||||
import "@openzeppelin/contracts/utils/Address.sol";
|
||||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
||||
import "./CosmosToken.sol";
|
||||
|
||||
error InvalidSignature();
|
||||
error InvalidValsetNonce(uint256 newNonce, uint256 currentNonce);
|
||||
error InvalidBatchNonce(uint256 newNonce, uint256 currentNonce);
|
||||
error InvalidLogicCallNonce(uint256 newNonce, uint256 currentNonce);
|
||||
error InvalidLogicCallTransfers();
|
||||
error InvalidLogicCallFees();
|
||||
error InvalidSendToCosmos();
|
||||
error IncorrectCheckpoint();
|
||||
error MalformedNewValidatorSet();
|
||||
error MalformedCurrentValidatorSet();
|
||||
error MalformedBatch();
|
||||
error InsufficientPower(uint256 cumulativePower, uint256 powerThreshold);
|
||||
error BatchTimedOut();
|
||||
error LogicCallTimedOut();
|
||||
|
||||
// This is being used purely to avoid stack too deep errors
|
||||
struct LogicCallArgs {
|
||||
// Transfers out to the logic contract
|
||||
uint256[] transferAmounts;
|
||||
address[] transferTokenContracts;
|
||||
// The fees (transferred to msg.sender)
|
||||
uint256[] feeAmounts;
|
||||
address[] feeTokenContracts;
|
||||
// The arbitrary logic call
|
||||
address logicContractAddress;
|
||||
bytes payload;
|
||||
// Invalidation metadata
|
||||
uint256 timeOut;
|
||||
bytes32 invalidationId;
|
||||
uint256 invalidationNonce;
|
||||
}
|
||||
|
||||
// This is used purely to avoid stack too deep errors
|
||||
// represents everything about a given validator set
|
||||
struct ValsetArgs {
|
||||
// the validators in this set, represented by an Ethereum address
|
||||
address[] validators;
|
||||
// the powers of the given validators in the same order as above
|
||||
uint256[] powers;
|
||||
// the nonce of this validator set
|
||||
uint256 valsetNonce;
|
||||
// the reward amount denominated in the below reward token, can be
|
||||
// set to zero
|
||||
uint256 rewardAmount;
|
||||
// the reward token, should be set to the zero address if not being used
|
||||
address rewardToken;
|
||||
}
|
||||
|
||||
// This represents a validator signature
|
||||
struct Signature {
|
||||
uint8 v;
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
}
|
||||
|
||||
contract Gravity is ReentrancyGuard {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
// The number of 'votes' required to execute a valset
|
||||
// update or batch execution, set to 2/3 of 2^32
|
||||
uint256 constant constant_powerThreshold = 2863311530;
|
||||
|
||||
// These are updated often
|
||||
bytes32 public state_lastValsetCheckpoint;
|
||||
mapping(address => uint256) public state_lastBatchNonces;
|
||||
mapping(bytes32 => uint256) public state_invalidationMapping;
|
||||
uint256 public state_lastValsetNonce = 0;
|
||||
// event nonce zero is reserved by the Cosmos module as a special
|
||||
// value indicating that no events have yet been submitted
|
||||
uint256 public state_lastEventNonce = 1;
|
||||
|
||||
// This is set once at initialization
|
||||
bytes32 public immutable state_gravityId;
|
||||
|
||||
// TransactionBatchExecutedEvent and SendToCosmosEvent both include the field _eventNonce.
|
||||
// This is incremented every time one of these events is emitted. It is checked by the
|
||||
// Cosmos module to ensure that all events are received in order, and that none are lost.
|
||||
//
|
||||
// ValsetUpdatedEvent does not include the field _eventNonce because it is never submitted to the Cosmos
|
||||
// module. It is purely for the use of relayers to allow them to successfully submit batches.
|
||||
event TransactionBatchExecutedEvent(
|
||||
uint256 indexed _batchNonce,
|
||||
address indexed _token,
|
||||
uint256 _eventNonce
|
||||
);
|
||||
event SendToCosmosEvent(
|
||||
address indexed _tokenContract,
|
||||
address indexed _sender,
|
||||
string _destination,
|
||||
uint256 _amount,
|
||||
uint256 _eventNonce
|
||||
);
|
||||
event ERC20DeployedEvent(
|
||||
// FYI: Can't index on a string without doing a bunch of weird stuff
|
||||
string _cosmosDenom,
|
||||
address indexed _tokenContract,
|
||||
string _name,
|
||||
string _symbol,
|
||||
uint8 _decimals,
|
||||
uint256 _eventNonce
|
||||
);
|
||||
event ValsetUpdatedEvent(
|
||||
uint256 indexed _newValsetNonce,
|
||||
uint256 _eventNonce,
|
||||
uint256 _rewardAmount,
|
||||
address _rewardToken,
|
||||
address[] _validators,
|
||||
uint256[] _powers
|
||||
);
|
||||
event LogicCallEvent(
|
||||
bytes32 _invalidationId,
|
||||
uint256 _invalidationNonce,
|
||||
bytes _returnData,
|
||||
uint256 _eventNonce
|
||||
);
|
||||
|
||||
// TEST FIXTURES
|
||||
// These are here to make it easier to measure gas usage. They should be removed before production
|
||||
function testMakeCheckpoint(ValsetArgs calldata _valsetArgs, bytes32 _gravityId) external pure {
|
||||
makeCheckpoint(_valsetArgs, _gravityId);
|
||||
}
|
||||
|
||||
function testCheckValidatorSignatures(
|
||||
ValsetArgs calldata _currentValset,
|
||||
Signature[] calldata _sigs,
|
||||
bytes32 _theHash,
|
||||
uint256 _powerThreshold
|
||||
) external pure {
|
||||
checkValidatorSignatures(_currentValset, _sigs, _theHash, _powerThreshold);
|
||||
}
|
||||
|
||||
// END TEST FIXTURES
|
||||
|
||||
function lastBatchNonce(address _erc20Address) external view returns (uint256) {
|
||||
return state_lastBatchNonces[_erc20Address];
|
||||
}
|
||||
|
||||
function lastLogicCallNonce(bytes32 _invalidation_id) external view returns (uint256) {
|
||||
return state_invalidationMapping[_invalidation_id];
|
||||
}
|
||||
|
||||
// Utility function to verify geth style signatures
|
||||
function verifySig(
|
||||
address _signer,
|
||||
bytes32 _theHash,
|
||||
Signature calldata _sig
|
||||
) private pure returns (bool) {
|
||||
bytes32 messageDigest = keccak256(
|
||||
abi.encodePacked("\x19Ethereum Signed Message:\n32", _theHash)
|
||||
);
|
||||
return _signer == ECDSA.recover(messageDigest, _sig.v, _sig.r, _sig.s);
|
||||
}
|
||||
|
||||
// Utility function to determine that a validator set and signatures are well formed
|
||||
function validateValset(ValsetArgs calldata _valset, Signature[] calldata _sigs) private pure {
|
||||
// Check that current validators, powers, and signatures (v,r,s) set is well-formed
|
||||
if (
|
||||
_valset.validators.length != _valset.powers.length ||
|
||||
_valset.validators.length != _sigs.length
|
||||
) {
|
||||
revert MalformedCurrentValidatorSet();
|
||||
}
|
||||
}
|
||||
|
||||
// Make a new checkpoint from the supplied validator set
|
||||
// A checkpoint is a hash of all relevant information about the valset. This is stored by the contract,
|
||||
// instead of storing the information directly. This saves on storage and gas.
|
||||
// The format of the checkpoint is:
|
||||
// h(gravityId, "checkpoint", valsetNonce, validators[], powers[])
|
||||
// Where h is the keccak256 hash function.
|
||||
// The validator powers must be decreasing or equal. This is important for checking the signatures on the
|
||||
// next valset, since it allows the caller to stop verifying signatures once a quorum of signatures have been verified.
|
||||
function makeCheckpoint(ValsetArgs memory _valsetArgs, bytes32 _gravityId)
|
||||
private
|
||||
pure
|
||||
returns (bytes32)
|
||||
{
|
||||
// bytes32 encoding of the string "checkpoint"
|
||||
bytes32 methodName = 0x636865636b706f696e7400000000000000000000000000000000000000000000;
|
||||
|
||||
bytes32 checkpoint = keccak256(
|
||||
abi.encode(
|
||||
_gravityId,
|
||||
methodName,
|
||||
_valsetArgs.valsetNonce,
|
||||
_valsetArgs.validators,
|
||||
_valsetArgs.powers,
|
||||
_valsetArgs.rewardAmount,
|
||||
_valsetArgs.rewardToken
|
||||
)
|
||||
);
|
||||
|
||||
return checkpoint;
|
||||
}
|
||||
|
||||
function checkValidatorSignatures(
|
||||
// The current validator set and their powers
|
||||
ValsetArgs calldata _currentValset,
|
||||
// The current validator's signatures
|
||||
Signature[] calldata _sigs,
|
||||
// This is what we are checking they have signed
|
||||
bytes32 _theHash,
|
||||
uint256 _powerThreshold
|
||||
) private pure {
|
||||
uint256 cumulativePower = 0;
|
||||
|
||||
for (uint256 i = 0; i < _currentValset.validators.length; i++) {
|
||||
// If v is set to 0, this signifies that it was not possible to get a signature from this validator and we skip evaluation
|
||||
// (In a valid signature, it is either 27 or 28)
|
||||
if (_sigs[i].v != 0) {
|
||||
// Check that the current validator has signed off on the hash
|
||||
if (!verifySig(_currentValset.validators[i], _theHash, _sigs[i])) {
|
||||
revert InvalidSignature();
|
||||
}
|
||||
|
||||
// Sum up cumulative power
|
||||
cumulativePower = cumulativePower + _currentValset.powers[i];
|
||||
|
||||
// Break early to avoid wasting gas
|
||||
if (cumulativePower > _powerThreshold) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that there was enough power
|
||||
if (cumulativePower <= _powerThreshold) {
|
||||
revert InsufficientPower(cumulativePower, _powerThreshold);
|
||||
}
|
||||
// Success
|
||||
}
|
||||
|
||||
// This updates the valset by checking that the validators in the current valset have signed off on the
|
||||
// new valset. The signatures supplied are the signatures of the current valset over the checkpoint hash
|
||||
// generated from the new valset.
|
||||
// Anyone can call this function, but they must supply valid signatures of constant_powerThreshold of the current valset over
|
||||
// the new valset.
|
||||
function updateValset(
|
||||
// The new version of the validator set
|
||||
ValsetArgs calldata _newValset,
|
||||
// The current validators that approve the change
|
||||
ValsetArgs calldata _currentValset,
|
||||
// These are arrays of the parts of the current validator's signatures
|
||||
Signature[] calldata _sigs
|
||||
) external {
|
||||
// CHECKS
|
||||
|
||||
// Check that the valset nonce is greater than the old one
|
||||
if (_newValset.valsetNonce <= _currentValset.valsetNonce) {
|
||||
revert InvalidValsetNonce({
|
||||
newNonce: _newValset.valsetNonce,
|
||||
currentNonce: _currentValset.valsetNonce
|
||||
});
|
||||
}
|
||||
|
||||
// Check that the valset nonce is less than a million nonces forward from the old one
|
||||
// this makes it difficult for an attacker to lock out the contract by getting a single
|
||||
// bad validator set through with uint256 max nonce
|
||||
if (_newValset.valsetNonce > _currentValset.valsetNonce + 1000000) {
|
||||
revert InvalidValsetNonce({
|
||||
newNonce: _newValset.valsetNonce,
|
||||
currentNonce: _currentValset.valsetNonce
|
||||
});
|
||||
}
|
||||
|
||||
// Check that new validators and powers set is well-formed
|
||||
if (
|
||||
_newValset.validators.length != _newValset.powers.length ||
|
||||
_newValset.validators.length == 0
|
||||
) {
|
||||
revert MalformedNewValidatorSet();
|
||||
}
|
||||
|
||||
// Check that current validators, powers, and signatures (v,r,s) set is well-formed
|
||||
validateValset(_currentValset, _sigs);
|
||||
|
||||
// Check cumulative power to ensure the contract has sufficient power to actually
|
||||
// pass a vote
|
||||
uint256 cumulativePower = 0;
|
||||
for (uint256 i = 0; i < _newValset.powers.length; i++) {
|
||||
cumulativePower = cumulativePower + _newValset.powers[i];
|
||||
if (cumulativePower > constant_powerThreshold) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cumulativePower <= constant_powerThreshold) {
|
||||
revert InsufficientPower({
|
||||
cumulativePower: cumulativePower,
|
||||
powerThreshold: constant_powerThreshold
|
||||
});
|
||||
}
|
||||
|
||||
// Check that the supplied current validator set matches the saved checkpoint
|
||||
if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) {
|
||||
revert IncorrectCheckpoint();
|
||||
}
|
||||
|
||||
// Check that enough current validators have signed off on the new validator set
|
||||
bytes32 newCheckpoint = makeCheckpoint(_newValset, state_gravityId);
|
||||
|
||||
checkValidatorSignatures(_currentValset, _sigs, newCheckpoint, constant_powerThreshold);
|
||||
|
||||
// ACTIONS
|
||||
|
||||
// Stored to be used next time to validate that the valset
|
||||
// supplied by the caller is correct.
|
||||
state_lastValsetCheckpoint = newCheckpoint;
|
||||
|
||||
// Store new nonce
|
||||
state_lastValsetNonce = _newValset.valsetNonce;
|
||||
|
||||
// Send submission reward to msg.sender if reward token is a valid value
|
||||
if (_newValset.rewardToken != address(0) && _newValset.rewardAmount != 0) {
|
||||
IERC20(_newValset.rewardToken).safeTransfer(msg.sender, _newValset.rewardAmount);
|
||||
}
|
||||
|
||||
// LOGS
|
||||
|
||||
state_lastEventNonce = state_lastEventNonce + 1;
|
||||
emit ValsetUpdatedEvent(
|
||||
_newValset.valsetNonce,
|
||||
state_lastEventNonce,
|
||||
_newValset.rewardAmount,
|
||||
_newValset.rewardToken,
|
||||
_newValset.validators,
|
||||
_newValset.powers
|
||||
);
|
||||
}
|
||||
|
||||
// submitBatch processes a batch of Cosmos -> Ethereum transactions by sending the tokens in the transactions
|
||||
// to the destination addresses. It is approved by the current Cosmos validator set.
|
||||
// Anyone can call this function, but they must supply valid signatures of constant_powerThreshold of the current valset over
|
||||
// the batch.
|
||||
function submitBatch(
|
||||
// The validators that approve the batch
|
||||
ValsetArgs calldata _currentValset,
|
||||
// These are arrays of the parts of the validators signatures
|
||||
Signature[] calldata _sigs,
|
||||
// The batch of transactions
|
||||
uint256[] calldata _amounts,
|
||||
address[] calldata _destinations,
|
||||
uint256[] calldata _fees,
|
||||
uint256 _batchNonce,
|
||||
address _tokenContract,
|
||||
// a block height beyond which this batch is not valid
|
||||
// used to provide a fee-free timeout
|
||||
uint256 _batchTimeout
|
||||
) external nonReentrant {
|
||||
// CHECKS scoped to reduce stack depth
|
||||
{
|
||||
// Check that the batch nonce is higher than the last nonce for this token
|
||||
if (_batchNonce <= state_lastBatchNonces[_tokenContract]) {
|
||||
revert InvalidBatchNonce({
|
||||
newNonce: _batchNonce,
|
||||
currentNonce: state_lastBatchNonces[_tokenContract]
|
||||
});
|
||||
}
|
||||
|
||||
// Check that the batch nonce is less than one million nonces forward from the old one
|
||||
// this makes it difficult for an attacker to lock out the contract by getting a single
|
||||
// bad batch through with uint256 max nonce
|
||||
if (_batchNonce > state_lastBatchNonces[_tokenContract] + 1000000) {
|
||||
revert InvalidBatchNonce({
|
||||
newNonce: _batchNonce,
|
||||
currentNonce: state_lastBatchNonces[_tokenContract]
|
||||
});
|
||||
}
|
||||
|
||||
// Check that the block height is less than the timeout height
|
||||
if (block.number >= _batchTimeout) {
|
||||
revert BatchTimedOut();
|
||||
}
|
||||
|
||||
// Check that current validators, powers, and signatures (v,r,s) set is well-formed
|
||||
validateValset(_currentValset, _sigs);
|
||||
|
||||
// Check that the supplied current validator set matches the saved checkpoint
|
||||
if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) {
|
||||
revert IncorrectCheckpoint();
|
||||
}
|
||||
|
||||
// Check that the transaction batch is well-formed
|
||||
if (_amounts.length != _destinations.length || _amounts.length != _fees.length) {
|
||||
revert MalformedBatch();
|
||||
}
|
||||
|
||||
// Check that enough current validators have signed off on the transaction batch and valset
|
||||
checkValidatorSignatures(
|
||||
_currentValset,
|
||||
_sigs,
|
||||
// Get hash of the transaction batch and checkpoint
|
||||
keccak256(
|
||||
abi.encode(
|
||||
state_gravityId,
|
||||
// bytes32 encoding of "transactionBatch"
|
||||
0x7472616e73616374696f6e426174636800000000000000000000000000000000,
|
||||
_amounts,
|
||||
_destinations,
|
||||
_fees,
|
||||
_batchNonce,
|
||||
_tokenContract,
|
||||
_batchTimeout
|
||||
)
|
||||
),
|
||||
constant_powerThreshold
|
||||
);
|
||||
|
||||
// ACTIONS
|
||||
|
||||
// Store batch nonce
|
||||
state_lastBatchNonces[_tokenContract] = _batchNonce;
|
||||
|
||||
{
|
||||
// Send transaction amounts to destinations
|
||||
uint256 totalFee;
|
||||
for (uint256 i = 0; i < _amounts.length; i++) {
|
||||
IERC20(_tokenContract).safeTransfer(_destinations[i], _amounts[i]);
|
||||
totalFee = totalFee + _fees[i];
|
||||
}
|
||||
|
||||
// Send transaction fees to msg.sender
|
||||
IERC20(_tokenContract).safeTransfer(msg.sender, totalFee);
|
||||
}
|
||||
}
|
||||
|
||||
// LOGS scoped to reduce stack depth
|
||||
{
|
||||
state_lastEventNonce = state_lastEventNonce + 1;
|
||||
emit TransactionBatchExecutedEvent(_batchNonce, _tokenContract, state_lastEventNonce);
|
||||
}
|
||||
}
|
||||
|
||||
// This makes calls to contracts that execute arbitrary logic
|
||||
// First, it gives the logic contract some tokens
|
||||
// Then, it gives msg.senders tokens for fees
|
||||
// Then, it calls an arbitrary function on the logic contract
|
||||
// invalidationId and invalidationNonce are used for replay prevention.
|
||||
// They can be used to implement a per-token nonce by setting the token
|
||||
// address as the invalidationId and incrementing the nonce each call.
|
||||
// They can be used for nonce-free replay prevention by using a different invalidationId
|
||||
// for each call.
|
||||
function submitLogicCall(
|
||||
// The validators that approve the call
|
||||
ValsetArgs calldata _currentValset,
|
||||
// These are arrays of the parts of the validators signatures
|
||||
Signature[] calldata _sigs,
|
||||
LogicCallArgs memory _args
|
||||
) external nonReentrant {
|
||||
// CHECKS scoped to reduce stack depth
|
||||
{
|
||||
// Check that the call has not timed out
|
||||
if (block.number >= _args.timeOut) {
|
||||
revert LogicCallTimedOut();
|
||||
}
|
||||
|
||||
// Check that the invalidation nonce is higher than the last nonce for this invalidation Id
|
||||
if (state_invalidationMapping[_args.invalidationId] >= _args.invalidationNonce) {
|
||||
revert InvalidLogicCallNonce({
|
||||
newNonce: _args.invalidationNonce,
|
||||
currentNonce: state_invalidationMapping[_args.invalidationId]
|
||||
});
|
||||
}
|
||||
|
||||
// note the lack of nonce skipping check, it's not needed here since an attacker
|
||||
// will never be able to fill the invalidationId space, therefore a nonce lockout
|
||||
// is simply not possible
|
||||
|
||||
// Check that current validators, powers, and signatures (v,r,s) set is well-formed
|
||||
validateValset(_currentValset, _sigs);
|
||||
|
||||
// Check that the supplied current validator set matches the saved checkpoint
|
||||
if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) {
|
||||
revert IncorrectCheckpoint();
|
||||
}
|
||||
|
||||
if (_args.transferAmounts.length != _args.transferTokenContracts.length) {
|
||||
revert InvalidLogicCallTransfers();
|
||||
}
|
||||
|
||||
if (_args.feeAmounts.length != _args.feeTokenContracts.length) {
|
||||
revert InvalidLogicCallFees();
|
||||
}
|
||||
}
|
||||
{
|
||||
bytes32 argsHash = keccak256(
|
||||
abi.encode(
|
||||
state_gravityId,
|
||||
// bytes32 encoding of "logicCall"
|
||||
0x6c6f67696343616c6c0000000000000000000000000000000000000000000000,
|
||||
_args.transferAmounts,
|
||||
_args.transferTokenContracts,
|
||||
_args.feeAmounts,
|
||||
_args.feeTokenContracts,
|
||||
_args.logicContractAddress,
|
||||
_args.payload,
|
||||
_args.timeOut,
|
||||
_args.invalidationId,
|
||||
_args.invalidationNonce
|
||||
)
|
||||
);
|
||||
|
||||
// Check that enough current validators have signed off on the transaction batch and valset
|
||||
checkValidatorSignatures(
|
||||
_currentValset,
|
||||
_sigs,
|
||||
// Get hash of the transaction batch and checkpoint
|
||||
argsHash,
|
||||
constant_powerThreshold
|
||||
);
|
||||
}
|
||||
|
||||
// ACTIONS
|
||||
|
||||
// Update invaldiation nonce
|
||||
state_invalidationMapping[_args.invalidationId] = _args.invalidationNonce;
|
||||
|
||||
// Send tokens to the logic contract
|
||||
for (uint256 i = 0; i < _args.transferAmounts.length; i++) {
|
||||
IERC20(_args.transferTokenContracts[i]).safeTransfer(
|
||||
_args.logicContractAddress,
|
||||
_args.transferAmounts[i]
|
||||
);
|
||||
}
|
||||
|
||||
// Make call to logic contract
|
||||
bytes memory returnData = Address.functionCall(_args.logicContractAddress, _args.payload);
|
||||
|
||||
// Send fees to msg.sender
|
||||
for (uint256 i = 0; i < _args.feeAmounts.length; i++) {
|
||||
IERC20(_args.feeTokenContracts[i]).safeTransfer(msg.sender, _args.feeAmounts[i]);
|
||||
}
|
||||
|
||||
// LOGS scoped to reduce stack depth
|
||||
{
|
||||
state_lastEventNonce = state_lastEventNonce + 1;
|
||||
emit LogicCallEvent(
|
||||
_args.invalidationId,
|
||||
_args.invalidationNonce,
|
||||
returnData,
|
||||
state_lastEventNonce
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function sendToCosmos(
|
||||
address _tokenContract,
|
||||
string calldata _destination,
|
||||
uint256 _amount
|
||||
) external nonReentrant {
|
||||
// we snapshot our current balance of this token
|
||||
uint256 ourStartingBalance = IERC20(_tokenContract).balanceOf(address(this));
|
||||
|
||||
// attempt to transfer the user specified amount
|
||||
IERC20(_tokenContract).safeTransferFrom(msg.sender, address(this), _amount);
|
||||
|
||||
// check what this particular ERC20 implementation actually gave us, since it doesn't
|
||||
// have to be at all related to the _amount
|
||||
uint256 ourEndingBalance = IERC20(_tokenContract).balanceOf(address(this));
|
||||
|
||||
// a very strange ERC20 may trigger this condition, if we didn't have this we would
|
||||
// underflow, so it's mostly just an error message printer
|
||||
if (ourEndingBalance <= ourStartingBalance) {
|
||||
revert InvalidSendToCosmos();
|
||||
}
|
||||
|
||||
state_lastEventNonce = state_lastEventNonce + 1;
|
||||
|
||||
// emit to Cosmos the actual amount our balance has changed, rather than the user
|
||||
// provided amount. This protects against a small set of wonky ERC20 behavior, like
|
||||
// burning on send but not tokens that for example change every users balance every day.
|
||||
emit SendToCosmosEvent(
|
||||
_tokenContract,
|
||||
msg.sender,
|
||||
_destination,
|
||||
ourEndingBalance - ourStartingBalance,
|
||||
state_lastEventNonce
|
||||
);
|
||||
}
|
||||
|
||||
function deployERC20(
|
||||
string calldata _cosmosDenom,
|
||||
string calldata _name,
|
||||
string calldata _symbol,
|
||||
uint8 _decimals
|
||||
) external {
|
||||
// Deploy an ERC20 with entire supply granted to Gravity.sol
|
||||
CosmosERC20 erc20 = new CosmosERC20(address(this), _name, _symbol, _decimals);
|
||||
|
||||
// Fire an event to let the Cosmos module know
|
||||
state_lastEventNonce = state_lastEventNonce + 1;
|
||||
emit ERC20DeployedEvent(
|
||||
_cosmosDenom,
|
||||
address(erc20),
|
||||
_name,
|
||||
_symbol,
|
||||
_decimals,
|
||||
state_lastEventNonce
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
// A unique identifier for this gravity instance to use in signatures
|
||||
bytes32 _gravityId,
|
||||
// The validator set, not in valset args format since many of it's
|
||||
// arguments would never be used in this case
|
||||
address[] memory _validators,
|
||||
uint256[] memory _powers
|
||||
) {
|
||||
// CHECKS
|
||||
|
||||
// Check that validators, powers, and signatures (v,r,s) set is well-formed
|
||||
if (_validators.length != _powers.length || _validators.length == 0) {
|
||||
revert MalformedCurrentValidatorSet();
|
||||
}
|
||||
|
||||
// Check cumulative power to ensure the contract has sufficient power to actually
|
||||
// pass a vote
|
||||
uint256 cumulativePower = 0;
|
||||
for (uint256 i = 0; i < _powers.length; i++) {
|
||||
cumulativePower = cumulativePower + _powers[i];
|
||||
if (cumulativePower > constant_powerThreshold) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cumulativePower <= constant_powerThreshold) {
|
||||
revert InsufficientPower({
|
||||
cumulativePower: cumulativePower,
|
||||
powerThreshold: constant_powerThreshold
|
||||
});
|
||||
}
|
||||
|
||||
ValsetArgs memory _valset;
|
||||
_valset = ValsetArgs(_validators, _powers, 0, 0, address(0));
|
||||
|
||||
bytes32 newCheckpoint = makeCheckpoint(_valset, _gravityId);
|
||||
|
||||
// ACTIONS
|
||||
|
||||
state_gravityId = _gravityId;
|
||||
state_lastValsetCheckpoint = newCheckpoint;
|
||||
|
||||
// LOGS
|
||||
|
||||
emit ValsetUpdatedEvent(
|
||||
state_lastValsetNonce,
|
||||
state_lastEventNonce,
|
||||
0,
|
||||
address(0),
|
||||
_validators,
|
||||
_powers
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
//SPDX-License-Identifier: Apache-2.0
|
||||
pragma solidity 0.8.10;
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
/**
|
||||
* This is a slightly modified version of the cosmos erc20 contract
|
||||
* which I have done for unit testing.
|
||||
*
|
||||
* All that has been changed is the MAX_UINT variable to allow
|
||||
* me to mint some tokens more easily in unit tests, and the
|
||||
* addition of the public mint() function.
|
||||
*/
|
||||
|
||||
|
||||
contract TestCosmosERC20 is ERC20 {
|
||||
/* canonical amount */
|
||||
// uint256 MAX_UINT = 2**256 - 1;
|
||||
|
||||
/* unit testing amount */
|
||||
uint256 HALF_MAX_UINT = 2**256 / 2;
|
||||
|
||||
uint8 private cosmosDecimals;
|
||||
address private gravityAddress;
|
||||
|
||||
// This override ensures we return the proper number of decimals
|
||||
// for the cosmos token
|
||||
function decimals() public view virtual override returns (uint8) {
|
||||
return cosmosDecimals;
|
||||
}
|
||||
|
||||
// This is not an accurate total supply. Instead this is the total supply
|
||||
// of the given cosmos asset on Ethereum at this moment in time. Keeping
|
||||
// a totally accurate supply would require constant updates from the Cosmos
|
||||
// side, while in theory this could be piggy-backed on some existing bridge
|
||||
// operation it's a lot of complextiy to add so we chose to forgoe it.
|
||||
function totalSupply() public view virtual override returns (uint256) {
|
||||
return HALF_MAX_UINT - balanceOf(gravityAddress);
|
||||
}
|
||||
|
||||
constructor(
|
||||
address _gravityAddress,
|
||||
string memory _name,
|
||||
string memory _symbol,
|
||||
uint8 _decimals
|
||||
) ERC20(_name, _symbol) {
|
||||
cosmosDecimals = _decimals;
|
||||
gravityAddress = _gravityAddress;
|
||||
_mint(_gravityAddress, HALF_MAX_UINT);
|
||||
}
|
||||
|
||||
// Additional function for our (nym repo) unit tests with bridge
|
||||
function mintForUnitTesting(address _to, uint _amount) public {
|
||||
_mint(_to, _amount);
|
||||
}
|
||||
}
|
||||
@@ -1,671 +0,0 @@
|
||||
//SPDX-License-Identifier: Apache-2.0
|
||||
pragma solidity 0.8.10;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
||||
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
||||
import "@openzeppelin/contracts/utils/Address.sol";
|
||||
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
|
||||
import "./TestCosmosToken.sol";
|
||||
|
||||
/**
|
||||
* This is a slightly modified version of the gravity bridge contract
|
||||
* which I have done for unit testing.
|
||||
*
|
||||
* All that has been changed is ___
|
||||
*/
|
||||
|
||||
error InvalidSignature();
|
||||
error InvalidValsetNonce(uint256 newNonce, uint256 currentNonce);
|
||||
error InvalidBatchNonce(uint256 newNonce, uint256 currentNonce);
|
||||
error InvalidLogicCallNonce(uint256 newNonce, uint256 currentNonce);
|
||||
error InvalidLogicCallTransfers();
|
||||
error InvalidLogicCallFees();
|
||||
error InvalidSendToCosmos();
|
||||
error IncorrectCheckpoint();
|
||||
error MalformedNewValidatorSet();
|
||||
error MalformedCurrentValidatorSet();
|
||||
error MalformedBatch();
|
||||
error InsufficientPower(uint256 cumulativePower, uint256 powerThreshold);
|
||||
error BatchTimedOut();
|
||||
error LogicCallTimedOut();
|
||||
|
||||
// This is being used purely to avoid stack too deep errors
|
||||
struct LogicCallArgs {
|
||||
// Transfers out to the logic contract
|
||||
uint256[] transferAmounts;
|
||||
address[] transferTokenContracts;
|
||||
// The fees (transferred to msg.sender)
|
||||
uint256[] feeAmounts;
|
||||
address[] feeTokenContracts;
|
||||
// The arbitrary logic call
|
||||
address logicContractAddress;
|
||||
bytes payload;
|
||||
// Invalidation metadata
|
||||
uint256 timeOut;
|
||||
bytes32 invalidationId;
|
||||
uint256 invalidationNonce;
|
||||
}
|
||||
|
||||
// This is used purely to avoid stack too deep errors
|
||||
// represents everything about a given validator set
|
||||
struct ValsetArgs {
|
||||
// the validators in this set, represented by an Ethereum address
|
||||
address[] validators;
|
||||
// the powers of the given validators in the same order as above
|
||||
uint256[] powers;
|
||||
// the nonce of this validator set
|
||||
uint256 valsetNonce;
|
||||
// the reward amount denominated in the below reward token, can be
|
||||
// set to zero
|
||||
uint256 rewardAmount;
|
||||
// the reward token, should be set to the zero address if not being used
|
||||
address rewardToken;
|
||||
}
|
||||
|
||||
// This represents a validator signature
|
||||
struct Signature {
|
||||
uint8 v;
|
||||
bytes32 r;
|
||||
bytes32 s;
|
||||
}
|
||||
|
||||
contract TestGravity is ReentrancyGuard {
|
||||
using SafeERC20 for IERC20;
|
||||
|
||||
// The number of 'votes' required to execute a valset
|
||||
// update or batch execution, set to 2/3 of 2^32
|
||||
uint256 constant constant_powerThreshold = 2863311530;
|
||||
|
||||
// These are updated often
|
||||
bytes32 public state_lastValsetCheckpoint;
|
||||
mapping(address => uint256) public state_lastBatchNonces;
|
||||
mapping(bytes32 => uint256) public state_invalidationMapping;
|
||||
uint256 public state_lastValsetNonce = 0;
|
||||
// event nonce zero is reserved by the Cosmos module as a special
|
||||
// value indicating that no events have yet been submitted
|
||||
uint256 public state_lastEventNonce = 1;
|
||||
|
||||
// This is set once at initialization
|
||||
bytes32 public immutable state_gravityId;
|
||||
|
||||
// TransactionBatchExecutedEvent and SendToCosmosEvent both include the field _eventNonce.
|
||||
// This is incremented every time one of these events is emitted. It is checked by the
|
||||
// Cosmos module to ensure that all events are received in order, and that none are lost.
|
||||
//
|
||||
// ValsetUpdatedEvent does not include the field _eventNonce because it is never submitted to the Cosmos
|
||||
// module. It is purely for the use of relayers to allow them to successfully submit batches.
|
||||
event TransactionBatchExecutedEvent(
|
||||
uint256 indexed _batchNonce,
|
||||
address indexed _token,
|
||||
uint256 _eventNonce
|
||||
);
|
||||
event SendToCosmosEvent(
|
||||
address indexed _tokenContract,
|
||||
address indexed _sender,
|
||||
string _destination,
|
||||
uint256 _amount,
|
||||
uint256 _eventNonce
|
||||
);
|
||||
event ERC20DeployedEvent(
|
||||
// FYI: Can't index on a string without doing a bunch of weird stuff
|
||||
string _cosmosDenom,
|
||||
address indexed _tokenContract,
|
||||
string _name,
|
||||
string _symbol,
|
||||
uint8 _decimals,
|
||||
uint256 _eventNonce
|
||||
);
|
||||
event ValsetUpdatedEvent(
|
||||
uint256 indexed _newValsetNonce,
|
||||
uint256 _eventNonce,
|
||||
uint256 _rewardAmount,
|
||||
address _rewardToken,
|
||||
address[] _validators,
|
||||
uint256[] _powers
|
||||
);
|
||||
event LogicCallEvent(
|
||||
bytes32 _invalidationId,
|
||||
uint256 _invalidationNonce,
|
||||
bytes _returnData,
|
||||
uint256 _eventNonce
|
||||
);
|
||||
|
||||
// TEST FIXTURES
|
||||
// These are here to make it easier to measure gas usage. They should be removed before production
|
||||
function testMakeCheckpoint(ValsetArgs calldata _valsetArgs, bytes32 _gravityId) external pure {
|
||||
makeCheckpoint(_valsetArgs, _gravityId);
|
||||
}
|
||||
|
||||
function testCheckValidatorSignatures(
|
||||
ValsetArgs calldata _currentValset,
|
||||
Signature[] calldata _sigs,
|
||||
bytes32 _theHash,
|
||||
uint256 _powerThreshold
|
||||
) external pure {
|
||||
checkValidatorSignatures(_currentValset, _sigs, _theHash, _powerThreshold);
|
||||
}
|
||||
|
||||
// END TEST FIXTURES
|
||||
|
||||
function lastBatchNonce(address _erc20Address) external view returns (uint256) {
|
||||
return state_lastBatchNonces[_erc20Address];
|
||||
}
|
||||
|
||||
function lastLogicCallNonce(bytes32 _invalidation_id) external view returns (uint256) {
|
||||
return state_invalidationMapping[_invalidation_id];
|
||||
}
|
||||
|
||||
// Utility function to verify geth style signatures
|
||||
function verifySig(
|
||||
address _signer,
|
||||
bytes32 _theHash,
|
||||
Signature calldata _sig
|
||||
) private pure returns (bool) {
|
||||
bytes32 messageDigest = keccak256(
|
||||
abi.encodePacked("\x19Ethereum Signed Message:\n32", _theHash)
|
||||
);
|
||||
return _signer == ECDSA.recover(messageDigest, _sig.v, _sig.r, _sig.s);
|
||||
}
|
||||
|
||||
// Utility function to determine that a validator set and signatures are well formed
|
||||
function validateValset(ValsetArgs calldata _valset, Signature[] calldata _sigs) private pure {
|
||||
// Check that current validators, powers, and signatures (v,r,s) set is well-formed
|
||||
if (
|
||||
_valset.validators.length != _valset.powers.length ||
|
||||
_valset.validators.length != _sigs.length
|
||||
) {
|
||||
revert MalformedCurrentValidatorSet();
|
||||
}
|
||||
}
|
||||
|
||||
// Make a new checkpoint from the supplied validator set
|
||||
// A checkpoint is a hash of all relevant information about the valset. This is stored by the contract,
|
||||
// instead of storing the information directly. This saves on storage and gas.
|
||||
// The format of the checkpoint is:
|
||||
// h(gravityId, "checkpoint", valsetNonce, validators[], powers[])
|
||||
// Where h is the keccak256 hash function.
|
||||
// The validator powers must be decreasing or equal. This is important for checking the signatures on the
|
||||
// next valset, since it allows the caller to stop verifying signatures once a quorum of signatures have been verified.
|
||||
function makeCheckpoint(ValsetArgs memory _valsetArgs, bytes32 _gravityId)
|
||||
private
|
||||
pure
|
||||
returns (bytes32)
|
||||
{
|
||||
// bytes32 encoding of the string "checkpoint"
|
||||
bytes32 methodName = 0x636865636b706f696e7400000000000000000000000000000000000000000000;
|
||||
|
||||
bytes32 checkpoint = keccak256(
|
||||
abi.encode(
|
||||
_gravityId,
|
||||
methodName,
|
||||
_valsetArgs.valsetNonce,
|
||||
_valsetArgs.validators,
|
||||
_valsetArgs.powers,
|
||||
_valsetArgs.rewardAmount,
|
||||
_valsetArgs.rewardToken
|
||||
)
|
||||
);
|
||||
|
||||
return checkpoint;
|
||||
}
|
||||
|
||||
function checkValidatorSignatures(
|
||||
// The current validator set and their powers
|
||||
ValsetArgs calldata _currentValset,
|
||||
// The current validator's signatures
|
||||
Signature[] calldata _sigs,
|
||||
// This is what we are checking they have signed
|
||||
bytes32 _theHash,
|
||||
uint256 _powerThreshold
|
||||
) private pure {
|
||||
uint256 cumulativePower = 0;
|
||||
|
||||
for (uint256 i = 0; i < _currentValset.validators.length; i++) {
|
||||
// If v is set to 0, this signifies that it was not possible to get a signature from this validator and we skip evaluation
|
||||
// (In a valid signature, it is either 27 or 28)
|
||||
if (_sigs[i].v != 0) {
|
||||
// Check that the current validator has signed off on the hash
|
||||
if (!verifySig(_currentValset.validators[i], _theHash, _sigs[i])) {
|
||||
revert InvalidSignature();
|
||||
}
|
||||
|
||||
// Sum up cumulative power
|
||||
cumulativePower = cumulativePower + _currentValset.powers[i];
|
||||
|
||||
// Break early to avoid wasting gas
|
||||
if (cumulativePower > _powerThreshold) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that there was enough power
|
||||
if (cumulativePower <= _powerThreshold) {
|
||||
revert InsufficientPower(cumulativePower, _powerThreshold);
|
||||
}
|
||||
// Success
|
||||
}
|
||||
|
||||
// This updates the valset by checking that the validators in the current valset have signed off on the
|
||||
// new valset. The signatures supplied are the signatures of the current valset over the checkpoint hash
|
||||
// generated from the new valset.
|
||||
// Anyone can call this function, but they must supply valid signatures of constant_powerThreshold of the current valset over
|
||||
// the new valset.
|
||||
function updateValset(
|
||||
// The new version of the validator set
|
||||
ValsetArgs calldata _newValset,
|
||||
// The current validators that approve the change
|
||||
ValsetArgs calldata _currentValset,
|
||||
// These are arrays of the parts of the current validator's signatures
|
||||
Signature[] calldata _sigs
|
||||
) external {
|
||||
// CHECKS
|
||||
|
||||
// Check that the valset nonce is greater than the old one
|
||||
if (_newValset.valsetNonce <= _currentValset.valsetNonce) {
|
||||
revert InvalidValsetNonce({
|
||||
newNonce: _newValset.valsetNonce,
|
||||
currentNonce: _currentValset.valsetNonce
|
||||
});
|
||||
}
|
||||
|
||||
// Check that the valset nonce is less than a million nonces forward from the old one
|
||||
// this makes it difficult for an attacker to lock out the contract by getting a single
|
||||
// bad validator set through with uint256 max nonce
|
||||
if (_newValset.valsetNonce > _currentValset.valsetNonce + 1000000) {
|
||||
revert InvalidValsetNonce({
|
||||
newNonce: _newValset.valsetNonce,
|
||||
currentNonce: _currentValset.valsetNonce
|
||||
});
|
||||
}
|
||||
|
||||
// Check that new validators and powers set is well-formed
|
||||
if (
|
||||
_newValset.validators.length != _newValset.powers.length ||
|
||||
_newValset.validators.length == 0
|
||||
) {
|
||||
revert MalformedNewValidatorSet();
|
||||
}
|
||||
|
||||
// Check that current validators, powers, and signatures (v,r,s) set is well-formed
|
||||
validateValset(_currentValset, _sigs);
|
||||
|
||||
// Check cumulative power to ensure the contract has sufficient power to actually
|
||||
// pass a vote
|
||||
uint256 cumulativePower = 0;
|
||||
for (uint256 i = 0; i < _newValset.powers.length; i++) {
|
||||
cumulativePower = cumulativePower + _newValset.powers[i];
|
||||
if (cumulativePower > constant_powerThreshold) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cumulativePower <= constant_powerThreshold) {
|
||||
revert InsufficientPower({
|
||||
cumulativePower: cumulativePower,
|
||||
powerThreshold: constant_powerThreshold
|
||||
});
|
||||
}
|
||||
|
||||
// Check that the supplied current validator set matches the saved checkpoint
|
||||
if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) {
|
||||
revert IncorrectCheckpoint();
|
||||
}
|
||||
|
||||
// Check that enough current validators have signed off on the new validator set
|
||||
bytes32 newCheckpoint = makeCheckpoint(_newValset, state_gravityId);
|
||||
|
||||
checkValidatorSignatures(_currentValset, _sigs, newCheckpoint, constant_powerThreshold);
|
||||
|
||||
// ACTIONS
|
||||
|
||||
// Stored to be used next time to validate that the valset
|
||||
// supplied by the caller is correct.
|
||||
state_lastValsetCheckpoint = newCheckpoint;
|
||||
|
||||
// Store new nonce
|
||||
state_lastValsetNonce = _newValset.valsetNonce;
|
||||
|
||||
// Send submission reward to msg.sender if reward token is a valid value
|
||||
if (_newValset.rewardToken != address(0) && _newValset.rewardAmount != 0) {
|
||||
IERC20(_newValset.rewardToken).safeTransfer(msg.sender, _newValset.rewardAmount);
|
||||
}
|
||||
|
||||
// LOGS
|
||||
|
||||
state_lastEventNonce = state_lastEventNonce + 1;
|
||||
emit ValsetUpdatedEvent(
|
||||
_newValset.valsetNonce,
|
||||
state_lastEventNonce,
|
||||
_newValset.rewardAmount,
|
||||
_newValset.rewardToken,
|
||||
_newValset.validators,
|
||||
_newValset.powers
|
||||
);
|
||||
}
|
||||
|
||||
// submitBatch processes a batch of Cosmos -> Ethereum transactions by sending the tokens in the transactions
|
||||
// to the destination addresses. It is approved by the current Cosmos validator set.
|
||||
// Anyone can call this function, but they must supply valid signatures of constant_powerThreshold of the current valset over
|
||||
// the batch.
|
||||
function submitBatch(
|
||||
// The validators that approve the batch
|
||||
ValsetArgs calldata _currentValset,
|
||||
// These are arrays of the parts of the validators signatures
|
||||
Signature[] calldata _sigs,
|
||||
// The batch of transactions
|
||||
uint256[] calldata _amounts,
|
||||
address[] calldata _destinations,
|
||||
uint256[] calldata _fees,
|
||||
uint256 _batchNonce,
|
||||
address _tokenContract,
|
||||
// a block height beyond which this batch is not valid
|
||||
// used to provide a fee-free timeout
|
||||
uint256 _batchTimeout
|
||||
) external nonReentrant {
|
||||
// CHECKS scoped to reduce stack depth
|
||||
{
|
||||
// Check that the batch nonce is higher than the last nonce for this token
|
||||
if (_batchNonce <= state_lastBatchNonces[_tokenContract]) {
|
||||
revert InvalidBatchNonce({
|
||||
newNonce: _batchNonce,
|
||||
currentNonce: state_lastBatchNonces[_tokenContract]
|
||||
});
|
||||
}
|
||||
|
||||
// Check that the batch nonce is less than one million nonces forward from the old one
|
||||
// this makes it difficult for an attacker to lock out the contract by getting a single
|
||||
// bad batch through with uint256 max nonce
|
||||
if (_batchNonce > state_lastBatchNonces[_tokenContract] + 1000000) {
|
||||
revert InvalidBatchNonce({
|
||||
newNonce: _batchNonce,
|
||||
currentNonce: state_lastBatchNonces[_tokenContract]
|
||||
});
|
||||
}
|
||||
|
||||
// Check that the block height is less than the timeout height
|
||||
if (block.number >= _batchTimeout) {
|
||||
revert BatchTimedOut();
|
||||
}
|
||||
|
||||
// Check that current validators, powers, and signatures (v,r,s) set is well-formed
|
||||
validateValset(_currentValset, _sigs);
|
||||
|
||||
// Check that the supplied current validator set matches the saved checkpoint
|
||||
if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) {
|
||||
revert IncorrectCheckpoint();
|
||||
}
|
||||
|
||||
// Check that the transaction batch is well-formed
|
||||
if (_amounts.length != _destinations.length || _amounts.length != _fees.length) {
|
||||
revert MalformedBatch();
|
||||
}
|
||||
|
||||
// Check that enough current validators have signed off on the transaction batch and valset
|
||||
checkValidatorSignatures(
|
||||
_currentValset,
|
||||
_sigs,
|
||||
// Get hash of the transaction batch and checkpoint
|
||||
keccak256(
|
||||
abi.encode(
|
||||
state_gravityId,
|
||||
// bytes32 encoding of "transactionBatch"
|
||||
0x7472616e73616374696f6e426174636800000000000000000000000000000000,
|
||||
_amounts,
|
||||
_destinations,
|
||||
_fees,
|
||||
_batchNonce,
|
||||
_tokenContract,
|
||||
_batchTimeout
|
||||
)
|
||||
),
|
||||
constant_powerThreshold
|
||||
);
|
||||
|
||||
// ACTIONS
|
||||
|
||||
// Store batch nonce
|
||||
state_lastBatchNonces[_tokenContract] = _batchNonce;
|
||||
|
||||
{
|
||||
// Send transaction amounts to destinations
|
||||
uint256 totalFee;
|
||||
for (uint256 i = 0; i < _amounts.length; i++) {
|
||||
IERC20(_tokenContract).safeTransfer(_destinations[i], _amounts[i]);
|
||||
totalFee = totalFee + _fees[i];
|
||||
}
|
||||
|
||||
// Send transaction fees to msg.sender
|
||||
IERC20(_tokenContract).safeTransfer(msg.sender, totalFee);
|
||||
}
|
||||
}
|
||||
|
||||
// LOGS scoped to reduce stack depth
|
||||
{
|
||||
state_lastEventNonce = state_lastEventNonce + 1;
|
||||
emit TransactionBatchExecutedEvent(_batchNonce, _tokenContract, state_lastEventNonce);
|
||||
}
|
||||
}
|
||||
|
||||
// This makes calls to contracts that execute arbitrary logic
|
||||
// First, it gives the logic contract some tokens
|
||||
// Then, it gives msg.senders tokens for fees
|
||||
// Then, it calls an arbitrary function on the logic contract
|
||||
// invalidationId and invalidationNonce are used for replay prevention.
|
||||
// They can be used to implement a per-token nonce by setting the token
|
||||
// address as the invalidationId and incrementing the nonce each call.
|
||||
// They can be used for nonce-free replay prevention by using a different invalidationId
|
||||
// for each call.
|
||||
function submitLogicCall(
|
||||
// The validators that approve the call
|
||||
ValsetArgs calldata _currentValset,
|
||||
// These are arrays of the parts of the validators signatures
|
||||
Signature[] calldata _sigs,
|
||||
LogicCallArgs memory _args
|
||||
) external nonReentrant {
|
||||
// CHECKS scoped to reduce stack depth
|
||||
{
|
||||
// Check that the call has not timed out
|
||||
if (block.number >= _args.timeOut) {
|
||||
revert LogicCallTimedOut();
|
||||
}
|
||||
|
||||
// Check that the invalidation nonce is higher than the last nonce for this invalidation Id
|
||||
if (state_invalidationMapping[_args.invalidationId] >= _args.invalidationNonce) {
|
||||
revert InvalidLogicCallNonce({
|
||||
newNonce: _args.invalidationNonce,
|
||||
currentNonce: state_invalidationMapping[_args.invalidationId]
|
||||
});
|
||||
}
|
||||
|
||||
// note the lack of nonce skipping check, it's not needed here since an attacker
|
||||
// will never be able to fill the invalidationId space, therefore a nonce lockout
|
||||
// is simply not possible
|
||||
|
||||
// Check that current validators, powers, and signatures (v,r,s) set is well-formed
|
||||
validateValset(_currentValset, _sigs);
|
||||
|
||||
// Check that the supplied current validator set matches the saved checkpoint
|
||||
if (makeCheckpoint(_currentValset, state_gravityId) != state_lastValsetCheckpoint) {
|
||||
revert IncorrectCheckpoint();
|
||||
}
|
||||
|
||||
if (_args.transferAmounts.length != _args.transferTokenContracts.length) {
|
||||
revert InvalidLogicCallTransfers();
|
||||
}
|
||||
|
||||
if (_args.feeAmounts.length != _args.feeTokenContracts.length) {
|
||||
revert InvalidLogicCallFees();
|
||||
}
|
||||
}
|
||||
{
|
||||
bytes32 argsHash = keccak256(
|
||||
abi.encode(
|
||||
state_gravityId,
|
||||
// bytes32 encoding of "logicCall"
|
||||
0x6c6f67696343616c6c0000000000000000000000000000000000000000000000,
|
||||
_args.transferAmounts,
|
||||
_args.transferTokenContracts,
|
||||
_args.feeAmounts,
|
||||
_args.feeTokenContracts,
|
||||
_args.logicContractAddress,
|
||||
_args.payload,
|
||||
_args.timeOut,
|
||||
_args.invalidationId,
|
||||
_args.invalidationNonce
|
||||
)
|
||||
);
|
||||
|
||||
// Check that enough current validators have signed off on the transaction batch and valset
|
||||
checkValidatorSignatures(
|
||||
_currentValset,
|
||||
_sigs,
|
||||
// Get hash of the transaction batch and checkpoint
|
||||
argsHash,
|
||||
constant_powerThreshold
|
||||
);
|
||||
}
|
||||
|
||||
// ACTIONS
|
||||
|
||||
// Update invaldiation nonce
|
||||
state_invalidationMapping[_args.invalidationId] = _args.invalidationNonce;
|
||||
|
||||
// Send tokens to the logic contract
|
||||
for (uint256 i = 0; i < _args.transferAmounts.length; i++) {
|
||||
IERC20(_args.transferTokenContracts[i]).safeTransfer(
|
||||
_args.logicContractAddress,
|
||||
_args.transferAmounts[i]
|
||||
);
|
||||
}
|
||||
|
||||
// Make call to logic contract
|
||||
bytes memory returnData = Address.functionCall(_args.logicContractAddress, _args.payload);
|
||||
|
||||
// Send fees to msg.sender
|
||||
for (uint256 i = 0; i < _args.feeAmounts.length; i++) {
|
||||
IERC20(_args.feeTokenContracts[i]).safeTransfer(msg.sender, _args.feeAmounts[i]);
|
||||
}
|
||||
|
||||
// LOGS scoped to reduce stack depth
|
||||
{
|
||||
state_lastEventNonce = state_lastEventNonce + 1;
|
||||
emit LogicCallEvent(
|
||||
_args.invalidationId,
|
||||
_args.invalidationNonce,
|
||||
returnData,
|
||||
state_lastEventNonce
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function sendToCosmos(
|
||||
address _tokenContract,
|
||||
string calldata _destination,
|
||||
uint256 _amount
|
||||
) external nonReentrant {
|
||||
// we snapshot our current balance of this token
|
||||
uint256 ourStartingBalance = IERC20(_tokenContract).balanceOf(address(this));
|
||||
|
||||
// attempt to transfer the user specified amount
|
||||
IERC20(_tokenContract).safeTransferFrom(msg.sender, address(this), _amount);
|
||||
|
||||
// check what this particular ERC20 implementation actually gave us, since it doesn't
|
||||
// have to be at all related to the _amount
|
||||
uint256 ourEndingBalance = IERC20(_tokenContract).balanceOf(address(this));
|
||||
|
||||
// a very strange ERC20 may trigger this condition, if we didn't have this we would
|
||||
// underflow, so it's mostly just an error message printer
|
||||
if (ourEndingBalance <= ourStartingBalance) {
|
||||
revert InvalidSendToCosmos();
|
||||
}
|
||||
|
||||
state_lastEventNonce = state_lastEventNonce + 1;
|
||||
|
||||
// emit to Cosmos the actual amount our balance has changed, rather than the user
|
||||
// provided amount. This protects against a small set of wonky ERC20 behavior, like
|
||||
// burning on send but not tokens that for example change every users balance every day.
|
||||
emit SendToCosmosEvent(
|
||||
_tokenContract,
|
||||
msg.sender,
|
||||
_destination,
|
||||
ourEndingBalance - ourStartingBalance,
|
||||
state_lastEventNonce
|
||||
);
|
||||
}
|
||||
|
||||
function deployERC20(
|
||||
string calldata _cosmosDenom,
|
||||
string calldata _name,
|
||||
string calldata _symbol,
|
||||
uint8 _decimals
|
||||
) external {
|
||||
// NOTE this is an edit made for the nym codebase unit tests - in the canonical bridge it is the entire token supply
|
||||
// Deploy an ERC20 with half of entire supply granted to Gravity.sol
|
||||
TestCosmosERC20 erc20 = new TestCosmosERC20(address(this), _name, _symbol, _decimals);
|
||||
|
||||
// Fire an event to let the Cosmos module know
|
||||
state_lastEventNonce = state_lastEventNonce + 1;
|
||||
emit ERC20DeployedEvent(
|
||||
_cosmosDenom,
|
||||
address(erc20),
|
||||
_name,
|
||||
_symbol,
|
||||
_decimals,
|
||||
state_lastEventNonce
|
||||
);
|
||||
}
|
||||
|
||||
constructor(
|
||||
// A unique identifier for this gravity instance to use in signatures
|
||||
bytes32 _gravityId,
|
||||
// The validator set, not in valset args format since many of it's
|
||||
// arguments would never be used in this case
|
||||
address[] memory _validators,
|
||||
uint256[] memory _powers
|
||||
) {
|
||||
// CHECKS
|
||||
|
||||
// Check that validators, powers, and signatures (v,r,s) set is well-formed
|
||||
if (_validators.length != _powers.length || _validators.length == 0) {
|
||||
revert MalformedCurrentValidatorSet();
|
||||
}
|
||||
|
||||
// Check cumulative power to ensure the contract has sufficient power to actually
|
||||
// pass a vote
|
||||
uint256 cumulativePower = 0;
|
||||
for (uint256 i = 0; i < _powers.length; i++) {
|
||||
cumulativePower = cumulativePower + _powers[i];
|
||||
if (cumulativePower > constant_powerThreshold) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cumulativePower <= constant_powerThreshold) {
|
||||
revert InsufficientPower({
|
||||
cumulativePower: cumulativePower,
|
||||
powerThreshold: constant_powerThreshold
|
||||
});
|
||||
}
|
||||
|
||||
ValsetArgs memory _valset;
|
||||
_valset = ValsetArgs(_validators, _powers, 0, 0, address(0));
|
||||
|
||||
bytes32 newCheckpoint = makeCheckpoint(_valset, _gravityId);
|
||||
|
||||
// ACTIONS
|
||||
|
||||
state_gravityId = _gravityId;
|
||||
state_lastValsetCheckpoint = newCheckpoint;
|
||||
|
||||
// LOGS
|
||||
|
||||
emit ValsetUpdatedEvent(
|
||||
state_lastValsetNonce,
|
||||
state_lastEventNonce,
|
||||
0,
|
||||
address(0),
|
||||
_validators,
|
||||
_powers
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
require("@nomiclabs/hardhat-etherscan");
|
||||
require("@nomiclabs/hardhat-truffle5");
|
||||
require("@nomiclabs/hardhat-web3");
|
||||
require("@nomiclabs/hardhat-ethers");
|
||||
require('dotenv').config({ path: require('find-config')('.env') });
|
||||
|
||||
/**
|
||||
* @type import('hardhat/config').HardhatUserConfig
|
||||
*/
|
||||
module.exports = {
|
||||
solidity: {
|
||||
version: "0.8.10",
|
||||
settings: {
|
||||
optimizer: {
|
||||
enabled: true
|
||||
}
|
||||
} },
|
||||
// paths: {
|
||||
// artifacts: "./artifacts/contracts"
|
||||
// },
|
||||
networks: {
|
||||
localhost: {
|
||||
url: "http://127.0.0.1:8545"
|
||||
},
|
||||
rinkeby: {
|
||||
url: process.env.RINKEBY_URL, //Infura url with projectId
|
||||
accounts: [process.env.PRIV_KEY], // private key of account used for contract interaction
|
||||
gas: "auto",
|
||||
gasPrice: "auto"
|
||||
},
|
||||
mainnet: {
|
||||
url: process.env.MAINNET_URL, //Infura url with projectId
|
||||
accounts: [process.env.PRIV_KEY], // private key of account used for contract interaction
|
||||
gas: "auto",
|
||||
gasPrice: "auto"
|
||||
}
|
||||
},
|
||||
etherscan: {
|
||||
// Your API key for Etherscan
|
||||
// Obtain one at https://etherscan.io/
|
||||
apiKey: process.env.ETHERSCAN_API_KEY
|
||||
}
|
||||
};
|
||||
@@ -1,34 +0,0 @@
|
||||
const { ethers } = require('hardhat');
|
||||
const { constants } = require('@openzeppelin/test-helpers');
|
||||
const contracts = require("../../contractAddresses.json");
|
||||
const fs = require('file-system');
|
||||
|
||||
async function main() {
|
||||
|
||||
const BandwidthGenerator = await ethers.getContractFactory("BandwidthGenerator");
|
||||
|
||||
console.log('preparing to deploy contract...')
|
||||
|
||||
// if this is failing, check whether the ERC20 address has been manually added to the contract addresses json file
|
||||
const bandwidthGenerator = await BandwidthGenerator.deploy(
|
||||
contracts.mainnet.NYM_ERC20,
|
||||
contracts.mainnet.GRAVITY
|
||||
);
|
||||
|
||||
console.log('...contract successfully deployed...');
|
||||
|
||||
contracts.mainnet.BANDWIDTH_GENERATOR = bandwidthGenerator.address;
|
||||
// the location of the json file is relative to where you are running the script from - run from root of directory
|
||||
fs.writeFileSync('./contractAddresses.json', JSON.stringify(contracts), (err) => {
|
||||
if (err) throw err;
|
||||
});
|
||||
|
||||
console.log(`...bandwidthGenerator.sol deployed at ${bandwidthGenerator.address}`);
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
// arguments for verification of bandwidth generator constructed with NYMT via hardhat-etherscan plugin
|
||||
// npx hardhat verify --constructor-args ./scripts/mainnet/mainnet-nymt-bandwidth-args.js --network mainnet 0xB3BF30DD53044c9050B7309031Bbf26b2cecF3be
|
||||
module.exports = [
|
||||
"0xE8883BAeF3869e14E4823F46662e81D4F7d2A81F",
|
||||
"0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906"
|
||||
];
|
||||
@@ -1,8 +0,0 @@
|
||||
// arguments for verification of gravity contract via hardhat-etherscan plugin
|
||||
// npx hardhat verify --constructor-args ./scripts/mainnet/mainnet-token-args.js --network mainnet 0xCf6DeE9947fdDc958985E5283e63d41CBC83Ff61
|
||||
module.exports = [
|
||||
"0xa4108aA1Ec4967F8b52220a4f7e94A8201F2D906",
|
||||
"nym",
|
||||
"nym",
|
||||
6
|
||||
];
|
||||
@@ -1,30 +0,0 @@
|
||||
const { ethers } = require('hardhat');
|
||||
const { constants } = require('@openzeppelin/test-helpers');
|
||||
const contracts = require("../../contractAddresses.json");
|
||||
const fs = require('file-system');
|
||||
|
||||
async function main() {
|
||||
const BandwidthGenerator = await ethers.getContractFactory("BandwidthGenerator");
|
||||
// if this is failing, check whether the ERC20 address has been manually added to the contract addresses json file
|
||||
const bandwidthGenerator = await BandwidthGenerator.deploy(
|
||||
contracts.rinkeby.NYM_ERC20,
|
||||
contracts.rinkeby.GRAVITY
|
||||
);
|
||||
|
||||
console.log("deploying...");
|
||||
|
||||
contracts.rinkeby.BANDWIDTH_GENERATOR = bandwidthGenerator.address;
|
||||
// the location of the json file is relative to where you are running the script from - run from root of directory
|
||||
fs.writeFileSync('./contractAddresses.json', JSON.stringify(contracts), (err) => {
|
||||
if (err) throw err;
|
||||
});
|
||||
|
||||
console.log(`...bandwidthGenerator.sol deployed at ${bandwidthGenerator.address}`);
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
const { ethers } = require('hardhat');
|
||||
const { constants } = require('@openzeppelin/test-helpers');
|
||||
const contracts = require("../../contractAddresses.json");
|
||||
const fs = require('file-system');
|
||||
|
||||
async function main() {
|
||||
const [deployer] = await ethers.getSigners();
|
||||
console.log(deployer.address);
|
||||
console.log(constants.ZERO_BYTES32);
|
||||
const Gravity = await ethers.getContractFactory("Gravity");
|
||||
// deploy with args from unit tests
|
||||
const gravity = await Gravity.deploy(
|
||||
constants.ZERO_BYTES32,
|
||||
[deployer.address],
|
||||
[2863311531]
|
||||
);
|
||||
|
||||
contracts.rinkeby.GRAVITY = gravity.address;
|
||||
// the location of the json file is relative to where you are running the script from - run from root of directory
|
||||
fs.writeFileSync('./contractAddresses.json', JSON.stringify(contracts), (err) => {
|
||||
if (err) throw err;
|
||||
});
|
||||
|
||||
console.log(`gravity.sol deployed at ${gravity.address}`);
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,6 +0,0 @@
|
||||
// arguments for verification of gravity contract via hardhat-etherscan plugin
|
||||
module.exports = [
|
||||
"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
["0xf5b1B5C9b89906219Ba29a6cb12F0528c4C25D18"],
|
||||
[2863311531]
|
||||
];
|
||||
@@ -1,7 +0,0 @@
|
||||
// arguments for verification of gravity contract via hardhat-etherscan plugin
|
||||
module.exports = [
|
||||
"0xe58c12f8bA4a28e87841b10BdEdf80A47F86BA9B",
|
||||
"test",
|
||||
"tst",
|
||||
6
|
||||
];
|
||||
@@ -1,266 +0,0 @@
|
||||
const { expect } = require("chai");
|
||||
const { constants, expectRevert, expectEvent } = require('@openzeppelin/test-helpers');
|
||||
const { artifacts, web3 } = require("hardhat");
|
||||
const BN = require('bn.js');
|
||||
const BandwidthGenerator = artifacts.require('BandwidthGenerator');
|
||||
const Gravity = artifacts.require('test-contracts/TestGravity');
|
||||
const CosmosToken = artifacts.require('TestCosmosERC20');
|
||||
|
||||
|
||||
contract('BandwidthGenerator', (accounts) => {
|
||||
let bandwidthGenerator;
|
||||
let gravity;
|
||||
let erc20token;
|
||||
let owner = accounts[0];
|
||||
let user = accounts[1];
|
||||
let cosmosRecipient = "nymt1f06hzmwf9chqewkpv93ajk6tayzp4784m2da9x"; // random sandbox testnet address
|
||||
let initialRatio = 1073741824; // 1073741824 bytes = 1GB
|
||||
let newRatio;
|
||||
let tokenAmount = web3.utils.toWei('100'); // this is converting 100 tokens to their representation in wei: 100000000000000000000
|
||||
let halfTokenAmount = web3.utils.toWei('50');
|
||||
let unevenTokenAmount = web3.utils.toWei('11.5'); // 11500000000000000000
|
||||
let oneToken = web3.utils.toWei('1');
|
||||
|
||||
before('deploy contracts', async () => {
|
||||
|
||||
// deploy gravity bridge with test data
|
||||
gravity = await Gravity.new(
|
||||
constants.ZERO_BYTES32,
|
||||
[owner],
|
||||
[2863311531]
|
||||
);
|
||||
|
||||
// deploy erc20 NYM from bridge
|
||||
await gravity.deployERC20(
|
||||
'cosmosNYMDenomination',
|
||||
'NYMERC20',
|
||||
'NYM',
|
||||
6
|
||||
);
|
||||
|
||||
// grab event args for getting token address
|
||||
const logs = await gravity.getPastEvents({
|
||||
fromBlock: 0,
|
||||
toBlock: "latest",
|
||||
});
|
||||
|
||||
// create contract abstraction of deployed erc20NYM with address from event args
|
||||
erc20token = await CosmosToken.at(logs[0].args._tokenContract);
|
||||
|
||||
// deploy bandwidthGenerator contract with contract address of erc20NYM & address of gravity bridge
|
||||
bandwidthGenerator = await BandwidthGenerator.new(erc20token.address, gravity.address);
|
||||
|
||||
});
|
||||
|
||||
context(">> deployment parameters are valid", () => {
|
||||
it("returns the correct erc20 address", async () => {
|
||||
expect((await bandwidthGenerator.erc20()).toString()).to.equal((erc20token.address).toString());
|
||||
});
|
||||
it("returns the correct gravity address", async () => {
|
||||
expect((await bandwidthGenerator.gravityBridge()).toString()).to.equal((gravity.address).toString());
|
||||
});
|
||||
it("returns the correct initial BytesPerToken ratio", async () => {
|
||||
expect((await bandwidthGenerator.BytesPerToken()).toString()).to.equal((initialRatio).toString());
|
||||
});
|
||||
it("returns the correct contract admin", async () => {
|
||||
expect((await bandwidthGenerator.owner()).toString()).to.equal((owner).toString());
|
||||
});
|
||||
it("returns the correct default generation state: true", async () => {
|
||||
expect((await bandwidthGenerator.credentialGenerationEnabled())).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
context(">> deployment parameters are invalid", () => {
|
||||
it("cannot be deployed with invalid erc20 address (zero address)", async () => {
|
||||
expectRevert(
|
||||
BandwidthGenerator.new(constants.ZERO_ADDRESS, gravity.address),
|
||||
"BandwidthGenerator: erc20 address cannot be null"
|
||||
)
|
||||
});
|
||||
it("cannot be deployed with invalid gravity bridge address (zero address)", async () => {
|
||||
expectRevert(
|
||||
BandwidthGenerator.new(erc20token.address, constants.ZERO_ADDRESS),
|
||||
"BandwidthGenerator: gravity bridge address cannot be null"
|
||||
)
|
||||
});
|
||||
});
|
||||
|
||||
context(">> generateBasicBandwidthCredential()", () => {
|
||||
before("mint tokens & approve", async () => {
|
||||
// transfer tokens to account which will create a BBCredential
|
||||
await erc20token.mintForUnitTesting(user, tokenAmount);
|
||||
// approve transfer to contract
|
||||
await erc20token.approve(bandwidthGenerator.address,(tokenAmount),{ from: user });
|
||||
});
|
||||
|
||||
it("transfers tokens to bridge and emits an event with the correct values: 50 erc20NYM = 50GB of bandwidth", async () => {
|
||||
let tx = await bandwidthGenerator.generateBasicBandwidthCredential(
|
||||
halfTokenAmount,
|
||||
15,
|
||||
[0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba,
|
||||
0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba],
|
||||
cosmosRecipient,
|
||||
{ from: user }
|
||||
);
|
||||
|
||||
let expectedBandwidthInMB = ((halfTokenAmount/10**18)*initialRatio); // 50 * (1024*1024*1024) bytes = 51200MB = 50GB of bandwidth
|
||||
|
||||
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'BBCredentialPurchased', {
|
||||
Bandwidth: expectedBandwidthInMB.toString(),
|
||||
VerificationKey: '15',
|
||||
SignedVerificationKey: '0x39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba',
|
||||
CosmosRecipient: cosmosRecipient
|
||||
});
|
||||
|
||||
await expectEvent.inTransaction(tx.tx, erc20token, 'Transfer', {
|
||||
from: user,
|
||||
to: bandwidthGenerator.address,
|
||||
});
|
||||
|
||||
await expectEvent.inTransaction(tx.tx, gravity, 'SendToCosmosEvent', {
|
||||
_tokenContract: erc20token.address,
|
||||
_sender: bandwidthGenerator.address,
|
||||
_destination: cosmosRecipient,
|
||||
_amount: halfTokenAmount
|
||||
});
|
||||
|
||||
expect((await erc20token.balanceOf(bandwidthGenerator.address)).toString()).to.equal('0');
|
||||
expect((await erc20token.balanceOf(user)).toString()).to.equal(halfTokenAmount.toString());
|
||||
});
|
||||
|
||||
/**
|
||||
* This can be out by a float still with amounts such as '.1'
|
||||
*/
|
||||
it("it transfers for uneven token amounts", async () => {
|
||||
let tx = await bandwidthGenerator.generateBasicBandwidthCredential(
|
||||
unevenTokenAmount,
|
||||
15,
|
||||
[0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba,
|
||||
0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba],
|
||||
cosmosRecipient,
|
||||
{ from: user }
|
||||
);
|
||||
|
||||
let newexpectedBandwidthInMB = ((11500000000000000000*initialRatio)/10**18);
|
||||
|
||||
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'BBCredentialPurchased', {
|
||||
Bandwidth: newexpectedBandwidthInMB.toString(),
|
||||
VerificationKey: '15',
|
||||
SignedVerificationKey: '0x39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba',
|
||||
CosmosRecipient: cosmosRecipient
|
||||
});
|
||||
|
||||
await expectEvent.inTransaction(tx.tx, erc20token, 'Transfer', {
|
||||
from: user,
|
||||
to: bandwidthGenerator.address,
|
||||
});
|
||||
|
||||
await expectEvent.inTransaction(tx.tx, gravity, 'SendToCosmosEvent', {
|
||||
_tokenContract: erc20token.address,
|
||||
_sender: bandwidthGenerator.address,
|
||||
_destination: cosmosRecipient,
|
||||
_amount: unevenTokenAmount
|
||||
});
|
||||
});
|
||||
|
||||
it("reverts when signed verification key !=64 bytes", async () => {
|
||||
await erc20token.approve(bandwidthGenerator.address,(halfTokenAmount),{ from: user });
|
||||
|
||||
await expectRevert(
|
||||
bandwidthGenerator.generateBasicBandwidthCredential(
|
||||
1,
|
||||
16,
|
||||
[0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba,
|
||||
0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba],
|
||||
cosmosRecipient,
|
||||
{ from: user }
|
||||
), "BandwidthGenerator: Signature doesn't have 64 bytes"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
context(">> changeRatio()", () => {
|
||||
it("only admin can change token to MB ratio", async () => {
|
||||
newRatio = 10 * initialRatio; // 10GB of bandwidth per 1 erc20NYM
|
||||
await expectRevert(
|
||||
bandwidthGenerator.changeRatio(newRatio, {from: user}),
|
||||
"Ownable: caller is not the owner"
|
||||
);
|
||||
});
|
||||
it("admin can change ratio, emits 'RatioChanged' event", async () => {
|
||||
let tx = await bandwidthGenerator.changeRatio(newRatio, {from: owner});
|
||||
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'RatioChanged', {
|
||||
NewBytesPerToken: newRatio.toString()
|
||||
});
|
||||
expect((await bandwidthGenerator.BytesPerToken()).toString()).to.equal((newRatio).toString());
|
||||
});
|
||||
it("BBCredential represents new ratio after change: 1 erc20NYM = 10GB of bandwidth", async () => {
|
||||
let tx = await bandwidthGenerator.generateBasicBandwidthCredential(
|
||||
oneToken,
|
||||
15,
|
||||
[0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba,
|
||||
0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba],
|
||||
cosmosRecipient,
|
||||
{ from: user }
|
||||
);
|
||||
|
||||
let expectedBandwidthInMB = ((oneToken/10**18)*newRatio);
|
||||
|
||||
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'BBCredentialPurchased', {
|
||||
Bandwidth: expectedBandwidthInMB.toString(),
|
||||
VerificationKey: '15',
|
||||
SignedVerificationKey: '0x39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba39530a00eae2a5aac8144209ccac917ae56bf4a9589544cb0020f92fee35a3ba',
|
||||
CosmosRecipient: cosmosRecipient
|
||||
});
|
||||
|
||||
await expectEvent.inTransaction(tx.tx, erc20token, 'Transfer', {
|
||||
from: user,
|
||||
to: bandwidthGenerator.address,
|
||||
});
|
||||
|
||||
await expectEvent.inTransaction(tx.tx, gravity, 'SendToCosmosEvent', {
|
||||
_tokenContract: erc20token.address,
|
||||
_sender: bandwidthGenerator.address,
|
||||
_destination: cosmosRecipient,
|
||||
_amount: oneToken
|
||||
});
|
||||
});
|
||||
});
|
||||
context(">>credential generation admin switch", () => {
|
||||
it("only admin can switch credential generation off", async () => {
|
||||
await expectRevert(
|
||||
bandwidthGenerator.credentialGenerationSwitch(false, { from: user }),
|
||||
"Ownable: caller is not the owner"
|
||||
)
|
||||
});
|
||||
it("admin can switch credential generation on/off & switch generates an event", async () => {
|
||||
let tx = await bandwidthGenerator.credentialGenerationSwitch(false);
|
||||
expect((await bandwidthGenerator.credentialGenerationEnabled())).to.equal(false);
|
||||
|
||||
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'CredentialGenerationSwitch', {
|
||||
Enabled: false,
|
||||
});
|
||||
|
||||
tx = await bandwidthGenerator.credentialGenerationSwitch(true);
|
||||
expect((await bandwidthGenerator.credentialGenerationEnabled())).to.equal(true);
|
||||
|
||||
await expectEvent.inTransaction(tx.tx, bandwidthGenerator, 'CredentialGenerationSwitch', {
|
||||
Enabled: true,
|
||||
});
|
||||
});
|
||||
it("cannot generate credentials if switch = false", async () => {
|
||||
await bandwidthGenerator.credentialGenerationSwitch(false);
|
||||
await expectRevert(
|
||||
bandwidthGenerator.generateBasicBandwidthCredential(
|
||||
oneToken,
|
||||
15,
|
||||
[0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba,
|
||||
0x39, 0x53, 0x0a, 0x00, 0xea, 0xe2, 0xa5, 0xaa, 0xc8, 0x14, 0x42, 0x09, 0xcc, 0xac, 0x91, 0x7a, 0xe5, 0x6b, 0xf4, 0xa9, 0x58, 0x95, 0x44, 0xcb, 0x00, 0x20, 0xf9, 0x2f, 0xee, 0x35, 0xa3, 0xba],
|
||||
cosmosRecipient,
|
||||
{ from: user }
|
||||
), "BandwidthGenerator: credential generation isn't currently enabled"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+42
-16
@@ -4,7 +4,10 @@
|
||||
[package]
|
||||
name = "nym-gateway"
|
||||
version = "1.0.2"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
authors = [
|
||||
"Dave Hrycyszyn <futurechimp@users.noreply.github.com>",
|
||||
"Jędrzej Stuczyński <andrew@nymtech.net>",
|
||||
]
|
||||
description = "Implementation of the Nym Mixnet Gateway"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
@@ -28,24 +31,31 @@ once_cell = "1.7.2"
|
||||
pretty_env_logger = "0.4"
|
||||
rand = "0.7"
|
||||
serde = { version = "1.0.104", features = ["derive"] }
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
subtle-encoding = { version = "0.5", features = ["bech32-preview"]}
|
||||
sqlx = { version = "0.5", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
"macros",
|
||||
"migrate",
|
||||
] }
|
||||
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 = { version = "1.19.1", features = [
|
||||
"rt-multi-thread",
|
||||
"net",
|
||||
"signal",
|
||||
"fs",
|
||||
] }
|
||||
tokio-stream = { version = "0.1.9", features = ["fs"] }
|
||||
tokio-tungstenite = "0.14"
|
||||
tokio-util = { version = "0.7.3", features = [ "codec" ] }
|
||||
url = { version = "2.2", features = [ "serde" ] }
|
||||
web3 = "0.17.0"
|
||||
tokio-util = { version = "0.7.3", features = ["codec"] }
|
||||
url = { version = "2.2", features = ["serde"] }
|
||||
|
||||
# internal
|
||||
coconut-interface = { path = "../common/coconut-interface" , optional = true}
|
||||
coconut-interface = { path = "../common/coconut-interface", optional = true }
|
||||
credentials = { path = "../common/credentials" }
|
||||
config = { path = "../common/config" }
|
||||
crypto = { path = "../common/crypto" }
|
||||
bandwidth-claim-contract = { path = "../common/bandwidth-claim-contract" }
|
||||
gateway-requests = { path = "gateway-requests" }
|
||||
gateway-client = { path = "../common/client-libs/gateway-client" }
|
||||
mixnet-client = { path = "../common/client-libs/mixnet-client" }
|
||||
mixnode-common = { path = "../common/mixnode-common" }
|
||||
network-defaults = { path = "../common/network-defaults" }
|
||||
@@ -53,14 +63,30 @@ nymsphinx = { path = "../common/nymsphinx" }
|
||||
pemstore = { path = "../common/pemstore" }
|
||||
statistics-common = { path = "../common/statistics" }
|
||||
validator-api-requests = { path = "../validator-api/validator-api-requests" }
|
||||
validator-client = { path = "../common/client-libs/validator-client", features = ["nymd-client"] }
|
||||
validator-client = { path = "../common/client-libs/validator-client", features = [
|
||||
"nymd-client",
|
||||
] }
|
||||
version-checker = { path = "../common/version-checker" }
|
||||
|
||||
[features]
|
||||
coconut = ["coconut-interface", "gateway-requests/coconut", "gateway-client/coconut", "credentials/coconut", "validator-api-requests/coconut"]
|
||||
eth = []
|
||||
coconut = [
|
||||
"coconut-interface",
|
||||
"gateway-requests/coconut",
|
||||
"credentials/coconut",
|
||||
"validator-api-requests/coconut",
|
||||
]
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { version = "1.19.1", features = ["rt-multi-thread", "macros"] }
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
vergen = { version = "5", default-features = false, features = ["build", "git", "rustc", "cargo"] }
|
||||
sqlx = { version = "0.5", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
"macros",
|
||||
"migrate",
|
||||
] }
|
||||
vergen = { version = "5", default-features = false, features = [
|
||||
"build",
|
||||
"git",
|
||||
"rustc",
|
||||
"cargo",
|
||||
] }
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ async fn main() {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let database_path = format!("{}/gateway-example.sqlite", out_dir);
|
||||
|
||||
let mut conn = SqliteConnection::connect(&*format!("sqlite://{}?mode=rwc", database_path))
|
||||
let mut conn = SqliteConnection::connect(&format!("sqlite://{}?mode=rwc", database_path))
|
||||
.await
|
||||
.expect("Failed to create SQLx database connection");
|
||||
|
||||
|
||||
@@ -52,15 +52,10 @@ pub struct Init {
|
||||
mnemonic: Option<String>,
|
||||
|
||||
/// Set this gateway to work in a enabled credentials mode that would disallow clients to bypass bandwidth credential requirement
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
#[clap(long)]
|
||||
enabled_statistics: Option<bool>,
|
||||
@@ -83,12 +78,9 @@ impl From<Init> for OverrideConfig {
|
||||
validators: init_config.validators,
|
||||
mnemonic: init_config.mnemonic,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: init_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Some(init_config.eth_endpoint),
|
||||
|
||||
enabled_statistics: init_config.enabled_statistics,
|
||||
statistics_service_url: init_config.statistics_service_url,
|
||||
}
|
||||
@@ -177,10 +169,8 @@ mod tests {
|
||||
mnemonic: None,
|
||||
statistics_service_url: None,
|
||||
enabled_statistics: None,
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: None,
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: "".to_string(),
|
||||
};
|
||||
std::env::set_var(BECH32_PREFIX, "n");
|
||||
|
||||
|
||||
@@ -18,8 +18,6 @@ pub(crate) mod run;
|
||||
pub(crate) mod sign;
|
||||
pub(crate) mod upgrade;
|
||||
|
||||
#[cfg(all(not(feature = "eth"), not(feature = "coconut")))]
|
||||
const DEFAULT_ETH_ENDPOINT: &str = "https://rinkeby.infura.io/v3/00000000000000000000000000000000";
|
||||
#[derive(Subcommand)]
|
||||
pub(crate) enum Commands {
|
||||
/// Initialise the gateway
|
||||
@@ -52,11 +50,8 @@ pub(crate) struct OverrideConfig {
|
||||
validators: Option<String>,
|
||||
mnemonic: Option<String>,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
pub(crate) async fn execute(args: Cli) {
|
||||
@@ -141,29 +136,13 @@ pub(crate) fn override_config(mut config: Config, args: OverrideConfig) -> Confi
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(all(not(feature = "eth"), not(feature = "coconut")))]
|
||||
{
|
||||
config = config.with_eth_endpoint(String::from(DEFAULT_ETH_ENDPOINT));
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
{
|
||||
if let Some(enabled_credentials_mode) = args.enabled_credentials_mode {
|
||||
config = config.with_disabled_credentials_mode(!enabled_credentials_mode);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
{
|
||||
if let Some(raw_validators) = args.validators {
|
||||
config = config.with_custom_validator_nymd(parse_validators(&raw_validators));
|
||||
}
|
||||
|
||||
if let Some(eth_endpoint) = args.eth_endpoint {
|
||||
config = config.with_eth_endpoint(eth_endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
|
||||
@@ -52,15 +52,10 @@ pub struct Run {
|
||||
mnemonic: Option<String>,
|
||||
|
||||
/// Set this gateway to work in a enabled credentials mode that would disallow clients to bypass bandwidth credential requirement
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
#[clap(long)]
|
||||
enabled_credentials_mode: Option<bool>,
|
||||
|
||||
/// URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
#[clap(long)]
|
||||
eth_endpoint: Option<String>,
|
||||
|
||||
/// Enable/disable gateway anonymized statistics that get sent to a statistics aggregator server
|
||||
#[clap(long)]
|
||||
enabled_statistics: Option<bool>,
|
||||
@@ -83,12 +78,9 @@ impl From<Run> for OverrideConfig {
|
||||
validators: run_config.validators,
|
||||
mnemonic: run_config.mnemonic,
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
enabled_credentials_mode: run_config.enabled_credentials_mode,
|
||||
|
||||
#[cfg(all(feature = "eth", not(feature = "coconut")))]
|
||||
eth_endpoint: run_config.eth_endpoint,
|
||||
|
||||
enabled_statistics: run_config.enabled_statistics,
|
||||
statistics_service_url: run_config.statistics_service_url,
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "eth", feature = "coconut"))]
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn with_disabled_credentials_mode(mut self, disabled_credentials_mode: bool) -> Self {
|
||||
self.gateway.disabled_credentials_mode = disabled_credentials_mode;
|
||||
self
|
||||
@@ -158,12 +158,6 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn with_eth_endpoint(mut self, eth_endpoint: String) -> Self {
|
||||
self.gateway.eth_endpoint = eth_endpoint;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_listening_address<S: Into<String>>(mut self, listening_address: S) -> Self {
|
||||
let listening_address_string = listening_address.into();
|
||||
if let Ok(ip_addr) = listening_address_string.parse() {
|
||||
@@ -237,11 +231,6 @@ impl Config {
|
||||
self.gateway.public_sphinx_key_file.clone()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn get_eth_endpoint(&self) -> String {
|
||||
self.gateway.eth_endpoint.clone()
|
||||
}
|
||||
|
||||
pub fn get_enabled_statistics(&self) -> bool {
|
||||
self.gateway.enabled_statistics
|
||||
}
|
||||
@@ -254,10 +243,12 @@ impl Config {
|
||||
self.gateway.validator_api_urls.clone()
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn get_validator_nymd_endpoints(&self) -> Vec<Url> {
|
||||
self.gateway.validator_nymd_urls.clone()
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub fn get_cosmos_mnemonic(&self) -> bip39::Mnemonic {
|
||||
self.gateway.cosmos_mnemonic.clone()
|
||||
}
|
||||
@@ -357,10 +348,6 @@ pub struct Gateway {
|
||||
/// Path to file containing public sphinx key.
|
||||
public_sphinx_key_file: PathBuf,
|
||||
|
||||
/// Address to an Ethereum full node.
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// Wheather gateway collects and sends anonymized statistics
|
||||
enabled_statistics: bool,
|
||||
|
||||
@@ -424,8 +411,6 @@ impl Default for Gateway {
|
||||
public_identity_key_file: Default::default(),
|
||||
private_sphinx_key_file: Default::default(),
|
||||
public_sphinx_key_file: Default::default(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_endpoint: "".to_string(),
|
||||
enabled_statistics: false,
|
||||
statistics_service_url: Url::from_str("http://127.0.0.1").unwrap(),
|
||||
validator_api_urls: vec![],
|
||||
|
||||
@@ -38,9 +38,6 @@ private_sphinx_key_file = '{{ gateway.private_sphinx_key_file }}'
|
||||
# Path to file containing public sphinx key.
|
||||
public_sphinx_key_file = '{{ gateway.public_sphinx_key_file }}'
|
||||
|
||||
# Addess to an Ethereum full node.
|
||||
eth_endpoint = '{{ gateway.eth_endpoint }}'
|
||||
|
||||
##### additional gateway config options #####
|
||||
|
||||
# Optional address announced to the directory server for the clients to connect to.
|
||||
|
||||
@@ -51,18 +51,6 @@ pub(crate) enum RequestHandlingError {
|
||||
#[error("Nymd Error - {0}")]
|
||||
NymdError(#[from] validator_client::nymd::error::NymdError),
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[error("Ethereum web3 error")]
|
||||
Web3Error(#[from] web3::Error),
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[error("Ethereum ABI error")]
|
||||
EthAbiError(#[from] web3::ethabi::Error),
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
#[error("Ethereum contract error")]
|
||||
EthContractError(#[from] web3::contract::Error),
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
#[error("Validator API error - {0}")]
|
||||
APIError(#[from] validator_client::ValidatorClientError),
|
||||
@@ -287,18 +275,6 @@ where
|
||||
String::from("gateway"),
|
||||
));
|
||||
}
|
||||
debug!("Verifying Ethereum for token burn...");
|
||||
let gateway_owner = self
|
||||
.inner
|
||||
.erc20_bridge
|
||||
.verify_eth_events(credential.verification_key())
|
||||
.await?;
|
||||
self.inner
|
||||
.erc20_bridge
|
||||
.verify_gateway_owner(gateway_owner, &credential.gateway_identity())
|
||||
.await?;
|
||||
debug!("Claim the token on Cosmos, to make sure it's not spent twice...");
|
||||
self.inner.erc20_bridge.claim_token(&credential).await?;
|
||||
|
||||
let bandwidth = Bandwidth::from(credential);
|
||||
let bandwidth_value = bandwidth.value();
|
||||
|
||||
@@ -6,129 +6,15 @@ use bandwidth_claim_contract::msg::ExecuteMsg;
|
||||
use bandwidth_claim_contract::payment::LinkPaymentData;
|
||||
use credentials::token::bandwidth::TokenCredential;
|
||||
use crypto::asymmetric::identity::{PublicKey, Signature, SIGNATURE_LENGTH};
|
||||
use gateway_client::bandwidth::eth_contract;
|
||||
use network_defaults::{ETH_EVENT_NAME, ETH_MIN_BLOCK_DEPTH};
|
||||
use std::str::FromStr;
|
||||
use validator_client::nymd::{AccountId, NymdClient, SigningNymdClient};
|
||||
use web3::contract::tokens::Detokenize;
|
||||
use web3::contract::{Contract, Error};
|
||||
use web3::ethabi::Token;
|
||||
use web3::transports::Http;
|
||||
use web3::types::{BlockNumber, FilterBuilder, H256};
|
||||
use web3::Web3;
|
||||
|
||||
pub(crate) struct ERC20Bridge {
|
||||
// This is needed because web3's Contract doesn't sufficiently expose it's eth interface
|
||||
web3: Web3<Http>,
|
||||
contract: Contract<Http>,
|
||||
nymd_client: NymdClient<SigningNymdClient>,
|
||||
}
|
||||
|
||||
impl ERC20Bridge {
|
||||
pub fn new(eth_endpoint: String, nymd_client: NymdClient<SigningNymdClient>) -> Self {
|
||||
let transport = Http::new(ð_endpoint).expect("Invalid Ethereum endpoint");
|
||||
let web3 = Web3::new(transport);
|
||||
|
||||
ERC20Bridge {
|
||||
contract: eth_contract(web3.clone()),
|
||||
web3,
|
||||
nymd_client,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn verify_eth_events(
|
||||
&self,
|
||||
verification_key: PublicKey,
|
||||
) -> Result<String, RequestHandlingError> {
|
||||
// It's safe to unwrap here, as we are guarded by a unit test that checks the event
|
||||
// name constant against the contract abi
|
||||
let event = self.contract.abi().event(ETH_EVENT_NAME).unwrap();
|
||||
let latest_block = self.web3.eth().block_number().await?;
|
||||
let check_until = if cfg!(debug_assertions) {
|
||||
latest_block
|
||||
} else {
|
||||
latest_block - ETH_MIN_BLOCK_DEPTH
|
||||
};
|
||||
let filter = FilterBuilder::default()
|
||||
.address(vec![self.contract.address()])
|
||||
.topics(
|
||||
Some(vec![event.signature()]),
|
||||
Some(vec![H256::from(verification_key.to_bytes())]),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.from_block(BlockNumber::Earliest)
|
||||
.to_block(BlockNumber::Number(check_until))
|
||||
.build();
|
||||
// Get only the first event that checks out. If the client burns more tokens with the
|
||||
// same verification key, those tokens would be lost
|
||||
for l in self.web3.eth().logs(filter).await? {
|
||||
let log = event.parse_log(web3::ethabi::RawLog {
|
||||
topics: l.topics,
|
||||
data: l.data.0,
|
||||
})?;
|
||||
let burned_event =
|
||||
Burned::from_tokens(log.params.into_iter().map(|x| x.value).collect::<Vec<_>>())?;
|
||||
if burned_event.verify(verification_key) {
|
||||
return Ok(burned_event.cosmos_recipient);
|
||||
}
|
||||
}
|
||||
|
||||
Err(RequestHandlingError::InvalidBandwidthCredential(
|
||||
String::from("gateway"),
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) async fn verify_gateway_owner(
|
||||
&self,
|
||||
gateway_owner: String,
|
||||
gateway_identity: &PublicKey,
|
||||
) -> Result<(), RequestHandlingError> {
|
||||
let owner_address = AccountId::from_str(&gateway_owner).map_err(|_| {
|
||||
RequestHandlingError::InvalidBandwidthCredential(String::from("gateway"))
|
||||
})?;
|
||||
let gateway_bond = self
|
||||
.nymd_client
|
||||
.owns_gateway(&owner_address)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
RequestHandlingError::InvalidBandwidthCredential(String::from("gateway"))
|
||||
})?;
|
||||
if gateway_bond.gateway.identity_key == gateway_identity.to_base58_string() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(RequestHandlingError::InvalidBandwidthCredential(
|
||||
String::from("gateway"),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn claim_token(
|
||||
&self,
|
||||
credential: &TokenCredential,
|
||||
) -> Result<(), RequestHandlingError> {
|
||||
let bandwidth_claim_contract_address = self.nymd_client.bandwidth_claim_contract_address();
|
||||
let req = ExecuteMsg::LinkPayment {
|
||||
data: LinkPaymentData::new(
|
||||
credential.verification_key().to_bytes(),
|
||||
credential.gateway_identity().to_bytes(),
|
||||
credential.bandwidth(),
|
||||
credential.signature_bytes(),
|
||||
),
|
||||
};
|
||||
self.nymd_client
|
||||
.execute(
|
||||
bandwidth_claim_contract_address,
|
||||
&req,
|
||||
Default::default(),
|
||||
"Linking payment",
|
||||
vec![],
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Burned {
|
||||
/// The bandwidth bought by the client
|
||||
|
||||
@@ -29,9 +29,6 @@ use thiserror::Error;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio_tungstenite::tungstenite::{protocol::Message, Error as WsError};
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use crate::node::client_handling::websocket::connection_handler::eth_events::ERC20Bridge;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum InitialAuthenticationError {
|
||||
#[error("Internal gateway storage error")]
|
||||
@@ -76,8 +73,6 @@ pub(crate) struct FreshHandler<R, S, St> {
|
||||
pub(crate) socket_connection: SocketStream<S>,
|
||||
pub(crate) storage: St,
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) erc20_bridge: Arc<ERC20Bridge>,
|
||||
#[cfg(feature = "coconut")]
|
||||
pub(crate) coconut_verifier: Arc<CoconutVerifier>,
|
||||
}
|
||||
@@ -101,7 +96,6 @@ where
|
||||
storage: St,
|
||||
active_clients_store: ActiveClientsStore,
|
||||
#[cfg(feature = "coconut")] coconut_verifier: Arc<CoconutVerifier>,
|
||||
#[cfg(not(feature = "coconut"))] erc20_bridge: Arc<ERC20Bridge>,
|
||||
) -> Self {
|
||||
FreshHandler {
|
||||
rng,
|
||||
@@ -113,8 +107,6 @@ where
|
||||
storage,
|
||||
#[cfg(feature = "coconut")]
|
||||
coconut_verifier,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_bridge,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@ pub(crate) use self::fresh::FreshHandler;
|
||||
mod authenticated;
|
||||
#[cfg(feature = "coconut")]
|
||||
pub(crate) mod coconut;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub(crate) mod eth_events;
|
||||
mod fresh;
|
||||
|
||||
//// TODO: note for my future self to consider the following idea:
|
||||
|
||||
@@ -16,9 +16,6 @@ use tokio::task::JoinHandle;
|
||||
#[cfg(feature = "coconut")]
|
||||
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use crate::node::client_handling::websocket::connection_handler::eth_events::ERC20Bridge;
|
||||
|
||||
pub(crate) struct Listener {
|
||||
address: SocketAddr,
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
@@ -26,8 +23,6 @@ pub(crate) struct Listener {
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
pub(crate) coconut_verifier: Arc<CoconutVerifier>,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_bridge: Arc<ERC20Bridge>,
|
||||
}
|
||||
|
||||
impl Listener {
|
||||
@@ -36,7 +31,6 @@ impl Listener {
|
||||
local_identity: Arc<identity::KeyPair>,
|
||||
disabled_credentials_mode: bool,
|
||||
#[cfg(feature = "coconut")] coconut_verifier: Arc<CoconutVerifier>,
|
||||
#[cfg(not(feature = "coconut"))] erc20_bridge: ERC20Bridge,
|
||||
) -> Self {
|
||||
Listener {
|
||||
address,
|
||||
@@ -44,8 +38,6 @@ impl Listener {
|
||||
disabled_credentials_mode,
|
||||
#[cfg(feature = "coconut")]
|
||||
coconut_verifier,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_bridge: Arc::new(erc20_bridge),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,8 +76,6 @@ impl Listener {
|
||||
active_clients_store.clone(),
|
||||
#[cfg(feature = "coconut")]
|
||||
Arc::clone(&self.coconut_verifier),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
Arc::clone(&self.erc20_bridge),
|
||||
);
|
||||
tokio::spawn(async move { handle.start_handling().await });
|
||||
}
|
||||
|
||||
+4
-11
@@ -12,6 +12,7 @@ use crate::node::storage::Storage;
|
||||
use crypto::asymmetric::{encryption, identity};
|
||||
use log::*;
|
||||
use mixnet_client::forwarder::{MixForwardingSender, PacketForwarder};
|
||||
#[cfg(feature = "coconut")]
|
||||
use network_defaults::NymNetworkDetails;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
@@ -23,10 +24,9 @@ use std::sync::Arc;
|
||||
use crate::config::persistence::pathfinder::GatewayPathfinder;
|
||||
#[cfg(feature = "coconut")]
|
||||
use crate::node::client_handling::websocket::connection_handler::coconut::CoconutVerifier;
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
use crate::node::client_handling::websocket::connection_handler::eth_events::ERC20Bridge;
|
||||
#[cfg(feature = "coconut")]
|
||||
use credentials::obtain_aggregate_verification_key;
|
||||
#[cfg(feature = "coconut")]
|
||||
use validator_client::nymd;
|
||||
|
||||
use self::storage::PersistentStorage;
|
||||
@@ -181,7 +181,6 @@ where
|
||||
forwarding_channel: MixForwardingSender,
|
||||
active_clients_store: ActiveClientsStore,
|
||||
#[cfg(feature = "coconut")] coconut_verifier: Arc<CoconutVerifier>,
|
||||
#[cfg(not(feature = "coconut"))] erc20_bridge: ERC20Bridge,
|
||||
) {
|
||||
info!("Starting client [web]socket listener...");
|
||||
|
||||
@@ -196,8 +195,6 @@ where
|
||||
self.config.get_disabled_credentials_mode(),
|
||||
#[cfg(feature = "coconut")]
|
||||
coconut_verifier,
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_bridge,
|
||||
)
|
||||
.start(
|
||||
forwarding_channel,
|
||||
@@ -241,6 +238,7 @@ where
|
||||
validator_client::ApiClient::new(validator_api.clone())
|
||||
}
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
fn random_nymd_client(
|
||||
&self,
|
||||
) -> validator_client::nymd::NymdClient<validator_client::nymd::SigningNymdClient> {
|
||||
@@ -311,7 +309,7 @@ where
|
||||
obtain_aggregate_verification_key(&self.config.get_validator_api_endpoints())
|
||||
.await
|
||||
.expect("failed to contact validators to obtain their verification keys");
|
||||
|
||||
#[cfg(feature = "coconut")]
|
||||
let nymd_client = self.random_nymd_client();
|
||||
#[cfg(feature = "coconut")]
|
||||
let coconut_verifier = CoconutVerifier::new(
|
||||
@@ -322,9 +320,6 @@ where
|
||||
)
|
||||
.expect("Could not create coconut verifier");
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let erc20_bridge = ERC20Bridge::new(self.config.get_eth_endpoint(), nymd_client);
|
||||
|
||||
let mix_forwarding_channel = self.start_packet_forwarder();
|
||||
|
||||
let active_clients_store = ActiveClientsStore::new();
|
||||
@@ -351,8 +346,6 @@ where
|
||||
active_clients_store,
|
||||
#[cfg(feature = "coconut")]
|
||||
Arc::new(coconut_verifier),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
erc20_bridge,
|
||||
);
|
||||
|
||||
info!("Finished nym gateway startup procedure - it should now be able to receive mix and client traffic!");
|
||||
|
||||
Generated
+1
-18
@@ -732,7 +732,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"handlebars",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"serde",
|
||||
@@ -916,7 +915,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"log",
|
||||
"nymcoconut",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@@ -993,7 +991,6 @@ dependencies = [
|
||||
"blake3",
|
||||
"bs58",
|
||||
"cipher 0.4.3",
|
||||
"config",
|
||||
"ctr 0.9.1",
|
||||
"digest 0.10.3",
|
||||
"ed25519-dalek",
|
||||
@@ -1915,7 +1912,6 @@ dependencies = [
|
||||
"futures",
|
||||
"gateway-requests",
|
||||
"getrandom 0.2.7",
|
||||
"json",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"nymsphinx",
|
||||
@@ -2873,12 +2869,6 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "json"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
|
||||
|
||||
[[package]]
|
||||
name = "json-patch"
|
||||
version = "0.2.6"
|
||||
@@ -3209,7 +3199,6 @@ dependencies = [
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"time 0.3.11",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5233,7 +5222,6 @@ name = "socks5-requests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nymsphinx-addressing",
|
||||
"ordered-buffer",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
@@ -5637,7 +5625,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"tokio",
|
||||
"tokio-util 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6404,7 +6391,6 @@ dependencies = [
|
||||
"mixnet-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6498,7 +6484,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vesting-contract"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
@@ -6513,13 +6499,10 @@ dependencies = [
|
||||
name = "vesting-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"config",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"mixnet-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -16,10 +16,6 @@ use crate::{
|
||||
|
||||
static SOCKS5_CONFIG_ID: &str = "nym-connect";
|
||||
|
||||
const DEFAULT_ETH_ENDPOINT: &str = "https://rinkeby.infura.io/v3/00000000000000000000000000000000";
|
||||
const DEFAULT_ETH_PRIVATE_KEY: &str =
|
||||
"0000000000000000000000000000000000000000000000000000000000000001";
|
||||
|
||||
pub fn socks5_config_id_appended_with(gateway_id: &str) -> Result<String> {
|
||||
use std::fmt::Write as _;
|
||||
let mut id = SOCKS5_CONFIG_ID.to_string();
|
||||
@@ -125,15 +121,6 @@ pub async fn init_socks5_config(provider_address: String, chosen_gateway_id: Str
|
||||
log::trace!("Creating config for id: {}", id);
|
||||
let mut config = Config::new(id.as_str(), &provider_address);
|
||||
|
||||
// As far as I'm aware, these two are not used, they are only set because the socks5 init code
|
||||
// requires them for initialising the bandwidth controller.
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_endpoint(DEFAULT_ETH_ENDPOINT);
|
||||
config
|
||||
.get_base_mut()
|
||||
.with_eth_private_key(DEFAULT_ETH_PRIVATE_KEY);
|
||||
|
||||
if let Ok(raw_validators) = std::env::var(config_common::defaults::var_names::API_VALIDATOR) {
|
||||
config
|
||||
.get_base_mut()
|
||||
|
||||
Generated
+2
-24
@@ -769,7 +769,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"handlebars",
|
||||
"humantime-serde",
|
||||
"log",
|
||||
"network-defaults",
|
||||
"serde",
|
||||
@@ -1482,7 +1481,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime 1.3.0",
|
||||
"humantime",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
@@ -2316,22 +2315,6 @@ dependencies = [
|
||||
"quick-error 1.2.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "humantime-serde"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac34a56cfd4acddb469cc7fff187ed5ac36f498ba085caf8bbc725e3ff474058"
|
||||
dependencies = [
|
||||
"humantime 2.1.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.17"
|
||||
@@ -2796,7 +2779,6 @@ dependencies = [
|
||||
"serde_repr",
|
||||
"thiserror",
|
||||
"time 0.3.7",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5550,7 +5532,6 @@ dependencies = [
|
||||
"mixnet-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -5620,7 +5601,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "vesting-contract"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
@@ -5635,13 +5616,10 @@ dependencies = [
|
||||
name = "vesting-contract-common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"config",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"mixnet-contract-common",
|
||||
"schemars",
|
||||
"serde",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -14,7 +14,6 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "chrono"]}
|
||||
thiserror = "1"
|
||||
tokio = { version = "1.4", features = [ "net", "rt-multi-thread", "macros", "time" ] }
|
||||
tokio-tungstenite = "0.14"
|
||||
|
||||
statistics-common = { path = "../../common/statistics" }
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ async fn main() {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let database_path = format!("{}/network-statistics-example.sqlite", out_dir);
|
||||
|
||||
let mut conn = SqliteConnection::connect(&*format!("sqlite://{}?mode=rwc", database_path))
|
||||
let mut conn = SqliteConnection::connect(&format!("sqlite://{}?mode=rwc", database_path))
|
||||
.await
|
||||
.expect("Failed to create SQLx database connection");
|
||||
|
||||
|
||||
@@ -45,12 +45,11 @@ tokio = { version = "1.19", features = [
|
||||
tokio-stream = "0.1.9"
|
||||
url = "2.2"
|
||||
|
||||
ts-rs = "6.1"
|
||||
ts-rs = {version = "6.1", optional = true}
|
||||
|
||||
anyhow = "1.0"
|
||||
getset = "0.1.1"
|
||||
|
||||
rocket_sync_db_pools = { version = "0.1.0-rc.2", default-features = false }
|
||||
sqlx = { version = "0.6.1", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
@@ -94,17 +93,17 @@ coconut = [
|
||||
"nymcoconut",
|
||||
]
|
||||
no-reward = []
|
||||
generate-ts = []
|
||||
generate-ts = ["ts-rs"]
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { version = "1.19.1", features = ["rt-multi-thread", "macros"] }
|
||||
sqlx = { version = "0.5", features = [
|
||||
sqlx = { version = "0.6.2", features = [
|
||||
"runtime-tokio-rustls",
|
||||
"sqlite",
|
||||
"macros",
|
||||
"migrate",
|
||||
] }
|
||||
vergen = { version = "5", default-features = false, features = [
|
||||
vergen = { version = "7", default-features = false, features = [
|
||||
"build",
|
||||
"git",
|
||||
"rustc",
|
||||
@@ -112,6 +111,5 @@ vergen = { version = "5", default-features = false, features = [
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
attohttpc = { version = "0.18.0", features = ["json"] }
|
||||
cw3 = "0.13.2"
|
||||
cw-utils = "0.13.2"
|
||||
|
||||
@@ -7,7 +7,7 @@ async fn main() {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let database_path = format!("{}/validator-api-example.sqlite", out_dir);
|
||||
|
||||
let mut conn = SqliteConnection::connect(&*format!("sqlite://{}?mode=rwc", database_path))
|
||||
let mut conn = SqliteConnection::connect(&format!("sqlite://{}?mode=rwc", database_path))
|
||||
.await
|
||||
.expect("Failed to create SQLx database connection");
|
||||
|
||||
|
||||
@@ -23,12 +23,6 @@ const DEFAULT_GATEWAY_PING_INTERVAL: Duration = Duration::from_secs(60);
|
||||
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5 * 60);
|
||||
const DEFAULT_GATEWAY_CONNECTION_TIMEOUT: Duration = Duration::from_millis(2_500);
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
const DEFAULT_ETH_ENDPOINT: &str = "https://rinkeby.infura.io/v3/00000000000000000000000000000000";
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
const DEFAULT_ETH_PRIVATE_KEY: &str =
|
||||
"0000000000000000000000000000000000000000000000000000000000000001";
|
||||
|
||||
const DEFAULT_TEST_ROUTES: usize = 3;
|
||||
const DEFAULT_MINIMUM_TEST_ROUTES: usize = 1;
|
||||
const DEFAULT_ROUTE_TEST_PACKETS: usize = 1000;
|
||||
@@ -163,14 +157,6 @@ pub struct NetworkMonitor {
|
||||
/// Path to the database containing bandwidth credentials of this client.
|
||||
credentials_database_path: PathBuf,
|
||||
|
||||
/// Ethereum private key.
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_private_key: String,
|
||||
|
||||
/// Addess to an Ethereum full node.
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_endpoint: String,
|
||||
|
||||
/// Desired number of test routes to be constructed (and working) during a monitor test run.
|
||||
test_routes: usize,
|
||||
|
||||
@@ -209,10 +195,6 @@ impl Default for NetworkMonitor {
|
||||
gateway_connection_timeout: DEFAULT_GATEWAY_CONNECTION_TIMEOUT,
|
||||
packet_delivery_timeout: DEFAULT_PACKET_DELIVERY_TIMEOUT,
|
||||
credentials_database_path: Self::default_credentials_database_path(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_private_key: DEFAULT_ETH_PRIVATE_KEY.to_string(),
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
eth_endpoint: DEFAULT_ETH_ENDPOINT.to_string(),
|
||||
test_routes: DEFAULT_TEST_ROUTES,
|
||||
minimum_test_routes: DEFAULT_MINIMUM_TEST_ROUTES,
|
||||
route_test_packets: DEFAULT_ROUTE_TEST_PACKETS,
|
||||
@@ -377,18 +359,6 @@ impl Config {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn with_eth_private_key(mut self, eth_private_key: String) -> Self {
|
||||
self.network_monitor.eth_private_key = eth_private_key;
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn with_eth_endpoint(mut self, eth_endpoint: String) -> Self {
|
||||
self.network_monitor.eth_endpoint = eth_endpoint;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> String {
|
||||
self.base.id.clone()
|
||||
}
|
||||
@@ -410,16 +380,6 @@ impl Config {
|
||||
self.network_monitor.credentials_database_path.clone()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn get_network_monitor_eth_private_key(&self) -> String {
|
||||
self.network_monitor.eth_private_key.clone()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
pub fn get_network_monitor_eth_endpoint(&self) -> String {
|
||||
self.network_monitor.eth_endpoint.clone()
|
||||
}
|
||||
|
||||
// TODO: Remove if still unused
|
||||
#[allow(dead_code)]
|
||||
pub fn get_rewarding_enabled(&self) -> bool {
|
||||
|
||||
@@ -57,12 +57,6 @@ packet_delivery_timeout = '{{ network_monitor.packet_delivery_timeout }}'
|
||||
|
||||
credentials_database_path = '{{ network_monitor.credentials_database_path }}'
|
||||
|
||||
# Ethereum private key.
|
||||
eth_private_key = '{{ network_monitor.eth_private_key }}'
|
||||
|
||||
# Addess to an Ethereum full node.
|
||||
eth_endpoint = '{{ network_monitor.eth_endpoint }}'
|
||||
|
||||
# Desired number of test routes to be constructed (and working) during a monitor test run.
|
||||
test_routes = {{ network_monitor.test_routes }}
|
||||
|
||||
|
||||
@@ -73,11 +73,6 @@ const KEYPAIR_ARG: &str = "keypair";
|
||||
#[cfg(feature = "coconut")]
|
||||
const COCONUT_ENABLED: &str = "enable-coconut";
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
const ETH_ENDPOINT: &str = "eth_endpoint";
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
const ETH_PRIVATE_KEY: &str = "eth_private_key";
|
||||
|
||||
const REWARDING_MONITOR_THRESHOLD_ARG: &str = "monitor-threshold";
|
||||
|
||||
const MIN_MIXNODE_RELIABILITY_ARG: &str = "min_mixnode_reliability";
|
||||
@@ -199,20 +194,6 @@ fn parse_args() -> ArgMatches {
|
||||
.requires_all(&[KEYPAIR_ARG, MNEMONIC_ARG, API_VALIDATORS_ARG])
|
||||
.long(COCONUT_ENABLED),
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
let base_app = base_app.arg(
|
||||
Arg::with_name(ETH_ENDPOINT)
|
||||
.help("URL of an Ethereum full node that we want to use for getting bandwidth tokens from ERC20 tokens")
|
||||
.takes_value(true)
|
||||
.long(ETH_ENDPOINT),
|
||||
).arg(
|
||||
Arg::with_name(ETH_PRIVATE_KEY)
|
||||
.help("Ethereum private key used for obtaining bandwidth tokens from ERC20 tokens")
|
||||
.takes_value(true)
|
||||
.long(ETH_PRIVATE_KEY),
|
||||
);
|
||||
|
||||
base_app.get_matches()
|
||||
}
|
||||
|
||||
@@ -366,16 +347,6 @@ fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
|
||||
config = config.with_keypair_path(keypair_path.into())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
if let Some(eth_private_key) = matches.value_of("eth_private_key") {
|
||||
config = config.with_eth_private_key(String::from(eth_private_key));
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "coconut"))]
|
||||
if let Some(eth_endpoint) = matches.value_of("eth_endpoint") {
|
||||
config = config.with_eth_endpoint(String::from(eth_endpoint));
|
||||
}
|
||||
|
||||
if matches.is_present(ENABLED_CREDENTIALS_MODE_ARG_NAME) {
|
||||
config = config.with_disabled_credentials_mode(false)
|
||||
}
|
||||
|
||||
@@ -83,8 +83,6 @@ impl<'a> NetworkMonitorBuilder<'a> {
|
||||
let bandwidth_controller = BandwidthController::new(
|
||||
credential_storage::initialise_storage(self.config.get_credentials_database_path())
|
||||
.await,
|
||||
self.config.get_network_monitor_eth_endpoint(),
|
||||
self.config.get_network_monitor_eth_private_key(),
|
||||
)
|
||||
.expect("Could not create bandwidth controller");
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ cosmrs = { git = "https://github.com/neacsu/cosmos-rust", branch = "neacsu/feegr
|
||||
getset = "0.1.1"
|
||||
schemars = { version = "0.8", features = ["preserve_order"] }
|
||||
serde = "1.0"
|
||||
ts-rs = "6.1.2"
|
||||
ts-rs = {version = "6.1.2", optional = true}
|
||||
|
||||
coconut-interface = { path = "../../common/coconut-interface", optional = true }
|
||||
mixnet-contract-common = { path= ".../../../../common/cosmwasm-smart-contracts/mixnet-contract" }
|
||||
@@ -19,4 +19,4 @@ mixnet-contract-common = { path= ".../../../../common/cosmwasm-smart-contracts/m
|
||||
[features]
|
||||
default = []
|
||||
coconut = ["coconut-interface"]
|
||||
generate-ts = []
|
||||
generate-ts = ["ts-rs"]
|
||||
|
||||
Reference in New Issue
Block a user