Compare commits
119 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d3d5cc3424 | |||
| a834bb17f8 | |||
| cee6d8c308 | |||
| 142eaf533b | |||
| 42365769f8 | |||
| 9fc822298f | |||
| 75221cfd3e | |||
| 50f71a21e0 | |||
| cfa9ecfcc4 | |||
| 549b33cd91 | |||
| be46da9906 | |||
| 66d123312f | |||
| a29f3db5fb | |||
| f452d97979 | |||
| 3eb7710a12 | |||
| d1c9251904 | |||
| 62894e2b40 | |||
| bd7fd1a61c | |||
| 28f118c73b | |||
| 65699736ee | |||
| caba594c95 | |||
| 746d52d017 | |||
| e323c05b33 | |||
| abdf071448 | |||
| a6f2b0e8c8 | |||
| f78b4a1742 | |||
| d4fde7b788 | |||
| d5a2952ef9 | |||
| 206b6ba742 | |||
| 67449b1c19 | |||
| 8d6e5d4fff | |||
| f448355b35 | |||
| cf78af6b98 | |||
| d041cfe5c5 | |||
| 85b0b6d73d | |||
| 685019884f | |||
| cd9d4eebd3 | |||
| 78610c7e28 | |||
| 496870b5f6 | |||
| 7eac5e3529 | |||
| 4ad4072709 | |||
| bd3711892a | |||
| b5926def85 | |||
| 50cc8bd0bf | |||
| fb1b58b5fb | |||
| c3a9ceae52 | |||
| c92a7e3e35 | |||
| e152c9a99e | |||
| 78cce00adf | |||
| 55e00a9a38 | |||
| 6d1b26daeb | |||
| 2d9e34cc81 | |||
| 7232fd83d1 | |||
| 3816142479 | |||
| 112ecc2e4c | |||
| 2f0fbd5ebd | |||
| f45a803139 | |||
| 7e16932c4a | |||
| a0a44509af | |||
| 852ed78e5d | |||
| 0d52800569 | |||
| a1dd9e656d | |||
| 1ea78e8e97 | |||
| 46d68e5448 | |||
| 2c820ca0ea | |||
| 2c731cf048 | |||
| 9a1f28bd43 | |||
| b9493004aa | |||
| b27f806c40 | |||
| 08aaa8813e | |||
| dd9f4f24f1 | |||
| f6c2cab531 | |||
| 10ff165c18 | |||
| eec3cc4c47 | |||
| f4dd9a915d | |||
| dcfd46ecf0 | |||
| d331e75375 | |||
| bac0f24cf7 | |||
| 6bba371c90 | |||
| 404b043591 | |||
| e09b33baff | |||
| 82bfab48a5 | |||
| e8956603d7 | |||
| a81e7e6c53 | |||
| 723e30fb1d | |||
| 951f8e7a74 | |||
| 4462dae45c | |||
| 8bfe670c9d | |||
| 7ca801fff3 | |||
| 8a92cca448 | |||
| 4308f602ea | |||
| 9ea2eafb2c | |||
| 061aa6b7bd | |||
| 3ee1e541ff | |||
| 866309cedf | |||
| 2d57ed49e8 | |||
| a08cc64fc7 | |||
| 23892fec8c | |||
| d807f66944 | |||
| 0861304368 | |||
| 077ea25990 | |||
| 77679064de | |||
| 2052577174 | |||
| 24a859d03c | |||
| b898ad3e97 | |||
| af3a216f71 | |||
| 7866cb0ae8 | |||
| 40adedb5e1 | |||
| c1660c2b27 | |||
| 84b497ab20 | |||
| 7407872b71 | |||
| 14d5e112d0 | |||
| 620c5e1188 | |||
| 60c740f723 | |||
| 8d9387d3ac | |||
| e8bc2c7a01 | |||
| 0486cd2e63 | |||
| 604098844a | |||
| 65e35bd2b0 |
@@ -0,0 +1,21 @@
|
||||
name: ci-cargo-deny
|
||||
on: [workflow_dispatch]
|
||||
jobs:
|
||||
cargo-deny:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
checks:
|
||||
# - advisories
|
||||
- licenses
|
||||
- bans sources
|
||||
|
||||
continue-on-error: ${{ matrix.checks == 'licenses' }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
with:
|
||||
log-level: warn
|
||||
command: check ${{ matrix.checks }}
|
||||
argument: --all-features
|
||||
@@ -17,6 +17,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: install yarn in root
|
||||
run: cd ../.. yarn install
|
||||
|
||||
- name: Install npm
|
||||
run: npm install
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
target
|
||||
.env
|
||||
.env.dev
|
||||
envs/devnet.env
|
||||
/.vscode/settings.json
|
||||
validator/.vscode
|
||||
sample-configs/validator-config.toml
|
||||
|
||||
Generated
+122
-259
@@ -661,6 +661,22 @@ dependencies = [
|
||||
"syn 2.0.38",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-tungstenite"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1e9efbe14612da0a19fb983059a0b621e9cf6225d7018ecab4f9988215540dc"
|
||||
dependencies = [
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"log",
|
||||
"pin-project-lite 0.2.13",
|
||||
"rustls-native-certs",
|
||||
"tokio",
|
||||
"tokio-rustls 0.24.1",
|
||||
"tungstenite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asynchronous-codec"
|
||||
version = "0.6.2"
|
||||
@@ -674,15 +690,6 @@ dependencies = [
|
||||
"pin-project-lite 0.2.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "616896e05fc0e2649463a93a15183c6a16bf03413a7af88ef1285ddedfa9cda5"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atoi"
|
||||
version = "1.0.0"
|
||||
@@ -987,27 +994,13 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||
|
||||
[[package]]
|
||||
name = "bls12_381"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54757888b09a69be70b5ec303e382a74227392086ba808cb01eeca29233a2397"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/jstuczyn/bls12_381?branch=feature/gt-serialization-0.8.0#c4543fde7d02efea6ecfcf22e14476ddb516b483"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"ff 0.10.1",
|
||||
"group 0.10.0",
|
||||
"pairing 0.20.0",
|
||||
"rand_core 0.6.4",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bls12_381"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/jstuczyn/bls12_381?branch=gt-serialisation#10fb6f700bfda17c8475af3bfd31e3fec15f2278"
|
||||
dependencies = [
|
||||
"digest 0.9.0",
|
||||
"ff 0.11.1",
|
||||
"group 0.11.0",
|
||||
"pairing 0.21.0",
|
||||
"ff 0.13.0",
|
||||
"group 0.13.0",
|
||||
"pairing",
|
||||
"rand_core 0.6.4",
|
||||
"subtle 2.4.1",
|
||||
"zeroize",
|
||||
@@ -1566,6 +1559,26 @@ version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
version = "0.2.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673"
|
||||
dependencies = [
|
||||
"const_format_proc_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_format_proc_macros"
|
||||
version = "0.2.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.3.0"
|
||||
@@ -1761,30 +1774,15 @@ dependencies = [
|
||||
"toml 0.5.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49fc9a695bca7f35f5f4c15cddc84415f66a74ea78eef08e90c5024f2b540e23"
|
||||
dependencies = [
|
||||
"crc-catalog 1.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
|
||||
dependencies = [
|
||||
"crc-catalog 2.2.0",
|
||||
"crc-catalog",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccaeedb56da03b09f598226e25e80088cb4cd25f316e6e4df7d695f0feeb1403"
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "2.2.0"
|
||||
@@ -2578,12 +2576,6 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "dotenv"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
@@ -2995,9 +2987,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.8"
|
||||
version = "0.6.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
|
||||
checksum = "b6267a1fa6f59179ea4afc8e50fd8612a3cc60bc858f786ff877a4a8cb042799"
|
||||
dependencies = [
|
||||
"indenter",
|
||||
"once_cell",
|
||||
@@ -3030,26 +3022,6 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "131655483be284720a17d74ff97592b8e76576dc25563148601df2d7c9080924"
|
||||
dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ff"
|
||||
version = "0.12.1"
|
||||
@@ -3066,6 +3038,7 @@ version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"rand_core 0.6.4",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
@@ -3519,30 +3492,6 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"ff 0.10.1",
|
||||
"rand_core 0.6.4",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"ff 0.11.1",
|
||||
"rand_core 0.6.4",
|
||||
"subtle 2.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "group"
|
||||
version = "0.12.1"
|
||||
@@ -4286,6 +4235,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
@@ -5937,6 +5895,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"getset",
|
||||
"humantime-serde",
|
||||
"itertools 0.12.0",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"nym-api-requests",
|
||||
@@ -5979,7 +5938,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sqlx 0.6.3",
|
||||
"sqlx",
|
||||
"tap",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
@@ -6001,10 +5960,12 @@ dependencies = [
|
||||
"cosmwasm-std",
|
||||
"getset",
|
||||
"nym-coconut-interface",
|
||||
"nym-crypto",
|
||||
"nym-mixnet-contract-common",
|
||||
"nym-node-requests",
|
||||
"schemars",
|
||||
"serde",
|
||||
"tendermint",
|
||||
"ts-rs",
|
||||
]
|
||||
|
||||
@@ -6022,6 +5983,7 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"thiserror",
|
||||
"url",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6204,7 +6166,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.10.8",
|
||||
"sqlx 0.6.3",
|
||||
"sqlx",
|
||||
"tap",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
@@ -6257,14 +6219,14 @@ dependencies = [
|
||||
name = "nym-coconut"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"bls12_381 0.6.0",
|
||||
"bls12_381",
|
||||
"bs58 0.4.0",
|
||||
"criterion",
|
||||
"digest 0.9.0",
|
||||
"doc-comment",
|
||||
"ff 0.11.1",
|
||||
"ff 0.13.0",
|
||||
"getrandom 0.2.10",
|
||||
"group 0.11.0",
|
||||
"group 0.13.0",
|
||||
"itertools 0.10.5",
|
||||
"nym-dkg",
|
||||
"nym-pemstore",
|
||||
@@ -6274,6 +6236,7 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"sha2 0.9.9",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6340,7 +6303,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"log",
|
||||
"sqlx 0.5.13",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
@@ -6364,7 +6327,7 @@ dependencies = [
|
||||
name = "nym-credentials"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bls12_381 0.5.0",
|
||||
"bls12_381",
|
||||
"cosmrs",
|
||||
"log",
|
||||
"nym-api-requests",
|
||||
@@ -6373,6 +6336,7 @@ dependencies = [
|
||||
"nym-validator-client",
|
||||
"rand 0.7.3",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6406,11 +6370,11 @@ name = "nym-dkg"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bls12_381 0.6.0",
|
||||
"bls12_381",
|
||||
"bs58 0.4.0",
|
||||
"criterion",
|
||||
"ff 0.11.1",
|
||||
"group 0.11.0",
|
||||
"ff 0.13.0",
|
||||
"group 0.13.0",
|
||||
"lazy_static",
|
||||
"nym-contracts-common",
|
||||
"nym-pemstore",
|
||||
@@ -6526,7 +6490,7 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx 0.5.13",
|
||||
"sqlx",
|
||||
"subtle-encoding",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
@@ -6848,7 +6812,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx 0.6.3",
|
||||
"sqlx",
|
||||
"tap",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
@@ -6869,7 +6833,7 @@ dependencies = [
|
||||
"pretty_env_logger",
|
||||
"rocket",
|
||||
"serde",
|
||||
"sqlx 0.5.13",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
@@ -7377,7 +7341,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx 0.5.13",
|
||||
"sqlx",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
@@ -7624,6 +7588,28 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nyxd-scraper"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"const_format",
|
||||
"cosmrs",
|
||||
"eyre",
|
||||
"futures",
|
||||
"nym-bin-common",
|
||||
"sha2 0.10.8",
|
||||
"sqlx",
|
||||
"tendermint",
|
||||
"tendermint-rpc",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.1"
|
||||
@@ -7689,9 +7675,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.57"
|
||||
version = "0.10.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
|
||||
checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"cfg-if",
|
||||
@@ -7730,9 +7716,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.93"
|
||||
version = "0.9.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d"
|
||||
checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -7882,20 +7868,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pairing"
|
||||
version = "0.20.0"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7de9d09263c9966e8196fe0380c9dbbc7ea114b5cf371ba29004bc1f9c6db7f3"
|
||||
checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f"
|
||||
dependencies = [
|
||||
"group 0.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pairing"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2e415e349a3006dd7d9482cdab1c980a845bed1377777d768cb693a44540b42"
|
||||
dependencies = [
|
||||
"group 0.11.0",
|
||||
"group 0.13.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -9790,9 +9767,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.107"
|
||||
version = "1.0.108"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
|
||||
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
@@ -10159,17 +10136,6 @@ dependencies = [
|
||||
"der 0.7.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlformat"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4b7922be017ee70900be125523f38bdd644f4f06a1b16e8fa5a8ee8c34bffd4"
|
||||
dependencies = [
|
||||
"itertools 0.10.5",
|
||||
"nom",
|
||||
"unicode_categories",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlformat"
|
||||
version = "0.2.2"
|
||||
@@ -10181,70 +10147,14 @@ dependencies = [
|
||||
"unicode_categories",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "551873805652ba0d912fec5bbb0f8b4cdd96baf8e2ebf5970e5671092966019b"
|
||||
dependencies = [
|
||||
"sqlx-core 0.5.13",
|
||||
"sqlx-macros 0.5.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188"
|
||||
dependencies = [
|
||||
"sqlx-core 0.6.3",
|
||||
"sqlx-macros 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-core"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e48c61941ccf5ddcada342cd59e3e5173b007c509e1e8e990dafc830294d9dc5"
|
||||
dependencies = [
|
||||
"ahash 0.7.6",
|
||||
"atoi 0.4.0",
|
||||
"bitflags 1.3.2",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc 2.1.0",
|
||||
"crossbeam-queue",
|
||||
"either",
|
||||
"event-listener",
|
||||
"flume",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-intrusive",
|
||||
"futures-util",
|
||||
"hashlink 0.7.0",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
"itoa",
|
||||
"libc",
|
||||
"libsqlite3-sys",
|
||||
"log",
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rustls 0.19.1",
|
||||
"sha2 0.10.8",
|
||||
"smallvec",
|
||||
"sqlformat 0.1.8",
|
||||
"sqlx-rt 0.5.13",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"webpki 0.21.4",
|
||||
"webpki-roots 0.21.1",
|
||||
"sqlx-core",
|
||||
"sqlx-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -10254,12 +10164,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029"
|
||||
dependencies = [
|
||||
"ahash 0.7.6",
|
||||
"atoi 1.0.0",
|
||||
"atoi",
|
||||
"bitflags 1.3.2",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc 3.0.1",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"dotenvy",
|
||||
"either",
|
||||
@@ -10285,34 +10195,16 @@ dependencies = [
|
||||
"rustls-pemfile",
|
||||
"sha2 0.10.8",
|
||||
"smallvec",
|
||||
"sqlformat 0.2.2",
|
||||
"sqlx-rt 0.6.3",
|
||||
"sqlformat",
|
||||
"sqlx-rt",
|
||||
"stringprep",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio-stream",
|
||||
"url",
|
||||
"webpki-roots 0.22.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0fba2b0cae21fc00fe6046f8baa4c7fcb49e379f0f592b04696607f69ed2e1"
|
||||
dependencies = [
|
||||
"dotenv",
|
||||
"either",
|
||||
"heck 0.4.1",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"sha2 0.10.8",
|
||||
"sqlx-core 0.5.13",
|
||||
"sqlx-rt 0.5.13",
|
||||
"syn 1.0.109",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-macros"
|
||||
version = "0.6.3"
|
||||
@@ -10326,23 +10218,12 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"sha2 0.10.8",
|
||||
"sqlx-core 0.6.3",
|
||||
"sqlx-rt 0.6.3",
|
||||
"sqlx-core",
|
||||
"sqlx-rt",
|
||||
"syn 1.0.109",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-rt"
|
||||
version = "0.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4db708cd3e459078f85f39f96a00960bd841f66ee2a669e90bf36907f5a79aae"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"tokio",
|
||||
"tokio-rustls 0.22.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlx-rt"
|
||||
version = "0.6.3"
|
||||
@@ -10475,7 +10356,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7e94b1ec00bad60e6410e058b52f1c66de3dc5fe4d62d09b3e52bb7d3b73e25"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"crc 3.0.1",
|
||||
"crc",
|
||||
"lazy_static",
|
||||
"md-5",
|
||||
"rand 0.8.5",
|
||||
@@ -10709,6 +10590,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfbf0a4753b46a190f367337e0163d0b552a2674a6bac54e74f9f2cdcde2969b"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"async-tungstenite",
|
||||
"bytes",
|
||||
"flex-error",
|
||||
"futures",
|
||||
@@ -10917,17 +10799,6 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6"
|
||||
dependencies = [
|
||||
"rustls 0.19.1",
|
||||
"tokio",
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.23.4"
|
||||
@@ -11014,16 +10885,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.9"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d"
|
||||
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"hashbrown 0.12.3",
|
||||
"hashbrown 0.14.1",
|
||||
"pin-project-lite 0.2.13",
|
||||
"slab",
|
||||
"tokio",
|
||||
@@ -11463,6 +11334,7 @@ dependencies = [
|
||||
"log",
|
||||
"native-tls",
|
||||
"rand 0.8.5",
|
||||
"rustls 0.21.7",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"url",
|
||||
@@ -12077,15 +11949,6 @@ dependencies = [
|
||||
"untrusted 0.9.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
|
||||
dependencies = [
|
||||
"webpki 0.21.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.22.6"
|
||||
@@ -12205,7 +12068,7 @@ checksum = "465a03cc11e9a7d7b4f9f99870558fe37a102b65b93f8045392fef7c67b39e80"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
"crc 3.0.1",
|
||||
"crc",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
@@ -12256,7 +12119,7 @@ dependencies = [
|
||||
"arc-swap",
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"crc 3.0.1",
|
||||
"crc",
|
||||
"log",
|
||||
"rand 0.8.5",
|
||||
"thiserror",
|
||||
|
||||
+13
-1
@@ -67,6 +67,7 @@ members = [
|
||||
"common/nymsphinx/params",
|
||||
"common/nymsphinx/routing",
|
||||
"common/nymsphinx/types",
|
||||
"common/nyxd-scraper",
|
||||
"common/pemstore",
|
||||
"common/socks5-client-core",
|
||||
"common/socks5/proxy-helpers",
|
||||
@@ -159,10 +160,12 @@ reqwest = "0.11.22"
|
||||
schemars = "0.8.1"
|
||||
serde = "1.0.152"
|
||||
serde_json = "1.0.91"
|
||||
sqlx = "0.6.3"
|
||||
tap = "1.0.1"
|
||||
time = "0.3.30"
|
||||
thiserror = "1.0.48"
|
||||
tokio = "1.24.1"
|
||||
tokio = "1.33.0"
|
||||
tokio-util = "0.7.10"
|
||||
tokio-tungstenite = "0.20.1"
|
||||
tracing = "0.1.37"
|
||||
tungstenite = { version = "0.20.1", default-features = false }
|
||||
@@ -172,6 +175,14 @@ utoipa-swagger-ui = "3.1.5"
|
||||
url = "2.4"
|
||||
zeroize = "1.6.0"
|
||||
|
||||
# coconut/DKG related
|
||||
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
|
||||
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
|
||||
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch ="feature/gt-serialization-0.8.0" }
|
||||
group = "0.13.0"
|
||||
ff = "0.13.0"
|
||||
|
||||
|
||||
# cosmwasm-related
|
||||
cosmwasm-derive = "=1.3.0"
|
||||
cosmwasm-schema = "=1.3.0"
|
||||
@@ -191,6 +202,7 @@ cw-controllers = { version = "=1.1.0" }
|
||||
# cosmrs-related
|
||||
bip32 = "0.5.1"
|
||||
cosmrs = "=0.15.0"
|
||||
tendermint = "0.34" # same version as used by cosmrs
|
||||
tendermint-rpc = "0.34" # same version as used by cosmrs
|
||||
prost = "0.12"
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: #333;
|
||||
color: white;
|
||||
}
|
||||
a {
|
||||
color: skyblue;
|
||||
}
|
||||
}
|
||||
.container {
|
||||
font-family: sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.intro {
|
||||
text-align: center;
|
||||
}
|
||||
.licenses-list {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.license-used-by {
|
||||
margin-top: -10px;
|
||||
}
|
||||
.license-text {
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main class="container">
|
||||
<div class="intro">
|
||||
<h1>Third Party Licenses</h1>
|
||||
<p>This page lists the licenses of the projects used in cargo-about.</p>
|
||||
</div>
|
||||
|
||||
<h2>Overview of licenses:</h2>
|
||||
<ul class="licenses-overview">
|
||||
{{#each overview}}
|
||||
<li><a href="#{{id}}">{{name}}</a> ({{count}})</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
<h2>All license text:</h2>
|
||||
<ul class="licenses-list">
|
||||
{{#each licenses}}
|
||||
<li class="license">
|
||||
<h3 id="{{id}}">{{name}}</h3>
|
||||
<h4>Used by:</h4>
|
||||
<ul class="license-used-by">
|
||||
{{#each used_by}}
|
||||
<li><a href="{{#if crate.repository}} {{crate.repository}} {{else}} https://crates.io/crates/{{crate.name}} {{/if}}">{{crate.name}} {{crate.version}}</a></li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
<pre class="license-text">{{text}}</pre>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
private = { ignore = true }
|
||||
|
||||
accepted = [
|
||||
"0BSD",
|
||||
"Apache-2.0",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"CC0-1.0",
|
||||
"ISC",
|
||||
"MIT",
|
||||
"MPL-2.0",
|
||||
"Unicode-DFS-2016",
|
||||
"OpenSSL",
|
||||
]
|
||||
|
||||
workarounds = [
|
||||
"ring",
|
||||
"rustls",
|
||||
]
|
||||
@@ -5,6 +5,7 @@ authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>", "Jędrzej St
|
||||
description = "Implementation of the Nym Client"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ name = "nym-client-websocket-requests"
|
||||
version = "0.1.0"
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
description = "A SOCKS5 localhost proxy that converts incoming messages to Sphinx and sends them to a Nym address"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true, features = ["cargo", "derive"] }
|
||||
|
||||
@@ -15,4 +15,4 @@ prod:
|
||||
mixnode_identity: 3pMCJswCyA19MGYWGDWT5fBk2M8ybSZGXttyAoNY5gBB
|
||||
gateway_identity: 2BuMSfMW3zpeAjKXyKLhmY4QW1DXurrtSPEJ6CjX3SEh
|
||||
log_level: error
|
||||
time_zone: utc
|
||||
time_zone: utc
|
||||
+19
-4
@@ -1,4 +1,6 @@
|
||||
import { dir } from "console";
|
||||
import { readFileSync } from "fs";
|
||||
import { dirname } from "path";
|
||||
import { TLogLevelName } from "tslog";
|
||||
|
||||
import YAML from "yaml";
|
||||
@@ -10,9 +12,11 @@ class ConfigHandler {
|
||||
|
||||
public commonConfig: { request_headers: object };
|
||||
|
||||
private currentEnvironment: string;
|
||||
|
||||
public environment: string;
|
||||
|
||||
public environmnetConfig: {
|
||||
public environmentConfig: {
|
||||
log_level: TLogLevelName;
|
||||
time_zone: string;
|
||||
api_base_url: string;
|
||||
@@ -35,8 +39,9 @@ class ConfigHandler {
|
||||
|
||||
private setCommonConfig(): void {
|
||||
try {
|
||||
const baseWorkingDirectory = __dirname;
|
||||
this.commonConfig = YAML.parse(
|
||||
readFileSync("src/config/config.yaml", "utf8")
|
||||
readFileSync(baseWorkingDirectory + "/config.yaml", "utf8"),
|
||||
).common;
|
||||
} catch (error) {
|
||||
throw Error(`Error reading common config: (${error})`);
|
||||
@@ -46,14 +51,24 @@ class ConfigHandler {
|
||||
private setEnvironmentConfig(environment: string): void {
|
||||
this.ensureEnvironmentIsValid(environment);
|
||||
try {
|
||||
this.environmnetConfig = YAML.parse(
|
||||
readFileSync("src/config/config.yaml", "utf8")
|
||||
const baseWorkingDirectory = __dirname;
|
||||
this.environmentConfig = YAML.parse(
|
||||
readFileSync(baseWorkingDirectory + "/config.yaml", "utf8"),
|
||||
)[environment];
|
||||
} catch (error) {
|
||||
console.log("fadsfasdfasdfsdfsa")
|
||||
throw Error(`Error reading environment config: (${error})`);
|
||||
}
|
||||
}
|
||||
|
||||
public getEnvironmentConfig(environment: string): any {
|
||||
const baseWorkingDirectory = __dirname;
|
||||
return (
|
||||
this.environmentConfig ||
|
||||
YAML.parse(readFileSync(baseWorkingDirectory + "/config.yaml", "utf8"))[environment]
|
||||
);
|
||||
}
|
||||
|
||||
private ensureEnvironmentIsValid(environment: string): void {
|
||||
if (this.validEnvironments.indexOf(environment) === -1) {
|
||||
throw Error(`Config environment is not valid: "${environment}"`);
|
||||
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
ConfigHandler: require('./config/configHandler.ts'),
|
||||
RestClient: require('./restClient/RestClient.ts')
|
||||
};
|
||||
+5
-5
@@ -13,9 +13,9 @@ import ConfigHandler from "../config/configHandler";
|
||||
|
||||
const config = ConfigHandler.getInstance();
|
||||
const log = new Logger({
|
||||
minLevel: config.environmnetConfig.log_level,
|
||||
minLevel: config.environmentConfig.log_level,
|
||||
dateTimeTimezone:
|
||||
config.environmnetConfig.time_zone ||
|
||||
config.environmentConfig.time_zone ||
|
||||
Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ function isSet(property): boolean {
|
||||
}
|
||||
|
||||
export class RestClient {
|
||||
private static authToken: string;
|
||||
public static authToken: string;
|
||||
|
||||
private axiosInstance: AxiosInstance;
|
||||
|
||||
@@ -83,7 +83,7 @@ export class RestClient {
|
||||
data,
|
||||
additionalConfigs,
|
||||
params,
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
await this.axiosInstance
|
||||
@@ -214,7 +214,7 @@ export class RestClient {
|
||||
|
||||
if (isSet(additionalConfigs)) {
|
||||
logRecord = `${logRecord}\nAdditional Configuration: ${stringify(
|
||||
additionalConfigs
|
||||
additionalConfigs,
|
||||
)}`;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "async-file-watcher"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-bandwidth-controller"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -10,6 +11,7 @@ bip39 = { workspace = true }
|
||||
rand = "0.7.3"
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
nym-coconut-interface = { path = "../coconut-interface" }
|
||||
nym-credential-storage = { path = "../credential-storage" }
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::BandwidthControllerError;
|
||||
use nym_coconut_interface::{Base58, Parameters};
|
||||
use nym_coconut_interface::Base58;
|
||||
use nym_credential_storage::storage::Storage;
|
||||
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
|
||||
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
|
||||
use nym_credentials::coconut::utils::obtain_aggregate_signature;
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use nym_network_defaults::VOUCHER_INFO;
|
||||
@@ -12,10 +12,8 @@ use nym_validator_client::coconut::all_coconut_api_clients;
|
||||
use nym_validator_client::nyxd::contract_traits::CoconutBandwidthSigningClient;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use nym_validator_client::nyxd::Coin;
|
||||
use nym_validator_client::nyxd::Hash;
|
||||
use rand::rngs::OsRng;
|
||||
use state::{KeyPair, State};
|
||||
use std::str::FromStr;
|
||||
use state::State;
|
||||
|
||||
pub mod state;
|
||||
|
||||
@@ -24,30 +22,29 @@ where
|
||||
C: CoconutBandwidthSigningClient + Sync,
|
||||
{
|
||||
let mut rng = OsRng;
|
||||
let signing_keypair = KeyPair::from(identity::KeyPair::new(&mut rng));
|
||||
let encryption_keypair = KeyPair::from(encryption::KeyPair::new(&mut rng));
|
||||
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
|
||||
let signing_key = identity::PrivateKey::new(&mut rng);
|
||||
let encryption_key = encryption::PrivateKey::new(&mut rng);
|
||||
let params = BandwidthVoucher::default_parameters();
|
||||
let voucher_value = amount.amount.to_string();
|
||||
|
||||
let tx_hash = client
|
||||
.deposit(
|
||||
amount,
|
||||
String::from(VOUCHER_INFO),
|
||||
signing_keypair.public_key.clone(),
|
||||
encryption_keypair.public_key.clone(),
|
||||
signing_key.public_key().to_base58_string(),
|
||||
encryption_key.public_key().to_base58_string(),
|
||||
None,
|
||||
)
|
||||
.await?
|
||||
.transaction_hash
|
||||
.to_string();
|
||||
.transaction_hash;
|
||||
|
||||
let voucher = BandwidthVoucher::new(
|
||||
¶ms,
|
||||
voucher_value,
|
||||
VOUCHER_INFO.to_string(),
|
||||
Hash::from_str(&tx_hash).map_err(|_| BandwidthControllerError::InvalidTxHash)?,
|
||||
identity::PrivateKey::from_base58_string(&signing_keypair.private_key)?,
|
||||
encryption::PrivateKey::from_base58_string(&encryption_keypair.private_key)?,
|
||||
tx_hash,
|
||||
signing_key,
|
||||
encryption_key,
|
||||
);
|
||||
|
||||
let state = State { voucher, params };
|
||||
|
||||
@@ -2,32 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_coconut_interface::Parameters;
|
||||
use nym_credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
|
||||
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
|
||||
pub(crate) struct KeyPair {
|
||||
pub public_key: String,
|
||||
pub private_key: String,
|
||||
}
|
||||
|
||||
impl From<identity::KeyPair> for KeyPair {
|
||||
fn from(kp: identity::KeyPair) -> Self {
|
||||
Self {
|
||||
public_key: kp.public_key().to_base58_string(),
|
||||
private_key: kp.private_key().to_base58_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<encryption::KeyPair> for KeyPair {
|
||||
fn from(kp: encryption::KeyPair) -> Self {
|
||||
Self {
|
||||
public_key: kp.public_key().to_base58_string(),
|
||||
private_key: kp.private_key().to_base58_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
use nym_credentials::coconut::bandwidth::BandwidthVoucher;
|
||||
|
||||
pub struct State {
|
||||
pub voucher: BandwidthVoucher,
|
||||
@@ -38,7 +13,7 @@ impl State {
|
||||
pub fn new(voucher: BandwidthVoucher) -> Self {
|
||||
State {
|
||||
voucher,
|
||||
params: Parameters::new(TOTAL_ATTRIBUTES).unwrap(),
|
||||
params: BandwidthVoucher::default_parameters(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use nym_credential_storage::storage::Storage;
|
||||
use nym_validator_client::coconut::all_coconut_api_clients;
|
||||
use nym_validator_client::nyxd::contract_traits::DkgQueryClient;
|
||||
use std::str::FromStr;
|
||||
use zeroize::Zeroizing;
|
||||
use {
|
||||
nym_coconut_interface::Base58,
|
||||
nym_credentials::coconut::{
|
||||
@@ -46,10 +47,12 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
||||
let voucher_value = u64::from_str(&bandwidth_credential.voucher_value)
|
||||
.map_err(|_| StorageError::InconsistentData)?;
|
||||
let voucher_info = bandwidth_credential.voucher_info.clone();
|
||||
let serial_number =
|
||||
nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.serial_number)?;
|
||||
let binding_number =
|
||||
nym_coconut_interface::Attribute::try_from_bs58(bandwidth_credential.binding_number)?;
|
||||
let serial_number = Zeroizing::new(nym_coconut_interface::Attribute::try_from_bs58(
|
||||
bandwidth_credential.serial_number,
|
||||
)?);
|
||||
let binding_number = Zeroizing::new(nym_coconut_interface::Attribute::try_from_bs58(
|
||||
bandwidth_credential.binding_number,
|
||||
)?);
|
||||
let signature =
|
||||
nym_coconut_interface::Signature::try_from_bs58(bandwidth_credential.signature)?;
|
||||
let epoch_id = u64::from_str(&bandwidth_credential.epoch_id)
|
||||
@@ -64,8 +67,8 @@ impl<C, St: Storage> BandwidthController<C, St> {
|
||||
prepare_for_spending(
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
serial_number,
|
||||
binding_number,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
epoch_id,
|
||||
&signature,
|
||||
&verification_key,
|
||||
|
||||
@@ -4,6 +4,7 @@ version = "1.1.15"
|
||||
authors = ["Dave Hrycyszyn <futurechimp@users.noreply.github.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.66"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -58,7 +59,7 @@ features = ["time"]
|
||||
version = "0.20.1"
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
version = "0.6.2"
|
||||
workspace = true
|
||||
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
|
||||
optional = true
|
||||
|
||||
@@ -89,7 +90,7 @@ tempfile = "3.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
pub use wasmtimer::{std::Instant, tokio::*};
|
||||
|
||||
+3
-1
@@ -127,7 +127,9 @@ impl ActionController {
|
||||
.insert(frag_id, (Arc::new(pending_ack), None))
|
||||
.is_some()
|
||||
{
|
||||
panic!("Tried to insert duplicate pending ack")
|
||||
// This used to be a panic, however since we've seen this actually happen in the
|
||||
// wild, let's not take the whole client (and possibly gateway) down because of it.
|
||||
error!("Tried to insert duplicate pending ack! This should not be possible!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,6 +259,7 @@ pub(super) fn get_specified_gateway(
|
||||
gateways: &[gateway::Node],
|
||||
must_use_tls: bool,
|
||||
) -> Result<gateway::Node, ClientCoreError> {
|
||||
log::debug!("Requesting specified gateway: {}", gateway_identity);
|
||||
let user_gateway = identity::PublicKey::from_base58_string(gateway_identity)
|
||||
.map_err(ClientCoreError::UnableToCreatePublicKeyFromGatewayId)?;
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ where
|
||||
D::StorageError: Send + Sync + 'static,
|
||||
T: DeserializeOwned + Serialize + Send + Sync,
|
||||
{
|
||||
log::trace!("Setting up gateway");
|
||||
log::debug!("Setting up gateway");
|
||||
match setup {
|
||||
GatewaySetup::MustLoad => use_loaded_gateway_details(key_store, details_store).await,
|
||||
GatewaySetup::New {
|
||||
|
||||
@@ -3,6 +3,7 @@ name = "nym-gateway-client"
|
||||
version = "0.1.0"
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -792,6 +792,7 @@ pub struct InitOnly;
|
||||
impl GatewayClient<InitOnly, EphemeralCredentialStorage> {
|
||||
// for initialisation we do not need credential storage. Though it's still a bit weird we have to set the generic...
|
||||
pub fn new_init(config: GatewayConfig, local_identity: Arc<identity::KeyPair>) -> Self {
|
||||
log::trace!("Initialising gateway client");
|
||||
use futures::channel::mpsc;
|
||||
|
||||
// note: this packet_router is completely invalid in normal circumstances, but "works"
|
||||
|
||||
@@ -3,14 +3,15 @@ name = "nym-mixnet-client"
|
||||
version = "0.1.0"
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
futures = { workspace = true }
|
||||
log = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = ["time", "net", "rt"] }
|
||||
tokio-util = { version = "0.7.4", features = ["codec"] }
|
||||
tokio = { workspace = true, features = ["time", "net", "rt"] }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
|
||||
# internal
|
||||
nym-sphinx = { path = "../../nymsphinx" }
|
||||
|
||||
@@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -5,8 +5,12 @@ use crate::nym_api::error::NymAPIError;
|
||||
use crate::nym_api::routes::{CORE_STATUS_COUNT, SINCE_ARG};
|
||||
use async_trait::async_trait;
|
||||
use http_api_client::{ApiClient, NO_PARAMS};
|
||||
use nym_api_requests::coconut::models::{
|
||||
EpochCredentialsResponse, IssuedCredentialResponse, IssuedCredentialsResponse,
|
||||
};
|
||||
use nym_api_requests::coconut::{
|
||||
BlindSignRequestBody, BlindedSignatureResponse, VerifyCredentialBody, VerifyCredentialResponse,
|
||||
BlindSignRequestBody, BlindedSignatureResponse, CredentialsRequestBody, VerifyCredentialBody,
|
||||
VerifyCredentialResponse,
|
||||
};
|
||||
use nym_api_requests::models::{
|
||||
ComputeRewardEstParam, DescribedGateway, GatewayBondAnnotated, GatewayCoreStatusResponse,
|
||||
@@ -15,6 +19,7 @@ use nym_api_requests::models::{
|
||||
MixnodeStatusResponse, MixnodeUptimeHistoryResponse, RewardEstimationResponse,
|
||||
StakeSaturationResponse, UptimeResponse,
|
||||
};
|
||||
use nym_coconut_dkg_common::types::EpochId;
|
||||
use nym_mixnet_contract_common::mixnode::MixNodeDetails;
|
||||
use nym_mixnet_contract_common::{GatewayBond, IdentityKeyRef, MixId};
|
||||
use nym_name_service_common::response::NamesListResponse;
|
||||
@@ -399,6 +404,60 @@ pub trait NymApiClientExt: ApiClient {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn epoch_credentials(
|
||||
&self,
|
||||
dkg_epoch: EpochId,
|
||||
) -> Result<EpochCredentialsResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
routes::BANDWIDTH,
|
||||
routes::COCONUT_EPOCH_CREDENTIALS,
|
||||
&dkg_epoch.to_string(),
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn issued_credential(
|
||||
&self,
|
||||
credential_id: i64,
|
||||
) -> Result<IssuedCredentialResponse, NymAPIError> {
|
||||
self.get_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
routes::BANDWIDTH,
|
||||
routes::COCONUT_ISSUED_CREDENTIAL,
|
||||
&credential_id.to_string(),
|
||||
],
|
||||
NO_PARAMS,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn issued_credentials(
|
||||
&self,
|
||||
credential_ids: Vec<i64>,
|
||||
) -> Result<IssuedCredentialsResponse, NymAPIError> {
|
||||
self.post_json(
|
||||
&[
|
||||
routes::API_VERSION,
|
||||
routes::COCONUT_ROUTES,
|
||||
routes::BANDWIDTH,
|
||||
routes::COCONUT_ISSUED_CREDENTIALS,
|
||||
],
|
||||
NO_PARAMS,
|
||||
&CredentialsRequestBody {
|
||||
credential_ids,
|
||||
pagination: None,
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_service_providers(&self) -> Result<ServicesListResponse, NymAPIError> {
|
||||
log::trace!("Getting service providers");
|
||||
self.get_json(&[routes::API_VERSION, routes::SERVICE_PROVIDERS], NO_PARAMS)
|
||||
|
||||
@@ -17,6 +17,9 @@ pub const BANDWIDTH: &str = "bandwidth";
|
||||
|
||||
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
|
||||
pub const COCONUT_VERIFY_BANDWIDTH_CREDENTIAL: &str = "verify-bandwidth-credential";
|
||||
pub const COCONUT_EPOCH_CREDENTIALS: &str = "epoch-credentials";
|
||||
pub const COCONUT_ISSUED_CREDENTIAL: &str = "issued-credential";
|
||||
pub const COCONUT_ISSUED_CREDENTIALS: &str = "issued-credentials";
|
||||
|
||||
pub const STATUS_ROUTES: &str = "status";
|
||||
pub const MIXNODE: &str = "mixnode";
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ pub trait CoconutBandwidthSigningClient {
|
||||
fee: Option<Fee>,
|
||||
) -> Result<ExecuteResult, NyxdError> {
|
||||
let req = CoconutBandwidthExecuteMsg::DepositFunds {
|
||||
data: DepositData::new(info.to_string(), verification_key, encryption_key),
|
||||
data: DepositData::new(info, verification_key, encryption_key),
|
||||
};
|
||||
self.execute_coconut_bandwidth_contract(
|
||||
fee,
|
||||
|
||||
+22
-2
@@ -6,8 +6,8 @@ use crate::nyxd::error::NyxdError;
|
||||
use crate::nyxd::CosmWasmClient;
|
||||
use async_trait::async_trait;
|
||||
use cw3::{
|
||||
ProposalListResponse, ProposalResponse, VoteListResponse, VoteResponse, VoterListResponse,
|
||||
VoterResponse,
|
||||
ProposalListResponse, ProposalResponse, VoteListResponse, VoteResponse, VoterDetail,
|
||||
VoterListResponse, VoterResponse,
|
||||
};
|
||||
use cw_utils::ThresholdResponse;
|
||||
use nym_multisig_contract_common::msg::QueryMsg as MultisigQueryMsg;
|
||||
@@ -114,6 +114,26 @@ pub trait PagedMultisigQueryClient: MultisigQueryClient {
|
||||
|
||||
Ok(proposals)
|
||||
}
|
||||
|
||||
async fn get_all_voters(&self) -> Result<Vec<VoterDetail>, NyxdError> {
|
||||
let mut voters = Vec::new();
|
||||
let mut start_after = None;
|
||||
|
||||
loop {
|
||||
let mut paged_response = self.list_voters(start_after.take(), None).await?;
|
||||
|
||||
let last_voter = paged_response.voters.last().map(|prop| prop.addr.clone());
|
||||
voters.append(&mut paged_response.voters);
|
||||
|
||||
if let Some(start_after_res) = last_voter {
|
||||
start_after = Some(start_after_res)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(voters)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nyxd::TxResponse;
|
||||
|
||||
pub fn find_tx_attribute(tx: &TxResponse, event_type: &str, attribute_key: &str) -> Option<String> {
|
||||
let event = tx.tx_result.events.iter().find(|e| e.kind == event_type)?;
|
||||
let attribute = event
|
||||
.attributes
|
||||
.iter()
|
||||
.find(|attr| attr.key == attribute_key)?;
|
||||
Some(attribute.value.clone())
|
||||
}
|
||||
@@ -43,10 +43,15 @@ pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
|
||||
pub use cosmrs::tendermint::Time as TendermintTime;
|
||||
pub use cosmrs::tx::Msg;
|
||||
pub use cosmrs::tx::{self};
|
||||
pub use cosmrs::Any;
|
||||
pub use cosmrs::Coin as CosmosCoin;
|
||||
pub use cosmrs::Gas;
|
||||
pub use cosmrs::{bip32, AccountId, Denom};
|
||||
pub use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
pub use cw2;
|
||||
pub use cw3;
|
||||
pub use cw4;
|
||||
pub use cw_controllers;
|
||||
pub use fee::{gas_price::GasPrice, GasAdjustable, GasAdjustment};
|
||||
pub use tendermint_rpc::{
|
||||
endpoint::{tx::Response as TxResponse, validators::Response as ValidatorResponse},
|
||||
@@ -67,6 +72,7 @@ pub mod contract_traits;
|
||||
pub mod cosmwasm_client;
|
||||
pub mod error;
|
||||
pub mod fee;
|
||||
pub mod helpers;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
@@ -376,7 +382,11 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn simulate<I, M>(&self, messages: I) -> Result<SimulateResponse, NyxdError>
|
||||
pub async fn simulate<I, M>(
|
||||
&self,
|
||||
messages: I,
|
||||
memo: impl Into<String> + Send + 'static,
|
||||
) -> Result<SimulateResponse, NyxdError>
|
||||
where
|
||||
I: IntoIterator<Item = M> + Send,
|
||||
M: Msg,
|
||||
@@ -391,7 +401,7 @@ where
|
||||
.map_err(|_| {
|
||||
NyxdError::SerializationError("custom simulate messages".to_owned())
|
||||
})?,
|
||||
"simulating execution of transactions",
|
||||
memo,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ name = "nym-coconut-interface"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "Crutch library until there is proper SerDe support for coconut structs"
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bs58 = "0.4.0"
|
||||
|
||||
@@ -21,10 +21,14 @@ pub use nym_coconut::{
|
||||
pub struct Credential {
|
||||
#[getset(get = "pub")]
|
||||
n_params: u32,
|
||||
|
||||
#[getset(get = "pub")]
|
||||
theta: Theta,
|
||||
|
||||
voucher_value: u64,
|
||||
|
||||
voucher_info: String,
|
||||
|
||||
#[getset(get = "pub")]
|
||||
epoch_id: u64,
|
||||
}
|
||||
@@ -64,14 +68,12 @@ impl Credential {
|
||||
|
||||
pub fn verify(&self, verification_key: &VerificationKey) -> bool {
|
||||
let params = Parameters::new(self.n_params).unwrap();
|
||||
let public_attributes = [
|
||||
self.voucher_value.to_string().as_bytes(),
|
||||
self.voucher_info.as_bytes(),
|
||||
]
|
||||
.iter()
|
||||
.map(hash_to_scalar)
|
||||
.collect::<Vec<Attribute>>();
|
||||
nym_coconut::verify_credential(¶ms, verification_key, &self.theta, &public_attributes)
|
||||
|
||||
let hashed_value = hash_to_scalar(self.voucher_value.to_string());
|
||||
let hashed_info = hash_to_scalar(&self.voucher_info);
|
||||
let public_attributes = &[&hashed_value, &hashed_info];
|
||||
|
||||
nym_coconut::verify_credential(¶ms, verification_key, &self.theta, public_attributes)
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
@@ -180,8 +182,8 @@ mod tests {
|
||||
¶ms,
|
||||
&verification_key,
|
||||
&signature,
|
||||
serial_number,
|
||||
binding_number,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
)
|
||||
.unwrap();
|
||||
let credential = Credential::new(4, theta, voucher_value, voucher_info, 42);
|
||||
|
||||
@@ -3,6 +3,7 @@ name = "nym-cli-commands"
|
||||
version = "1.0.0"
|
||||
authors.workspace = true
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
|
||||
@@ -26,6 +26,10 @@ pub struct Args {
|
||||
}
|
||||
|
||||
pub async fn execute(args: Args, client: SigningClient) -> anyhow::Result<()> {
|
||||
if args.amount == 0 {
|
||||
bail!("did not specify credential amount")
|
||||
}
|
||||
|
||||
let loaded = CommonConfigsWrapper::try_load(args.client_config)?;
|
||||
|
||||
if let Ok(id) = loaded.try_get_id() {
|
||||
|
||||
@@ -3,6 +3,7 @@ name = "nym-config"
|
||||
version = "0.1.0"
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -17,4 +18,4 @@ url = { workspace = true }
|
||||
nym-network-defaults = { path = "../network-defaults" }
|
||||
|
||||
[features]
|
||||
default = ["dirs"]
|
||||
default = ["dirs"]
|
||||
|
||||
@@ -73,7 +73,7 @@ where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let path = path.as_ref();
|
||||
log::debug!("trying to save config file to {}", path.display());
|
||||
log::info!("saving config file to {}", path.display());
|
||||
|
||||
if let Some(parent) = path.parent() {
|
||||
create_dir_all(parent)?;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-coconut-bandwidth-contract-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -12,4 +13,4 @@ cw2 = { workspace = true, optional = true }
|
||||
nym-multisig-contract-common = { path = "../multisig-contract" }
|
||||
|
||||
[features]
|
||||
schema = ["cw2"]
|
||||
schema = ["cw2"]
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
// event types
|
||||
pub const DEPOSITED_FUNDS_EVENT_TYPE: &str = "deposited-funds";
|
||||
|
||||
// a 'wasm-' prefix is added to all cosmwasm events
|
||||
pub const COSMWASM_DEPOSITED_FUNDS_EVENT_TYPE: &str = "wasm-deposited-funds";
|
||||
|
||||
// attributes that are used in multiple places
|
||||
pub const DEPOSIT_VALUE: &str = "deposit-value";
|
||||
pub const DEPOSIT_INFO: &str = "deposit-info";
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-coconut-dkg-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -14,4 +15,4 @@ contracts-common = { path = "../contracts-common", package = "nym-contracts-comm
|
||||
nym-multisig-contract-common = { path = "../multisig-contract" }
|
||||
|
||||
[features]
|
||||
schema = []
|
||||
schema = []
|
||||
|
||||
@@ -174,17 +174,19 @@ impl Display for EpochState {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
EpochState::PublicKeySubmission { resharing } => {
|
||||
write!(f, "PublicKeySubmission with resharing {resharing}")
|
||||
write!(f, "PublicKeySubmission (resharing: {resharing})")
|
||||
}
|
||||
EpochState::DealingExchange { resharing } => {
|
||||
write!(f, "DealingExchange (resharing: {resharing})")
|
||||
}
|
||||
EpochState::DealingExchange { resharing } => write!(f, "DealingExchange {resharing}"),
|
||||
EpochState::VerificationKeySubmission { resharing } => {
|
||||
write!(f, "VerificationKeySubmission with resharing {resharing}")
|
||||
write!(f, "VerificationKeySubmission (resharing: {resharing})")
|
||||
}
|
||||
EpochState::VerificationKeyValidation { resharing } => {
|
||||
write!(f, "VerificationKeyValidation with resharing {resharing}")
|
||||
write!(f, "VerificationKeyValidation (resharing: {resharing})")
|
||||
}
|
||||
EpochState::VerificationKeyFinalization { resharing } => {
|
||||
write!(f, "VerificationKeyFinalization with resharing {resharing}")
|
||||
write!(f, "VerificationKeyFinalization (resharing: {resharing})")
|
||||
}
|
||||
EpochState::InProgress => write!(f, "InProgress"),
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-ephemera-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -13,4 +14,4 @@ cw-utils = { workspace = true }
|
||||
contracts-common = { path = "../contracts-common", package = "nym-contracts-common" }
|
||||
|
||||
[features]
|
||||
schema = []
|
||||
schema = []
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-group-contract-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-multisig-contract-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-name-service-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -16,4 +17,4 @@ serde = { workspace = true, features = ["derive"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[features]
|
||||
schema = ["cw2"]
|
||||
schema = ["cw2"]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-service-provider-directory-common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -15,4 +16,4 @@ nym-contracts-common = { path = "../contracts-common", version = "0.5.0" }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
[features]
|
||||
schema = ["cw2"]
|
||||
schema = ["cw2"]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-credential-storage"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -13,14 +14,14 @@ thiserror = { workspace = true }
|
||||
tokio = { version = "1.24.1", features = ["sync"]}
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.sqlx]
|
||||
version = "0.5"
|
||||
workspace = true
|
||||
features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"]
|
||||
|
||||
[target."cfg(not(target_arch = \"wasm32\"))".dependencies.tokio]
|
||||
version = "1.24.1"
|
||||
workspace = true
|
||||
features = [ "rt-multi-thread", "net", "signal", "fs" ]
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
sqlx = { version = "0.5", features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
tokio = { version = "1.24.1", features = ["rt-multi-thread", "macros"] }
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-credential-utils"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -2,18 +2,20 @@
|
||||
name = "nym-credentials"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||
bls12_381 = { workspace = true, default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||
cosmrs = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
zeroize = { workspace = true }
|
||||
|
||||
# I guess temporarily until we get serde support in coconut up and running
|
||||
nym-coconut-interface = { path = "../coconut-interface" }
|
||||
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "hashing"] }
|
||||
nym-crypto = { path = "../crypto", features = ["rand", "asymmetric"] }
|
||||
nym-api-requests = { path = "../../nym-api/nym-api-requests" }
|
||||
nym-validator-client = { path = "../client-libs/validator-client", default-features = false }
|
||||
|
||||
|
||||
@@ -13,38 +13,60 @@ use nym_coconut_interface::{
|
||||
PrivateAttribute, PublicAttribute, Signature, VerificationKey,
|
||||
};
|
||||
use nym_crypto::asymmetric::{encryption, identity};
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
use super::utils::prepare_credential_for_spending;
|
||||
use crate::error::Error;
|
||||
|
||||
pub const PUBLIC_ATTRIBUTES: u32 = 2;
|
||||
pub const PRIVATE_ATTRIBUTES: u32 = 2;
|
||||
pub const TOTAL_ATTRIBUTES: u32 = PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES;
|
||||
|
||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||
pub struct BandwidthVoucher {
|
||||
// a random secret value generated by the client used for double-spending detection
|
||||
// private attributes
|
||||
/// a random secret value generated by the client used for double-spending detection
|
||||
serial_number: PrivateAttribute,
|
||||
// a random secret value generated by the client used to bind multiple credentials together
|
||||
|
||||
/// a random secret value generated by the client used to bind multiple credentials together
|
||||
binding_number: PrivateAttribute,
|
||||
// the value (e.g., bandwidth) encoded in this voucher
|
||||
voucher_value: PublicAttribute,
|
||||
// the plain text value (e.g., bandwidth) encoded in this voucher
|
||||
|
||||
// public atttributes:
|
||||
/// the plain text value (e.g., bandwidth) encoded in this voucher
|
||||
// TODO: in another PR change the value from `"1000"` to `"1000unym"`
|
||||
voucher_value_plain: String,
|
||||
// a field with public information, e.g., type of voucher, interval etc.
|
||||
voucher_info: PublicAttribute,
|
||||
// the plain text information
|
||||
|
||||
/// the plain text information
|
||||
voucher_info_plain: String,
|
||||
// the hash of the deposit transaction
|
||||
|
||||
/// the precomputed value (e.g., bandwidth) encoded in this voucher
|
||||
_voucher_value_prehashed: PublicAttribute,
|
||||
|
||||
/// the precomputed field with public information, e.g., type of voucher, interval etc.
|
||||
_voucher_info_prehashed: PublicAttribute,
|
||||
|
||||
/// the hash of the deposit transaction
|
||||
#[zeroize(skip)]
|
||||
tx_hash: Hash,
|
||||
// base58 encoded private key ensuring the depositer requested these attributes
|
||||
|
||||
/// base58 encoded private key ensuring the depositer requested these attributes
|
||||
signing_key: identity::PrivateKey,
|
||||
// base58 encoded private key ensuring only this client receives the signature share
|
||||
encryption_key: encryption::PrivateKey,
|
||||
|
||||
/// base58 encoded private key ensuring only this client receives the signature share
|
||||
unused_ed25519: encryption::PrivateKey,
|
||||
|
||||
pedersen_commitments_openings: Vec<Attribute>,
|
||||
|
||||
#[zeroize(skip)]
|
||||
blind_sign_request: BlindSignRequest,
|
||||
}
|
||||
|
||||
impl BandwidthVoucher {
|
||||
pub const PUBLIC_ATTRIBUTES: u32 = 2;
|
||||
pub const PRIVATE_ATTRIBUTES: u32 = 2;
|
||||
pub const ENCODED_ATTRIBUTES: u32 = 4;
|
||||
|
||||
pub fn default_parameters() -> Parameters {
|
||||
// safety: the unwrap is fine here as Self::ENCODED_ATTRIBUTES is non-zero
|
||||
Parameters::new(Self::ENCODED_ATTRIBUTES).unwrap()
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
params: &Parameters,
|
||||
voucher_value: String,
|
||||
@@ -57,24 +79,26 @@ impl BandwidthVoucher {
|
||||
let binding_number = params.random_scalar();
|
||||
let voucher_value_plain = voucher_value.clone();
|
||||
let voucher_info_plain = voucher_info.clone();
|
||||
let voucher_value = hash_to_scalar(voucher_value.as_bytes());
|
||||
let voucher_info = hash_to_scalar(voucher_info.as_bytes());
|
||||
|
||||
let _voucher_value_prehashed = hash_to_scalar(voucher_value);
|
||||
let _voucher_info_prehashed = hash_to_scalar(voucher_info);
|
||||
|
||||
let (pedersen_commitments_openings, blind_sign_request) = prepare_blind_sign(
|
||||
params,
|
||||
&[serial_number, binding_number],
|
||||
&[voucher_value, voucher_info],
|
||||
&[&serial_number, &binding_number],
|
||||
&[&_voucher_value_prehashed, &_voucher_info_prehashed],
|
||||
)
|
||||
.unwrap();
|
||||
BandwidthVoucher {
|
||||
serial_number,
|
||||
binding_number,
|
||||
voucher_value,
|
||||
_voucher_value_prehashed,
|
||||
voucher_value_plain,
|
||||
voucher_info,
|
||||
_voucher_info_prehashed,
|
||||
voucher_info_plain,
|
||||
tx_hash,
|
||||
signing_key,
|
||||
encryption_key,
|
||||
unused_ed25519: encryption_key,
|
||||
pedersen_commitments_openings,
|
||||
blind_sign_request,
|
||||
}
|
||||
@@ -87,7 +111,7 @@ impl BandwidthVoucher {
|
||||
let voucher_info_plain_b = self.voucher_info_plain.as_bytes();
|
||||
let tx_hash_b = self.tx_hash.as_bytes();
|
||||
let signing_key_b = self.signing_key.to_bytes();
|
||||
let encryption_key_b = self.encryption_key.to_bytes();
|
||||
let encryption_key_b = self.unused_ed25519.to_bytes();
|
||||
let blind_sign_request_b = self.blind_sign_request.to_bytes();
|
||||
|
||||
let mut ret = Vec::new();
|
||||
@@ -171,13 +195,13 @@ impl BandwidthVoucher {
|
||||
bytes[var_length_pointer..var_length_pointer + voucher_value_plain_no].to_vec(),
|
||||
)
|
||||
.or_else(utf_err)?;
|
||||
let voucher_value = hash_to_scalar(&voucher_value_plain);
|
||||
let _voucher_value_prehashed = hash_to_scalar(&voucher_value_plain);
|
||||
var_length_pointer += voucher_value_plain_no;
|
||||
let voucher_info_plain = String::from_utf8(
|
||||
bytes[var_length_pointer..var_length_pointer + voucher_info_plain_no].to_vec(),
|
||||
)
|
||||
.or_else(utf_err)?;
|
||||
let voucher_info = hash_to_scalar(&voucher_info_plain);
|
||||
let _voucher_info_prehashed = hash_to_scalar(&voucher_info_plain);
|
||||
var_length_pointer += voucher_info_plain_no;
|
||||
let blind_sign_request = BlindSignRequest::from_bytes(
|
||||
&bytes[var_length_pointer..var_length_pointer + blind_sign_request_no],
|
||||
@@ -196,36 +220,43 @@ impl BandwidthVoucher {
|
||||
Ok(Self {
|
||||
serial_number,
|
||||
binding_number,
|
||||
voucher_value,
|
||||
_voucher_value_prehashed,
|
||||
voucher_value_plain,
|
||||
voucher_info,
|
||||
_voucher_info_prehashed,
|
||||
voucher_info_plain,
|
||||
tx_hash,
|
||||
signing_key,
|
||||
encryption_key,
|
||||
unused_ed25519: encryption_key,
|
||||
pedersen_commitments_openings,
|
||||
blind_sign_request,
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if the plain values correspond to the PublicAttributes
|
||||
pub fn verify_against_plain(values: &[PublicAttribute], plain_values: &[String]) -> bool {
|
||||
pub fn verify_against_plain(values: &[&PublicAttribute], plain_values: &[String]) -> bool {
|
||||
values.len() == 2
|
||||
&& plain_values.len() == 2
|
||||
&& values[0] == hash_to_scalar(&plain_values[0])
|
||||
&& values[1] == hash_to_scalar(&plain_values[1])
|
||||
&& values[0] == &hash_to_scalar(&plain_values[0])
|
||||
&& values[1] == &hash_to_scalar(&plain_values[1])
|
||||
}
|
||||
|
||||
pub fn tx_hash(&self) -> &Hash {
|
||||
&self.tx_hash
|
||||
pub fn tx_hash(&self) -> Hash {
|
||||
self.tx_hash
|
||||
}
|
||||
|
||||
pub fn get_public_attributes(&self) -> Vec<PublicAttribute> {
|
||||
vec![self.voucher_value, self.voucher_info]
|
||||
pub fn get_public_attributes(&self) -> Vec<&PublicAttribute> {
|
||||
vec![
|
||||
&self._voucher_value_prehashed,
|
||||
&self._voucher_info_prehashed,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn identity_key(&self) -> &identity::PrivateKey {
|
||||
&self.signing_key
|
||||
}
|
||||
|
||||
pub fn encryption_key(&self) -> &encryption::PrivateKey {
|
||||
&self.encryption_key
|
||||
&self.unused_ed25519
|
||||
}
|
||||
|
||||
pub fn pedersen_commitments_openings(&self) -> &Vec<Attribute> {
|
||||
@@ -247,27 +278,32 @@ impl BandwidthVoucher {
|
||||
]
|
||||
}
|
||||
|
||||
pub fn get_private_attributes(&self) -> Vec<PrivateAttribute> {
|
||||
vec![self.serial_number, self.binding_number]
|
||||
pub fn get_private_attributes(&self) -> Vec<&PrivateAttribute> {
|
||||
vec![&self.serial_number, &self.binding_number]
|
||||
}
|
||||
|
||||
pub fn sign(&self, request: &BlindSignRequest) -> identity::Signature {
|
||||
pub fn signable_plaintext(request: &BlindSignRequest, tx_hash: Hash) -> Vec<u8> {
|
||||
let mut message = request.to_bytes();
|
||||
message.extend_from_slice(self.tx_hash.to_string().as_bytes());
|
||||
self.signing_key.sign(&message)
|
||||
message.extend_from_slice(tx_hash.as_bytes());
|
||||
message
|
||||
}
|
||||
|
||||
pub fn sign(&self) -> identity::Signature {
|
||||
let message = Self::signable_plaintext(&self.blind_sign_request, self.tx_hash);
|
||||
self.signing_key.sign(message)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_for_spending(
|
||||
voucher_value: u64,
|
||||
voucher_info: String,
|
||||
serial_number: PrivateAttribute,
|
||||
binding_number: PrivateAttribute,
|
||||
serial_number: &PrivateAttribute,
|
||||
binding_number: &PrivateAttribute,
|
||||
epoch_id: u64,
|
||||
signature: &Signature,
|
||||
verification_key: &VerificationKey,
|
||||
) -> Result<Credential, Error> {
|
||||
let params = Parameters::new(TOTAL_ATTRIBUTES)?;
|
||||
let params = Parameters::new(BandwidthVoucher::ENCODED_ATTRIBUTES)?;
|
||||
|
||||
prepare_credential_for_spending(
|
||||
¶ms,
|
||||
@@ -316,24 +352,30 @@ mod test {
|
||||
let deserialized_voucher = BandwidthVoucher::try_from_bytes(&bytes).unwrap();
|
||||
assert_eq!(voucher.serial_number, deserialized_voucher.serial_number);
|
||||
assert_eq!(voucher.binding_number, deserialized_voucher.binding_number);
|
||||
assert_eq!(voucher.voucher_value, deserialized_voucher.voucher_value);
|
||||
assert_eq!(
|
||||
voucher.voucher_value_plain,
|
||||
deserialized_voucher.voucher_value_plain
|
||||
);
|
||||
assert_eq!(voucher.voucher_info, deserialized_voucher.voucher_info);
|
||||
assert_eq!(
|
||||
voucher.voucher_info_plain,
|
||||
deserialized_voucher.voucher_info_plain
|
||||
);
|
||||
assert_eq!(
|
||||
voucher._voucher_value_prehashed,
|
||||
deserialized_voucher._voucher_value_prehashed
|
||||
);
|
||||
assert_eq!(
|
||||
voucher._voucher_info_prehashed,
|
||||
deserialized_voucher._voucher_info_prehashed
|
||||
);
|
||||
assert_eq!(voucher.tx_hash, deserialized_voucher.tx_hash);
|
||||
assert_eq!(
|
||||
voucher.signing_key.to_string(),
|
||||
deserialized_voucher.signing_key.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
voucher.encryption_key.to_string(),
|
||||
deserialized_voucher.encryption_key.to_string()
|
||||
voucher.unused_ed25519.to_string(),
|
||||
deserialized_voucher.unused_ed25519.to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
voucher.pedersen_commitments_openings,
|
||||
@@ -371,11 +413,11 @@ mod test {
|
||||
]
|
||||
));
|
||||
assert!(!BandwidthVoucher::verify_against_plain(
|
||||
&[voucher.get_public_attributes()[0], Attribute::one()],
|
||||
&[voucher.get_public_attributes()[0], &Attribute::one()],
|
||||
&voucher.get_public_attributes_plain()
|
||||
));
|
||||
assert!(!BandwidthVoucher::verify_against_plain(
|
||||
&[Attribute::one(), voucher.get_public_attributes()[1]],
|
||||
&[&Attribute::one(), voucher.get_public_attributes()[1]],
|
||||
&voucher.get_public_attributes_plain()
|
||||
));
|
||||
assert!(BandwidthVoucher::verify_against_plain(
|
||||
|
||||
@@ -2,5 +2,4 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
pub mod bandwidth;
|
||||
pub mod params;
|
||||
pub mod utils;
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use nym_crypto::aes::Aes128;
|
||||
use nym_crypto::blake3;
|
||||
use nym_crypto::ctr;
|
||||
|
||||
type Aes128Ctr = ctr::Ctr64LE<Aes128>;
|
||||
|
||||
/// Hashing algorithm used during hkdf for ephemeral shared key generation per blinded signature
|
||||
/// response encryption.
|
||||
pub type NymApiCredentialHkdfAlgorithm = blake3::Hasher;
|
||||
|
||||
/// Encryption algorithm used for end-to-end encryption of blinded signature response
|
||||
pub type NymApiCredentialEncryptionAlgorithm = Aes128Ctr;
|
||||
@@ -1,18 +1,14 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::coconut::bandwidth::{BandwidthVoucher, PRIVATE_ATTRIBUTES, PUBLIC_ATTRIBUTES};
|
||||
use crate::coconut::params::{NymApiCredentialEncryptionAlgorithm, NymApiCredentialHkdfAlgorithm};
|
||||
use crate::coconut::bandwidth::BandwidthVoucher;
|
||||
use crate::error::Error;
|
||||
use log::{debug, warn};
|
||||
use nym_api_requests::coconut::BlindSignRequestBody;
|
||||
use nym_coconut_interface::{
|
||||
aggregate_signature_shares, aggregate_verification_keys, prove_bandwidth_credential, Attribute,
|
||||
BlindedSignature, Credential, Parameters, Signature, SignatureShare, VerificationKey,
|
||||
Credential, Parameters, Signature, SignatureShare, VerificationKey,
|
||||
};
|
||||
use nym_crypto::asymmetric::encryption::PublicKey;
|
||||
use nym_crypto::shared_key::recompute_shared_key;
|
||||
use nym_crypto::symmetric::stream_cipher;
|
||||
use nym_validator_client::client::CoconutApiClient;
|
||||
|
||||
pub async fn obtain_aggregate_verification_key(
|
||||
@@ -36,47 +32,34 @@ pub async fn obtain_aggregate_verification_key(
|
||||
|
||||
async fn obtain_partial_credential(
|
||||
params: &Parameters,
|
||||
attributes: &BandwidthVoucher,
|
||||
voucher: &BandwidthVoucher,
|
||||
client: &nym_validator_client::client::NymApiClient,
|
||||
validator_vk: &VerificationKey,
|
||||
) -> Result<Signature, Error> {
|
||||
let public_attributes = attributes.get_public_attributes();
|
||||
let public_attributes_plain = attributes.get_public_attributes_plain();
|
||||
let private_attributes = attributes.get_private_attributes();
|
||||
let blind_sign_request = attributes.blind_sign_request();
|
||||
let public_attributes_plain = voucher.get_public_attributes_plain();
|
||||
let blind_sign_request = voucher.blind_sign_request();
|
||||
let request_signature = voucher.sign();
|
||||
|
||||
let blind_sign_request_body = BlindSignRequestBody::new(
|
||||
blind_sign_request,
|
||||
attributes.tx_hash().to_string(),
|
||||
attributes.sign(blind_sign_request).to_base58_string(),
|
||||
&public_attributes,
|
||||
blind_sign_request.clone(),
|
||||
voucher.tx_hash(),
|
||||
request_signature,
|
||||
public_attributes_plain,
|
||||
(public_attributes.len() + private_attributes.len()) as u32,
|
||||
);
|
||||
let response = client.blind_sign(&blind_sign_request_body).await?;
|
||||
let encrypted_signature = response.encrypted_signature;
|
||||
let remote_key = PublicKey::from_bytes(&response.remote_key)?;
|
||||
|
||||
let encryption_key = recompute_shared_key::<
|
||||
NymApiCredentialEncryptionAlgorithm,
|
||||
NymApiCredentialHkdfAlgorithm,
|
||||
>(&remote_key, attributes.encryption_key());
|
||||
let zero_iv = stream_cipher::zero_iv::<NymApiCredentialEncryptionAlgorithm>();
|
||||
let blinded_signature_bytes = stream_cipher::decrypt::<NymApiCredentialEncryptionAlgorithm>(
|
||||
&encryption_key,
|
||||
&zero_iv,
|
||||
&encrypted_signature,
|
||||
);
|
||||
let blinded_signature = response.blinded_signature;
|
||||
|
||||
let blinded_signature = BlindedSignature::from_bytes(&blinded_signature_bytes)?;
|
||||
let public_attributes = voucher.get_public_attributes();
|
||||
let private_attributes = voucher.get_private_attributes();
|
||||
|
||||
let unblinded_signature = blinded_signature.unblind(
|
||||
let unblinded_signature = blinded_signature.unblind_and_verify(
|
||||
params,
|
||||
validator_vk,
|
||||
&private_attributes,
|
||||
&public_attributes,
|
||||
&blind_sign_request.get_commitment_hash(),
|
||||
attributes.pedersen_commitments_openings(),
|
||||
voucher.pedersen_commitments_openings(),
|
||||
)?;
|
||||
|
||||
Ok(unblinded_signature)
|
||||
@@ -84,16 +67,13 @@ async fn obtain_partial_credential(
|
||||
|
||||
pub async fn obtain_aggregate_signature(
|
||||
params: &Parameters,
|
||||
attributes: &BandwidthVoucher,
|
||||
voucher: &BandwidthVoucher,
|
||||
coconut_api_clients: &[CoconutApiClient],
|
||||
threshold: u64,
|
||||
) -> Result<Signature, Error> {
|
||||
if coconut_api_clients.is_empty() {
|
||||
return Err(Error::NoValidatorsAvailable);
|
||||
}
|
||||
let public_attributes = attributes.get_public_attributes();
|
||||
let private_attributes = attributes.get_private_attributes();
|
||||
|
||||
let mut shares = Vec::with_capacity(coconut_api_clients.len());
|
||||
let validators_partial_vks: Vec<_> = coconut_api_clients
|
||||
.iter()
|
||||
@@ -114,7 +94,7 @@ pub async fn obtain_aggregate_signature(
|
||||
|
||||
match obtain_partial_credential(
|
||||
params,
|
||||
attributes,
|
||||
voucher,
|
||||
&coconut_api_client.api_client,
|
||||
&coconut_api_client.verification_key,
|
||||
)
|
||||
@@ -136,6 +116,9 @@ pub async fn obtain_aggregate_signature(
|
||||
return Err(Error::NotEnoughShares);
|
||||
}
|
||||
|
||||
let public_attributes = voucher.get_public_attributes();
|
||||
let private_attributes = voucher.get_private_attributes();
|
||||
|
||||
let mut attributes = Vec::with_capacity(private_attributes.len() + public_attributes.len());
|
||||
attributes.extend_from_slice(&private_attributes);
|
||||
attributes.extend_from_slice(&public_attributes);
|
||||
@@ -150,8 +133,8 @@ pub fn prepare_credential_for_spending(
|
||||
params: &Parameters,
|
||||
voucher_value: u64,
|
||||
voucher_info: String,
|
||||
serial_number: Attribute,
|
||||
binding_number: Attribute,
|
||||
serial_number: &Attribute,
|
||||
binding_number: &Attribute,
|
||||
epoch_id: u64,
|
||||
signature: &Signature,
|
||||
verification_key: &VerificationKey,
|
||||
@@ -165,7 +148,7 @@ pub fn prepare_credential_for_spending(
|
||||
)?;
|
||||
|
||||
Ok(Credential::new(
|
||||
PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES,
|
||||
BandwidthVoucher::ENCODED_ATTRIBUTES,
|
||||
theta,
|
||||
voucher_value,
|
||||
voucher_info,
|
||||
|
||||
@@ -201,6 +201,17 @@ impl<'a> From<&'a PrivateKey> for PublicKey {
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
#[cfg(feature = "rand")]
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||
let x25519_secret = x25519_dalek::StaticSecret::new(rng);
|
||||
|
||||
PrivateKey(x25519_secret)
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; PRIVATE_KEY_SIZE] {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
|
||||
@@ -5,13 +5,14 @@ pub use ed25519_dalek::ed25519::signature::Signature as SignatureTrait;
|
||||
pub use ed25519_dalek::SignatureError;
|
||||
pub use ed25519_dalek::{Verifier, PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH, SIGNATURE_LENGTH};
|
||||
use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
|
||||
#[cfg(feature = "sphinx")]
|
||||
use nym_sphinx_types::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
use thiserror::Error;
|
||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||
|
||||
#[cfg(feature = "sphinx")]
|
||||
use nym_sphinx_types::{DestinationAddressBytes, DESTINATION_ADDRESS_LENGTH};
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
use rand::{CryptoRng, RngCore};
|
||||
#[cfg(feature = "serde")]
|
||||
@@ -224,6 +225,17 @@ impl<'a> From<&'a PrivateKey> for PublicKey {
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
#[cfg(feature = "rand")]
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R) -> Self {
|
||||
let ed25519_secret = ed25519_dalek::SecretKey::generate(rng);
|
||||
|
||||
PrivateKey(ed25519_secret)
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
@@ -295,7 +307,7 @@ impl PemStorableKey for PrivateKey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Signature(ed25519_dalek::Signature);
|
||||
|
||||
impl Signature {
|
||||
@@ -319,6 +331,14 @@ impl Signature {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Signature {
|
||||
type Err = Ed25519RecoveryError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Signature::from_base58_string(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Signature {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
|
||||
@@ -3,6 +3,7 @@ name = "nym-dkg"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -11,7 +12,7 @@ bitvec = "1.0.0"
|
||||
|
||||
# unfortunately until https://github.com/zkcrypto/bls12_381/issues/10 is resolved, we have to rely on the fork
|
||||
# as we need to be able to serialize Gt so that we could create the lookup table for baby-step-giant-step algorithm
|
||||
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch ="gt-serialisation", default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] }
|
||||
bls12_381 = { workspace = true, default-features = false, features = ["alloc", "pairings", "experimental", "zeroize"] }
|
||||
nym-contracts-common = { path = "../cosmwasm-smart-contracts/contracts-common", optional = true }
|
||||
bs58 = "0.4"
|
||||
|
||||
@@ -29,11 +30,11 @@ zeroize = { workspace = true, features = ["zeroize_derive"] }
|
||||
nym-pemstore = { path = "../pemstore" }
|
||||
|
||||
[dependencies.group]
|
||||
version = "0.11"
|
||||
workspace = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.ff]
|
||||
version = "0.11"
|
||||
workspace = true
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-execute"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
@@ -81,6 +81,15 @@ ExitPolicy accept6 *6:119
|
||||
ExitPolicy accept *4:120
|
||||
ExitPolicy reject6 [FC00::]/7:*
|
||||
|
||||
# Portless
|
||||
ExitPolicy accept *:0
|
||||
ExitPolicy accept *4:0
|
||||
ExitPolicy accept *6:0
|
||||
|
||||
ExitPolicy reject *:0
|
||||
ExitPolicy reject *4:0
|
||||
ExitPolicy reject *6:0
|
||||
|
||||
#ExitPolicy accept *:8080 #and another comment here
|
||||
|
||||
ExitPolicy reject FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
|
||||
@@ -184,6 +193,60 @@ ExitPolicy reject *:*
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy accept *:0
|
||||
expected.push(
|
||||
Accept,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy accept *4:0
|
||||
expected.push(
|
||||
Accept,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V4Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy accept *6:0
|
||||
expected.push(
|
||||
Accept,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V6Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy reject *:0
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy reject *4:0
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V4Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy reject *6:0
|
||||
expected.push(
|
||||
Reject,
|
||||
AddressPortPattern {
|
||||
ip_pattern: IpPattern::V6Star,
|
||||
ports: PortRange::new_zero(),
|
||||
},
|
||||
);
|
||||
|
||||
// ExitPolicy FE80:0000:0000:0000:0202:B3FF:FE1E:8329:*
|
||||
expected.push(
|
||||
Reject,
|
||||
|
||||
@@ -264,7 +264,13 @@ mod stringified_ip_pattern {
|
||||
impl AddressPortPattern {
|
||||
/// Return true iff this pattern matches a given address and port.
|
||||
pub fn matches(&self, addr: &IpAddr, port: u16) -> bool {
|
||||
self.ip_pattern.matches(addr) && self.ports.contains(port)
|
||||
// For backward compatibility, we treat port 0 as a wildcard until all gateways have
|
||||
// upgraded, at which point we can add *:0 to the policy list.
|
||||
if port == 0 {
|
||||
self.ip_pattern.matches(addr)
|
||||
} else {
|
||||
self.ip_pattern.matches(addr) && self.ports.contains(port)
|
||||
}
|
||||
}
|
||||
|
||||
/// As matches, but accept a SocketAddr.
|
||||
@@ -395,19 +401,9 @@ fn parse_addr(s: &str) -> Result<IpAddr, PolicyError> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Helper: try to parse a port making sure it's non-zero
|
||||
fn parse_port(s: &str) -> Result<u16, PolicyError> {
|
||||
let port = s
|
||||
.parse::<u16>()
|
||||
.map_err(|_| PolicyError::InvalidPort { raw: s.to_string() })?;
|
||||
|
||||
if port == 0 {
|
||||
Err(PolicyError::InvalidPort {
|
||||
raw: port.to_string(),
|
||||
})
|
||||
} else {
|
||||
Ok(port)
|
||||
}
|
||||
s.parse::<u16>()
|
||||
.map_err(|_| PolicyError::InvalidPort { raw: s.to_string() })
|
||||
}
|
||||
|
||||
impl FromStr for IpPattern {
|
||||
@@ -494,6 +490,10 @@ impl PortRange {
|
||||
PortRange::new_unchecked(1, 65535)
|
||||
}
|
||||
|
||||
pub fn new_zero() -> Self {
|
||||
PortRange { start: 0, end: 0 }
|
||||
}
|
||||
|
||||
/// Create a new PortRange.
|
||||
///
|
||||
/// The Portrange contains all ports between `start` and `end` inclusive.
|
||||
@@ -574,6 +574,7 @@ mod test {
|
||||
|
||||
check("marzipan:80");
|
||||
check("1.2.3.4:90-80");
|
||||
check("1.2.3.4:0-80");
|
||||
check("1.2.3.4/100:8888");
|
||||
check("[1.2.3.4]/16:80");
|
||||
check("[::1]/130:8888");
|
||||
@@ -612,6 +613,22 @@ mod test {
|
||||
|
||||
check("0.0.0.0/0:*", &["127.0.0.1:80"], &["[f00b::]:80"]);
|
||||
check("[::]/0:*", &["[f00b::]:80"], &["127.0.0.1:80"]);
|
||||
|
||||
check(
|
||||
"*:0",
|
||||
&["1.2.3.4:0", "[::1]:0", "9.0.0.0:0"],
|
||||
&["1.2.3.4:443", "[::1]:500", "9.0.0.0:80", "[::1]:80"],
|
||||
);
|
||||
check(
|
||||
"*4:0",
|
||||
&["1.2.3.4:0", "9.0.0.0:0"],
|
||||
&["1.2.3.4:443", "9.0.0.0:80", "[::1]:0", "[::1]:80"],
|
||||
);
|
||||
check(
|
||||
"*6:0",
|
||||
&["[::1]:0"],
|
||||
&["[::1]:80", "1.2.3.4:0", "1.2.3.4:443"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -620,6 +637,7 @@ mod test {
|
||||
policy.push(AddressPolicyAction::Accept, "*:443".parse()?);
|
||||
policy.push(AddressPolicyAction::Accept, "[::1]:80".parse()?);
|
||||
policy.push(AddressPolicyAction::Reject, "*:80".parse()?);
|
||||
policy.push(AddressPolicyAction::Accept, "*:0".parse()?);
|
||||
|
||||
let policy = policy; // drop mut
|
||||
assert!(policy
|
||||
@@ -640,6 +658,9 @@ mod test {
|
||||
assert!(policy
|
||||
.allows_sockaddr(&"127.0.0.1:66".parse().unwrap())
|
||||
.is_none());
|
||||
assert!(policy
|
||||
.allows_sockaddr(&"127.0.0.1:0".parse().unwrap())
|
||||
.unwrap());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -672,7 +693,6 @@ mod test {
|
||||
assert_eq!("*".parse::<PortRange>().unwrap(), PortRange::new_all());
|
||||
|
||||
assert!("hello".parse::<PortRange>().is_err());
|
||||
assert!("0".parse::<PortRange>().is_err());
|
||||
assert!("65536".parse::<PortRange>().is_err());
|
||||
assert!("65537".parse::<PortRange>().is_err());
|
||||
assert!("1-2-3".parse::<PortRange>().is_err());
|
||||
@@ -680,6 +700,9 @@ mod test {
|
||||
assert!("1-".parse::<PortRange>().is_err());
|
||||
assert!("-2".parse::<PortRange>().is_err());
|
||||
assert!("-".parse::<PortRange>().is_err());
|
||||
|
||||
assert_eq!("0".parse::<PortRange>().unwrap(), PortRange::new_zero(),);
|
||||
assert!("0-1".parse::<PortRange>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "ledger"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -10,4 +11,4 @@ bip32 = "0.5.1"
|
||||
k256 = { workspace = true }
|
||||
ledger-transport = "0.10.0"
|
||||
ledger-transport-hid = "0.10.0"
|
||||
thiserror = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -3,6 +3,7 @@ name = "nym-mixnode-common"
|
||||
version = "0.1.0"
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@@ -20,7 +21,7 @@ tokio = { version = "1.24.1", features = [
|
||||
"net",
|
||||
"io-util",
|
||||
] }
|
||||
tokio-util = { version = "0.7.4", features = ["codec"] }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
url = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
|
||||
@@ -108,13 +108,13 @@ impl ConnectionHandler {
|
||||
let reply_packet = match maybe_echo_packet {
|
||||
Some(Ok(echo_packet)) => self.handle_echo_packet(echo_packet),
|
||||
Some(Err(err)) => {
|
||||
error!(
|
||||
debug!(
|
||||
"The socket connection got corrupted with error: {err}. Closing the socket",
|
||||
);
|
||||
return;
|
||||
}
|
||||
None => {
|
||||
error!("The socket connection got terminated by the remote!");
|
||||
debug!("The socket connection got terminated by the remote!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -125,7 +125,7 @@ impl ConnectionHandler {
|
||||
.write_all(reply_packet.to_bytes().as_ref())
|
||||
.await
|
||||
{
|
||||
error!(
|
||||
debug!(
|
||||
"Failed to write reply packet back to the sender - {}. Closing the socket on our end",
|
||||
err
|
||||
);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
use crate::var_names;
|
||||
use crate::{DenomDetails, ValidatorDetails};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub const NETWORK_NAME: &str = "mainnet";
|
||||
|
||||
@@ -60,6 +61,12 @@ pub fn read_var_if_not_default(var: &str) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_parsed_var_if_not_default<T: FromStr>(var: &str) -> Option<Result<T, T::Err>> {
|
||||
read_var_if_not_default(var)
|
||||
.as_deref()
|
||||
.map(FromStr::from_str)
|
||||
}
|
||||
|
||||
pub fn export_to_env() {
|
||||
set_var_to_default(var_names::CONFIGURED, "true");
|
||||
set_var_to_default(var_names::NETWORK_NAME, NETWORK_NAME);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "nym-node-tester-utils"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ name = "nym-nonexhaustive-delayqueue"
|
||||
version = "0.1.0"
|
||||
authors = ["Jędrzej Stuczyński <andrew@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@ name = "nym-coconut"
|
||||
version = "0.5.0"
|
||||
authors = ["Jedrzej Stuczynski <andrew@nymtech.net>", "Ania Piotrowska <ania@nymtech.net>"]
|
||||
edition = "2021"
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bls12_381 = { git = "https://github.com/jstuczyn/bls12_381", branch ="gt-serialisation", default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||
bls12_381 = { workspace = true, default-features = false, features = ["pairings", "alloc", "experimental"] }
|
||||
itertools = "0.10"
|
||||
digest = "0.9"
|
||||
rand = "0.8"
|
||||
@@ -16,16 +17,17 @@ serde = { workspace = true }
|
||||
serde_derive = "1.0"
|
||||
bs58 = "0.4.0"
|
||||
sha2 = "0.9"
|
||||
zeroize = { workspace = true, optional = true }
|
||||
|
||||
nym-dkg = { path = "../dkg" }
|
||||
nym-pemstore = { path = "../pemstore" }
|
||||
|
||||
[dependencies.ff]
|
||||
version = "0.11"
|
||||
workspace = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.group]
|
||||
version = "0.11"
|
||||
workspace = true
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -38,7 +40,9 @@ name = "benchmarks"
|
||||
harness = false
|
||||
|
||||
[features]
|
||||
key-zeroize = ["zeroize", "bls12_381/zeroize"]
|
||||
default = []
|
||||
|
||||
|
||||
[target.'cfg(target_env = "wasm32-unknown-unknown")'.dependencies]
|
||||
getrandom = { version="0.2", features=["js"] }
|
||||
|
||||
@@ -7,7 +7,7 @@ use ff::Field;
|
||||
use group::{Curve, Group};
|
||||
use nym_coconut::{
|
||||
aggregate_signature_shares, aggregate_verification_keys, blind_sign, prepare_blind_sign,
|
||||
prove_bandwidth_credential, setup, ttp_keygen, verify_credential,
|
||||
prove_bandwidth_credential, random_scalars_refs, setup, ttp_keygen, verify_credential,
|
||||
verify_partial_blind_signature, Attribute, BlindedSignature, Parameters, Signature,
|
||||
SignatureShare, VerificationKey,
|
||||
};
|
||||
@@ -66,8 +66,8 @@ fn unblind_and_aggregate(
|
||||
params: &Parameters,
|
||||
blinded_signatures: &[BlindedSignature],
|
||||
partial_verification_keys: &[VerificationKey],
|
||||
private_attributes: &[Attribute],
|
||||
public_attributes: &[Attribute],
|
||||
private_attributes: &[&Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
commitment_hash: &G1Projective,
|
||||
pedersen_commitments_openings: &[Scalar],
|
||||
verification_key: &VerificationKey,
|
||||
@@ -78,7 +78,7 @@ fn unblind_and_aggregate(
|
||||
.zip(partial_verification_keys.iter())
|
||||
.map(|(signature, partial_verification_key)| {
|
||||
signature
|
||||
.unblind(
|
||||
.unblind_and_verify(
|
||||
params,
|
||||
partial_verification_key,
|
||||
private_attributes,
|
||||
@@ -171,10 +171,10 @@ fn bench_coconut(c: &mut Criterion) {
|
||||
|
||||
let params = setup(case.num_public_attrs + case.num_private_attrs).unwrap();
|
||||
|
||||
let public_attributes = params.n_random_scalars(case.num_public_attrs as usize);
|
||||
random_scalars_refs!(public_attributes, params, case.num_public_attrs as usize);
|
||||
let serial_number = params.random_scalar();
|
||||
let binding_number = params.random_scalar();
|
||||
let private_attributes = vec![serial_number, binding_number];
|
||||
let private_attributes = vec![&serial_number, &binding_number];
|
||||
|
||||
// The prepare blind sign is performed by the user
|
||||
let (pedersen_commitments_openings, blind_sign_request) =
|
||||
@@ -213,7 +213,7 @@ fn bench_coconut(c: &mut Criterion) {
|
||||
b.iter(|| {
|
||||
blind_sign(
|
||||
¶ms,
|
||||
&keypair.secret_key(),
|
||||
keypair.secret_key(),
|
||||
&blind_sign_request,
|
||||
&public_attributes,
|
||||
)
|
||||
@@ -228,7 +228,7 @@ fn bench_coconut(c: &mut Criterion) {
|
||||
for keypair in coconut_keypairs.iter() {
|
||||
let blinded_signature = blind_sign(
|
||||
¶ms,
|
||||
&keypair.secret_key(),
|
||||
keypair.secret_key(),
|
||||
&blind_sign_request,
|
||||
&public_attributes,
|
||||
)
|
||||
@@ -238,7 +238,7 @@ fn bench_coconut(c: &mut Criterion) {
|
||||
|
||||
let verification_keys: Vec<VerificationKey> = coconut_keypairs
|
||||
.iter()
|
||||
.map(|keypair| keypair.verification_key())
|
||||
.map(|keypair| keypair.verification_key().clone())
|
||||
.collect();
|
||||
|
||||
// verify a random partial blind signature
|
||||
@@ -310,8 +310,8 @@ fn bench_coconut(c: &mut Criterion) {
|
||||
¶ms,
|
||||
&aggr_verification_key,
|
||||
&aggregated_signature,
|
||||
serial_number,
|
||||
binding_number,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -329,8 +329,8 @@ fn bench_coconut(c: &mut Criterion) {
|
||||
¶ms,
|
||||
&aggr_verification_key,
|
||||
&aggregated_signature,
|
||||
serial_number,
|
||||
binding_number,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
|
||||
@@ -34,7 +34,10 @@ impl TryFrom<&[u8]> for Ciphertext {
|
||||
)));
|
||||
}
|
||||
|
||||
// safety: we just checked for the length so the unwraps are fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let c1_bytes: &[u8; 48] = &bytes[..48].try_into().unwrap();
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let c2_bytes: &[u8; 48] = &bytes[48..].try_into().unwrap();
|
||||
|
||||
let c1 = try_deserialize_g1_projective(
|
||||
@@ -112,7 +115,16 @@ impl Bytable for PrivateKey {
|
||||
}
|
||||
|
||||
fn try_from_byte_slice(slice: &[u8]) -> Result<Self> {
|
||||
PrivateKey::from_bytes(slice.try_into().unwrap())
|
||||
let received = slice.len();
|
||||
let Ok(arr) = slice.try_into() else {
|
||||
return Err(CoconutError::UnexpectedArrayLength {
|
||||
typ: "elgamal::PrivateKey".to_string(),
|
||||
received,
|
||||
expected: 32,
|
||||
});
|
||||
};
|
||||
|
||||
PrivateKey::from_bytes(arr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,21 +157,36 @@ impl PublicKey {
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 48] {
|
||||
self.to_byte_vec().try_into().unwrap()
|
||||
self.0.to_affine().to_compressed()
|
||||
}
|
||||
|
||||
pub fn from_bytes(bytes: &[u8; 48]) -> Result<PublicKey> {
|
||||
Ok(PublicKey::try_from(bytes.to_vec().as_slice()).unwrap())
|
||||
try_deserialize_g1_projective(
|
||||
bytes,
|
||||
CoconutError::Deserialization(
|
||||
"Failed to deserialize compressed ElGamal public key".to_string(),
|
||||
),
|
||||
)
|
||||
.map(PublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
impl Bytable for PublicKey {
|
||||
fn to_byte_vec(&self) -> Vec<u8> {
|
||||
self.0.to_affine().to_compressed().into()
|
||||
self.to_bytes().into()
|
||||
}
|
||||
|
||||
fn try_from_byte_slice(slice: &[u8]) -> Result<Self> {
|
||||
Ok(PublicKey::from_bytes(slice.try_into().unwrap()).unwrap())
|
||||
let received = slice.len();
|
||||
let Ok(arr) = slice.try_into() else {
|
||||
return Err(CoconutError::UnexpectedArrayLength {
|
||||
typ: "elgamal::PublicKey".to_string(),
|
||||
received,
|
||||
expected: 48,
|
||||
});
|
||||
};
|
||||
|
||||
PublicKey::from_bytes(arr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,13 +194,7 @@ impl TryFrom<&[u8]> for PublicKey {
|
||||
type Error = CoconutError;
|
||||
|
||||
fn try_from(slice: &[u8]) -> Result<PublicKey> {
|
||||
try_deserialize_g1_projective(
|
||||
slice.try_into().unwrap(),
|
||||
CoconutError::Deserialization(
|
||||
"Failed to deserialize compressed ElGamal public key".to_string(),
|
||||
),
|
||||
)
|
||||
.map(PublicKey)
|
||||
PublicKey::try_from_byte_slice(slice)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +246,7 @@ pub fn elgamal_keygen(params: &Parameters) -> ElGamalKeyPair {
|
||||
|
||||
pub fn compute_attribute_encryption(
|
||||
params: &Parameters,
|
||||
private_attributes: &[Attribute],
|
||||
private_attributes: &[&Attribute],
|
||||
pub_key: &PublicKey,
|
||||
commitment_hash: G1Projective,
|
||||
) -> (Vec<Ciphertext>, Vec<EphemeralKey>) {
|
||||
|
||||
@@ -50,4 +50,20 @@ pub enum CoconutError {
|
||||
modulus: usize,
|
||||
object: String,
|
||||
},
|
||||
|
||||
#[error("received an array of unexpected size for deserialization of {typ}. got {received} but expected {expected}")]
|
||||
UnexpectedArrayLength {
|
||||
typ: String,
|
||||
received: usize,
|
||||
expected: usize,
|
||||
},
|
||||
|
||||
#[error("failed to decode the base58 representation: {0}")]
|
||||
Base58DecodingFailure(#[from] bs58::decode::Error),
|
||||
|
||||
#[error("failed to deserialize scalar from the received bytes - it might not have been canonically encoded")]
|
||||
ScalarDeserializationFailure,
|
||||
|
||||
#[error("failed to deserialize G1Projective point from the received bytes - it might not have been canonically encoded")]
|
||||
G1ProjectiveDeserializationFailure,
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::convert::TryInto;
|
||||
#![warn(clippy::expect_used)]
|
||||
#![warn(clippy::unwrap_used)]
|
||||
|
||||
use bls12_381::Scalar;
|
||||
|
||||
pub use crate::traits::Bytable;
|
||||
pub use elgamal::elgamal_keygen;
|
||||
pub use elgamal::ElGamalKeyPair;
|
||||
pub use elgamal::PublicKey;
|
||||
@@ -30,6 +28,7 @@ pub use scheme::BlindedSignature;
|
||||
pub use scheme::Signature;
|
||||
pub use scheme::SignatureShare;
|
||||
pub use traits::Base58;
|
||||
pub use traits::Bytable;
|
||||
pub use utils::hash_to_scalar;
|
||||
|
||||
pub mod elgamal;
|
||||
@@ -41,18 +40,6 @@ pub mod tests;
|
||||
mod traits;
|
||||
mod utils;
|
||||
|
||||
pub type Attribute = Scalar;
|
||||
pub type Attribute = bls12_381::Scalar;
|
||||
pub type PrivateAttribute = Attribute;
|
||||
pub type PublicAttribute = Attribute;
|
||||
|
||||
impl Bytable for Attribute {
|
||||
fn to_byte_vec(&self) -> Vec<u8> {
|
||||
self.to_bytes().to_vec()
|
||||
}
|
||||
|
||||
fn try_from_byte_slice(slice: &[u8]) -> Result<Self, CoconutError> {
|
||||
Ok(Attribute::from_bytes(slice.try_into().unwrap()).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl Base58 for Attribute {}
|
||||
|
||||
@@ -91,8 +91,8 @@ impl ProofCmCs {
|
||||
commitment_opening: &Scalar,
|
||||
commitments: &[G1Projective],
|
||||
pedersen_commitments_openings: &[Scalar],
|
||||
private_attributes: &[Attribute],
|
||||
public_attributes: &[Attribute],
|
||||
private_attributes: &[&Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
) -> Self {
|
||||
// note: this is only called from `prepare_blind_sign` that already checks
|
||||
// whether private attributes are non-empty and whether we don't have too many
|
||||
@@ -162,11 +162,8 @@ impl ProofCmCs {
|
||||
&challenge,
|
||||
&pedersen_commitments_openings.iter().collect::<Vec<_>>(),
|
||||
);
|
||||
let response_attributes = produce_responses(
|
||||
&witness_attributes,
|
||||
&challenge,
|
||||
&private_attributes.iter().collect::<Vec<_>>(),
|
||||
);
|
||||
let response_attributes =
|
||||
produce_responses(&witness_attributes, &challenge, private_attributes);
|
||||
|
||||
ProofCmCs {
|
||||
challenge,
|
||||
@@ -181,7 +178,7 @@ impl ProofCmCs {
|
||||
params: &Parameters,
|
||||
commitment: &G1Projective,
|
||||
commitments: &[G1Projective],
|
||||
public_attributes: &[Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
) -> bool {
|
||||
if self.response_attributes.len() != commitments.len() {
|
||||
return false;
|
||||
@@ -203,7 +200,7 @@ impl ProofCmCs {
|
||||
- public_attributes
|
||||
.iter()
|
||||
.zip(params.gen_hs().iter().skip(self.response_attributes.len()))
|
||||
.map(|(pub_attr, hs)| hs * pub_attr)
|
||||
.map(|(&pub_attr, hs)| hs * pub_attr)
|
||||
.sum::<G1Projective>())
|
||||
* self.challenge
|
||||
+ g1 * self.response_opening
|
||||
@@ -280,8 +277,12 @@ impl ProofCmCs {
|
||||
}
|
||||
|
||||
let mut idx = 0;
|
||||
// safety: bound checked + constant offset
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let challenge_bytes = bytes[idx..idx + 32].try_into().unwrap();
|
||||
idx += 32;
|
||||
// safety: bound checked + constant offset
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let response_opening_bytes = bytes[idx..idx + 32].try_into().unwrap();
|
||||
idx += 32;
|
||||
|
||||
@@ -297,6 +298,8 @@ impl ProofCmCs {
|
||||
),
|
||||
)?;
|
||||
|
||||
// safety: bound checked + constant offset
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let ro_len = u64::from_le_bytes(bytes[idx..idx + 8].try_into().unwrap());
|
||||
idx += 8;
|
||||
if bytes[idx..].len() < ro_len as usize * 32 + 8 {
|
||||
@@ -313,6 +316,8 @@ impl ProofCmCs {
|
||||
CoconutError::Deserialization("Failed to deserialize openings response".to_string()),
|
||||
)?;
|
||||
|
||||
// safety: bound checked + constant offset
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let rm_len = u64::from_le_bytes(bytes[ro_end..ro_end + 8].try_into().unwrap());
|
||||
let response_attributes = try_deserialize_scalar_vec(
|
||||
rm_len,
|
||||
@@ -462,7 +467,7 @@ impl ProofKappaZeta {
|
||||
|
||||
pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
||||
// at the very minimum there must be a single attribute being proven
|
||||
if bytes.len() < 32 * 4 || (bytes.len()) % 32 != 0 {
|
||||
if bytes.len() != 128 {
|
||||
return Err(CoconutError::DeserializationInvalidLength {
|
||||
actual: bytes.len(),
|
||||
modulus_target: bytes.len(),
|
||||
@@ -472,24 +477,32 @@ impl ProofKappaZeta {
|
||||
});
|
||||
}
|
||||
|
||||
// safety: bound checked + constant offset
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let challenge_bytes = bytes[..32].try_into().unwrap();
|
||||
let challenge = try_deserialize_scalar(
|
||||
&challenge_bytes,
|
||||
CoconutError::Deserialization("Failed to deserialize challenge".to_string()),
|
||||
)?;
|
||||
|
||||
// safety: bound checked + constant offset
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let serial_number_bytes = &bytes[32..64].try_into().unwrap();
|
||||
let response_serial_number = try_deserialize_scalar(
|
||||
serial_number_bytes,
|
||||
CoconutError::Deserialization("failed to deserialize the serial number".to_string()),
|
||||
)?;
|
||||
|
||||
// safety: bound checked + constant offset
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let binding_number_bytes = &bytes[64..96].try_into().unwrap();
|
||||
let response_binding_number = try_deserialize_scalar(
|
||||
binding_number_bytes,
|
||||
CoconutError::Deserialization("failed to deserialize the binding number".to_string()),
|
||||
)?;
|
||||
|
||||
// safety: bound checked + constant offset
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let blinder_bytes = bytes[96..].try_into().unwrap();
|
||||
let response_blinder = try_deserialize_scalar(
|
||||
&blinder_bytes,
|
||||
@@ -512,14 +525,13 @@ impl ProofKappaZeta {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use group::Group;
|
||||
use rand::thread_rng;
|
||||
|
||||
use super::*;
|
||||
use crate::scheme::keygen::keygen;
|
||||
use crate::scheme::setup::setup;
|
||||
use crate::scheme::verification::{compute_kappa, compute_zeta};
|
||||
|
||||
use super::*;
|
||||
use crate::tests::helpers::random_scalars_refs;
|
||||
use group::Group;
|
||||
use rand::thread_rng;
|
||||
|
||||
#[test]
|
||||
fn proof_cm_cs_bytes_roundtrip() {
|
||||
@@ -530,7 +542,7 @@ mod tests {
|
||||
let r = params.random_scalar();
|
||||
let cms: [G1Projective; 1] = [G1Projective::random(&mut rng)];
|
||||
let rs = params.n_random_scalars(1);
|
||||
let private_attributes = params.n_random_scalars(1);
|
||||
random_scalars_refs!(private_attributes, params, 1);
|
||||
|
||||
// 0 public 1 private
|
||||
let pi_s = ProofCmCs::construct(¶ms, &cm, &r, &cms, &rs, &private_attributes, &[]);
|
||||
@@ -546,7 +558,7 @@ mod tests {
|
||||
G1Projective::random(&mut rng),
|
||||
];
|
||||
let rs = params.n_random_scalars(2);
|
||||
let private_attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(private_attributes, params, 2);
|
||||
|
||||
// 0 public 2 privates
|
||||
let pi_s = ProofCmCs::construct(¶ms, &cm, &r, &cms, &rs, &private_attributes, &[]);
|
||||
@@ -562,20 +574,20 @@ mod tests {
|
||||
let keypair = keygen(¶ms);
|
||||
|
||||
// we don't care about 'correctness' of the proof. only whether we can correctly recover it from bytes
|
||||
let serial_number = params.random_scalar();
|
||||
let binding_number = params.random_scalar();
|
||||
let serial_number = ¶ms.random_scalar();
|
||||
let binding_number = ¶ms.random_scalar();
|
||||
let private_attributes = vec![serial_number, binding_number];
|
||||
|
||||
let r = params.random_scalar();
|
||||
let kappa = compute_kappa(¶ms, &keypair.verification_key(), &private_attributes, r);
|
||||
let kappa = compute_kappa(¶ms, keypair.verification_key(), &private_attributes, r);
|
||||
let zeta = compute_zeta(¶ms, serial_number);
|
||||
|
||||
// 0 public 2 private
|
||||
let pi_v = ProofKappaZeta::construct(
|
||||
¶ms,
|
||||
&keypair.verification_key(),
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
keypair.verification_key(),
|
||||
serial_number,
|
||||
binding_number,
|
||||
&r,
|
||||
&kappa,
|
||||
&zeta,
|
||||
@@ -592,9 +604,9 @@ mod tests {
|
||||
|
||||
let pi_v = ProofKappaZeta::construct(
|
||||
¶ms,
|
||||
&keypair.verification_key(),
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
keypair.verification_key(),
|
||||
serial_number,
|
||||
binding_number,
|
||||
&r,
|
||||
&kappa,
|
||||
&zeta,
|
||||
|
||||
@@ -83,7 +83,7 @@ pub fn aggregate_verification_keys(
|
||||
pub fn aggregate_signatures(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKey,
|
||||
attributes: &[Attribute],
|
||||
attributes: &[&Attribute],
|
||||
signatures: &[PartialSignature],
|
||||
indices: Option<&[SignerIndex]>,
|
||||
) -> Result<Signature> {
|
||||
@@ -100,7 +100,7 @@ pub fn aggregate_signatures(
|
||||
let tmp = attributes
|
||||
.iter()
|
||||
.zip(verification_key.beta_g2.iter())
|
||||
.map(|(attr, beta_i)| beta_i * attr)
|
||||
.map(|(&attr, beta_i)| beta_i * attr)
|
||||
.sum::<G2Projective>();
|
||||
|
||||
if !check_bilinear_pairing(
|
||||
@@ -119,7 +119,7 @@ pub fn aggregate_signatures(
|
||||
pub fn aggregate_signature_shares(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKey,
|
||||
attributes: &[Attribute],
|
||||
attributes: &[&Attribute],
|
||||
shares: &[SignatureShare],
|
||||
) -> Result<Signature> {
|
||||
let (signatures, indices): (Vec<_>, Vec<_>) = shares
|
||||
@@ -138,13 +138,13 @@ pub fn aggregate_signature_shares(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bls12_381::G1Projective;
|
||||
use group::Group;
|
||||
|
||||
use crate::scheme::issuance::sign;
|
||||
use crate::scheme::keygen::ttp_keygen;
|
||||
use crate::scheme::setup::Parameters;
|
||||
use crate::scheme::verification::verify;
|
||||
use crate::tests::helpers::random_scalars_refs;
|
||||
use bls12_381::G1Projective;
|
||||
use group::Group;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -155,7 +155,7 @@ mod tests {
|
||||
|
||||
let vks = keypairs
|
||||
.into_iter()
|
||||
.map(|keypair| keypair.verification_key())
|
||||
.map(|keypair| keypair.verification_key().clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let aggr_vk1 = aggregate_verification_keys(&vks[..3], Some(&[1, 2, 3])).unwrap();
|
||||
@@ -212,13 +212,18 @@ mod tests {
|
||||
#[test]
|
||||
fn signature_aggregation_works_for_any_subset_of_signatures() {
|
||||
let mut params = Parameters::new(2).unwrap();
|
||||
let attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(attributes, params, 2);
|
||||
|
||||
let keypairs = ttp_keygen(¶ms, 3, 5).unwrap();
|
||||
|
||||
let (sks, vks): (Vec<_>, Vec<_>) = keypairs
|
||||
.into_iter()
|
||||
.map(|keypair| (keypair.secret_key(), keypair.verification_key()))
|
||||
.map(|keypair| {
|
||||
(
|
||||
keypair.secret_key().clone(),
|
||||
keypair.verification_key().clone(),
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
|
||||
let sigs = sks
|
||||
@@ -312,12 +317,17 @@ mod tests {
|
||||
fn signature_aggregation_doesnt_work_for_empty_set_of_signatures() {
|
||||
let signatures: Vec<Signature> = vec![];
|
||||
let params = Parameters::new(2).unwrap();
|
||||
let attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(attributes, params, 2);
|
||||
let keypairs = ttp_keygen(¶ms, 3, 5).unwrap();
|
||||
|
||||
let (_, vks): (Vec<_>, Vec<_>) = keypairs
|
||||
.into_iter()
|
||||
.map(|keypair| (keypair.secret_key(), keypair.verification_key()))
|
||||
.map(|keypair| {
|
||||
(
|
||||
keypair.secret_key().clone(),
|
||||
keypair.verification_key().clone(),
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
|
||||
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
|
||||
@@ -330,11 +340,16 @@ mod tests {
|
||||
fn signature_aggregation_doesnt_work_if_indices_have_invalid_length() {
|
||||
let signatures = vec![random_signature()];
|
||||
let params = Parameters::new(2).unwrap();
|
||||
let attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(attributes, params, 2);
|
||||
let keypairs = ttp_keygen(¶ms, 3, 5).unwrap();
|
||||
let (_, vks): (Vec<_>, Vec<_>) = keypairs
|
||||
.into_iter()
|
||||
.map(|keypair| (keypair.secret_key(), keypair.verification_key()))
|
||||
.map(|keypair| {
|
||||
(
|
||||
keypair.secret_key().clone(),
|
||||
keypair.verification_key().clone(),
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
|
||||
|
||||
@@ -356,11 +371,16 @@ mod tests {
|
||||
fn signature_aggregation_doesnt_work_for_non_unique_indices() {
|
||||
let signatures = vec![random_signature(), random_signature()];
|
||||
let params = Parameters::new(2).unwrap();
|
||||
let attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(attributes, params, 2);
|
||||
let keypairs = ttp_keygen(¶ms, 3, 5).unwrap();
|
||||
let (_, vks): (Vec<_>, Vec<_>) = keypairs
|
||||
.into_iter()
|
||||
.map(|keypair| (keypair.secret_key(), keypair.verification_key()))
|
||||
.map(|keypair| {
|
||||
(
|
||||
keypair.secret_key().clone(),
|
||||
keypair.verification_key().clone(),
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
let aggr_vk_all = aggregate_verification_keys(&vks, None).unwrap();
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ impl TryFrom<&[u8]> for BlindedSerialNumber {
|
||||
));
|
||||
}
|
||||
|
||||
// safety: we've just made a check for 96 bytes
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let inner = try_deserialize_g2_projective(
|
||||
&bytes.try_into().unwrap(),
|
||||
CoconutError::Deserialization(
|
||||
|
||||
@@ -54,6 +54,8 @@ impl TryFrom<&[u8]> for BlindSignRequest {
|
||||
let commitment_bytes_len = 48;
|
||||
let commitment_hash_bytes_len = 48;
|
||||
|
||||
// safety: we made bound check and we're using constant offest
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let cm_bytes = bytes[..j + commitment_bytes_len].try_into().unwrap();
|
||||
let commitment = try_deserialize_g1_projective(
|
||||
&cm_bytes,
|
||||
@@ -63,6 +65,8 @@ impl TryFrom<&[u8]> for BlindSignRequest {
|
||||
)?;
|
||||
j += commitment_bytes_len;
|
||||
|
||||
// safety: we made bound check and we're using constant offest
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let cm_hash_bytes = bytes[j..j + commitment_hash_bytes_len].try_into().unwrap();
|
||||
let commitment_hash = try_deserialize_g1_projective(
|
||||
&cm_hash_bytes,
|
||||
@@ -72,6 +76,8 @@ impl TryFrom<&[u8]> for BlindSignRequest {
|
||||
)?;
|
||||
j += commitment_hash_bytes_len;
|
||||
|
||||
// safety: we made bound check and we're using constant offest
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let c_len = u64::from_le_bytes(bytes[j..j + 8].try_into().unwrap());
|
||||
j += 8;
|
||||
if bytes[j..].len() < c_len as usize * 48 {
|
||||
@@ -86,6 +92,14 @@ impl TryFrom<&[u8]> for BlindSignRequest {
|
||||
let start = j + i * 48;
|
||||
let end = start + 48;
|
||||
|
||||
if bytes.len() < end {
|
||||
return Err(CoconutError::Deserialization(
|
||||
"Failed to deserialize compressed commitment".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
// safety: we made bound check and we're using constant offest
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let private_attributes_commitment_bytes = bytes[start..end].try_into().unwrap();
|
||||
let private_attributes_commitment = try_deserialize_g1_projective(
|
||||
&private_attributes_commitment_bytes,
|
||||
@@ -137,7 +151,7 @@ impl Bytable for BlindSignRequest {
|
||||
impl Base58 for BlindSignRequest {}
|
||||
|
||||
impl BlindSignRequest {
|
||||
fn verify_proof(&self, params: &Parameters, public_attributes: &[Attribute]) -> bool {
|
||||
fn verify_proof(&self, params: &Parameters, public_attributes: &[&Attribute]) -> bool {
|
||||
self.pi_s.verify(
|
||||
params,
|
||||
&self.commitment,
|
||||
@@ -150,8 +164,8 @@ impl BlindSignRequest {
|
||||
self.commitment_hash
|
||||
}
|
||||
|
||||
pub fn get_private_attributes_pedersen_commitments(&self) -> Vec<G1Projective> {
|
||||
self.private_attributes_commitments.clone()
|
||||
pub fn get_private_attributes_pedersen_commitments(&self) -> &[G1Projective] {
|
||||
&self.private_attributes_commitments
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
@@ -161,12 +175,16 @@ impl BlindSignRequest {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<BlindSignRequest> {
|
||||
BlindSignRequest::try_from(bytes)
|
||||
}
|
||||
|
||||
pub fn num_private_attributes(&self) -> usize {
|
||||
self.private_attributes_commitments.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_attributes_commitment(
|
||||
params: &Parameters,
|
||||
private_attributes: &[Attribute],
|
||||
public_attributes: &[Attribute],
|
||||
private_attributes: &[&Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
hs: &[G1Affine],
|
||||
) -> (Scalar, G1Projective) {
|
||||
let commitment_opening = params.random_scalar();
|
||||
@@ -187,7 +205,7 @@ pub fn compute_attributes_commitment(
|
||||
|
||||
pub fn compute_pedersen_commitments_for_private_attributes(
|
||||
params: &Parameters,
|
||||
private_attributes: &[Attribute],
|
||||
private_attributes: &[&Attribute],
|
||||
h: &G1Projective,
|
||||
) -> (Vec<Scalar>, Vec<G1Projective>) {
|
||||
// Generate openings for Pedersen commitment for each private attribute
|
||||
@@ -197,13 +215,13 @@ pub fn compute_pedersen_commitments_for_private_attributes(
|
||||
let pedersen_commitments = commitments_openings
|
||||
.iter()
|
||||
.zip(private_attributes.iter())
|
||||
.map(|(o_j, m_j)| params.gen1() * o_j + h * m_j)
|
||||
.map(|(o_j, &m_j)| params.gen1() * o_j + h * m_j)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
(commitments_openings, pedersen_commitments)
|
||||
}
|
||||
|
||||
pub fn compute_hash(commitment: G1Projective, public_attributes: &[Attribute]) -> G1Projective {
|
||||
pub fn compute_hash(commitment: G1Projective, public_attributes: &[&Attribute]) -> G1Projective {
|
||||
let mut buff = Vec::new();
|
||||
buff.extend_from_slice(commitment.to_bytes().as_ref());
|
||||
for attr in public_attributes {
|
||||
@@ -215,8 +233,8 @@ pub fn compute_hash(commitment: G1Projective, public_attributes: &[Attribute]) -
|
||||
/// Builds cryptographic material required for blind sign.
|
||||
pub fn prepare_blind_sign(
|
||||
params: &Parameters,
|
||||
private_attributes: &[Attribute],
|
||||
public_attributes: &[Attribute],
|
||||
private_attributes: &[&Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
) -> Result<(Vec<Scalar>, BlindSignRequest)> {
|
||||
if private_attributes.is_empty() {
|
||||
return Err(CoconutError::Issuance(
|
||||
@@ -271,7 +289,7 @@ pub fn blind_sign(
|
||||
params: &Parameters,
|
||||
signing_secret_key: &SecretKey,
|
||||
blind_sign_request: &BlindSignRequest,
|
||||
public_attributes: &[Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
) -> Result<BlindedSignature> {
|
||||
let num_private = blind_sign_request.private_attributes_commitments.len();
|
||||
let hs = params.gen_hs();
|
||||
@@ -304,7 +322,7 @@ pub fn blind_sign(
|
||||
let signed_public = h * public_attributes
|
||||
.iter()
|
||||
.zip(signing_secret_key.ys.iter().skip(num_private))
|
||||
.map(|(attr, yi)| attr * yi)
|
||||
.map(|(&attr, yi)| attr * yi)
|
||||
.sum::<Scalar>();
|
||||
|
||||
// h ^ x + c[0] ^ y[0] + ... c[m] ^ y[m] + h ^ (pub_m[0] * y[m + 1] + ... + pub_m[n] * y[m + n])
|
||||
@@ -345,7 +363,7 @@ pub fn blind_sign(
|
||||
pub fn verify_partial_blind_signature(
|
||||
params: &Parameters,
|
||||
blind_sign_request: &BlindSignRequest,
|
||||
public_attributes: &[Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
blind_sig: &BlindedSignature,
|
||||
partial_verification_key: &VerificationKey,
|
||||
) -> bool {
|
||||
@@ -383,7 +401,7 @@ pub fn verify_partial_blind_signature(
|
||||
}
|
||||
|
||||
// for each public attribute, add (s^pub_j, beta_{priv + j}) to the miller terms
|
||||
for (pub_attr, beta_g2) in public_attributes.iter().zip(
|
||||
for (&pub_attr, beta_g2) in public_attributes.iter().zip(
|
||||
partial_verification_key
|
||||
.beta_g2
|
||||
.iter()
|
||||
@@ -415,7 +433,7 @@ pub fn verify_partial_blind_signature(
|
||||
pub fn sign(
|
||||
params: &mut Parameters,
|
||||
secret_key: &SecretKey,
|
||||
public_attributes: &[Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
) -> Result<Signature> {
|
||||
if public_attributes.len() > secret_key.ys.len() {
|
||||
return Err(CoconutError::IssuanceMaxAttributes {
|
||||
@@ -429,7 +447,7 @@ pub fn sign(
|
||||
// (the python implementation hashes string representation of all attributes onto the curve,
|
||||
// but I think the same can be achieved by just summing the attributes thus avoiding the unnecessary
|
||||
// transformation. If I'm wrong, please correct me.)
|
||||
let attributes_sum = public_attributes.iter().sum::<Scalar>();
|
||||
let attributes_sum = public_attributes.iter().copied().sum::<Scalar>();
|
||||
let h = hash_g1((params.gen1() * attributes_sum).to_bytes());
|
||||
|
||||
// x + m0 * y0 + m1 * y1 + ... mn * yn
|
||||
@@ -437,7 +455,7 @@ pub fn sign(
|
||||
+ public_attributes
|
||||
.iter()
|
||||
.zip(secret_key.ys.iter())
|
||||
.map(|(m_i, y_i)| m_i * y_i)
|
||||
.map(|(&m_i, y_i)| m_i * y_i)
|
||||
.sum::<Scalar>();
|
||||
|
||||
let sig2 = h * exponent;
|
||||
@@ -448,13 +466,14 @@ pub fn sign(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::scheme::keygen::keygen;
|
||||
use crate::tests::helpers::random_scalars_refs;
|
||||
|
||||
#[test]
|
||||
fn blind_sign_request_bytes_roundtrip() {
|
||||
// 0 public and 1 private attribute
|
||||
let params = Parameters::new(1).unwrap();
|
||||
let private_attributes = params.n_random_scalars(1);
|
||||
let public_attributes = params.n_random_scalars(0);
|
||||
random_scalars_refs!(private_attributes, params, 1);
|
||||
random_scalars_refs!(public_attributes, params, 0);
|
||||
|
||||
let (_commitments_openings, lambda) =
|
||||
prepare_blind_sign(¶ms, &private_attributes, &public_attributes).unwrap();
|
||||
@@ -467,8 +486,8 @@ mod tests {
|
||||
|
||||
// 2 public and 2 private attributes
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let private_attributes = params.n_random_scalars(2);
|
||||
let public_attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(private_attributes, params, 2);
|
||||
random_scalars_refs!(public_attributes, params, 2);
|
||||
|
||||
let (_commitments_openings, lambda) =
|
||||
prepare_blind_sign(¶ms, &private_attributes, &public_attributes).unwrap();
|
||||
@@ -483,8 +502,8 @@ mod tests {
|
||||
#[test]
|
||||
fn successful_verify_partial_blind_signature() {
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let private_attributes = params.n_random_scalars(2);
|
||||
let public_attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(private_attributes, params, 2);
|
||||
random_scalars_refs!(public_attributes, params, 2);
|
||||
|
||||
let (_commitments_openings, request) =
|
||||
prepare_blind_sign(¶ms, &private_attributes, &public_attributes).unwrap();
|
||||
@@ -492,7 +511,7 @@ mod tests {
|
||||
let validator_keypair = keygen(¶ms);
|
||||
let blind_sig = blind_sign(
|
||||
¶ms,
|
||||
&validator_keypair.secret_key(),
|
||||
validator_keypair.secret_key(),
|
||||
&request,
|
||||
&public_attributes,
|
||||
)
|
||||
@@ -503,36 +522,35 @@ mod tests {
|
||||
&request,
|
||||
&public_attributes,
|
||||
&blind_sig,
|
||||
&validator_keypair.verification_key()
|
||||
validator_keypair.verification_key()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn successful_verify_partial_blind_signature_no_public_attributes() {
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let private_attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(private_attributes, params, 2);
|
||||
|
||||
let (_commitments_openings, request) =
|
||||
prepare_blind_sign(¶ms, &private_attributes, &[]).unwrap();
|
||||
|
||||
let validator_keypair = keygen(¶ms);
|
||||
let blind_sig =
|
||||
blind_sign(¶ms, &validator_keypair.secret_key(), &request, &[]).unwrap();
|
||||
let blind_sig = blind_sign(¶ms, validator_keypair.secret_key(), &request, &[]).unwrap();
|
||||
|
||||
assert!(verify_partial_blind_signature(
|
||||
¶ms,
|
||||
&request,
|
||||
&[],
|
||||
&blind_sig,
|
||||
&validator_keypair.verification_key()
|
||||
validator_keypair.verification_key()
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fail_verify_partial_blind_signature_with_wrong_key() {
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let private_attributes = params.n_random_scalars(2);
|
||||
let public_attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(private_attributes, params, 2);
|
||||
random_scalars_refs!(public_attributes, params, 2);
|
||||
|
||||
let (_commitments_openings, request) =
|
||||
prepare_blind_sign(¶ms, &private_attributes, &public_attributes).unwrap();
|
||||
@@ -541,7 +559,7 @@ mod tests {
|
||||
let validator2_keypair = keygen(¶ms);
|
||||
let blind_sig = blind_sign(
|
||||
¶ms,
|
||||
&validator_keypair.secret_key(),
|
||||
validator_keypair.secret_key(),
|
||||
&request,
|
||||
&public_attributes,
|
||||
)
|
||||
@@ -553,7 +571,7 @@ mod tests {
|
||||
&request,
|
||||
&public_attributes,
|
||||
&blind_sig,
|
||||
&validator2_keypair.verification_key()
|
||||
validator2_keypair.verification_key()
|
||||
),);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,12 @@ use crate::utils::{
|
||||
};
|
||||
use crate::Base58;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Clone))]
|
||||
#[cfg_attr(
|
||||
feature = "key-zeroize",
|
||||
derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)
|
||||
)]
|
||||
pub struct SecretKey {
|
||||
pub(crate) x: Scalar,
|
||||
pub(crate) ys: Vec<Scalar>,
|
||||
@@ -62,7 +66,9 @@ impl TryFrom<&[u8]> for SecretKey {
|
||||
}
|
||||
|
||||
// this conversion will not fail as we are taking the same length of data
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let x_bytes: [u8; 32] = bytes[..32].try_into().unwrap();
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let ys_len = u64::from_le_bytes(bytes[32..40].try_into().unwrap());
|
||||
let actual_ys_len = (bytes.len() - 40) / 32;
|
||||
|
||||
@@ -97,6 +103,10 @@ impl SecretKey {
|
||||
(self.x, self.ys.clone())
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.ys.len()
|
||||
}
|
||||
|
||||
/// Derive verification key using this secret key.
|
||||
pub fn verification_key(&self, params: &Parameters) -> VerificationKey {
|
||||
let g1 = params.gen1();
|
||||
@@ -141,6 +151,10 @@ impl Base58 for SecretKey {}
|
||||
// TODO: perhaps change points to affine representation
|
||||
// to make verification slightly more efficient?
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(
|
||||
feature = "key-zeroize",
|
||||
derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)
|
||||
)]
|
||||
pub struct VerificationKey {
|
||||
// TODO add gen2 as per the paper or imply it from the fact library is using bls381?
|
||||
pub(crate) alpha: G2Projective,
|
||||
@@ -180,7 +194,9 @@ impl TryFrom<&[u8]> for VerificationKey {
|
||||
}
|
||||
|
||||
// this conversion will not fail as we are taking the same length of data
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let alpha_bytes: [u8; 96] = bytes[..96].try_into().unwrap();
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let betas_len = u64::from_le_bytes(bytes[96..104].try_into().unwrap());
|
||||
|
||||
let actual_betas_len = (bytes.len() - 104) / (96 + 48);
|
||||
@@ -204,6 +220,8 @@ impl TryFrom<&[u8]> for VerificationKey {
|
||||
for i in 0..betas_len {
|
||||
let start = (104 + i * 48) as usize;
|
||||
let end = start + 48;
|
||||
// we're using a constant 48 byte offset (which is the size of G1 compressed) so unwrap is fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let beta_i_bytes = bytes[start..end].try_into().unwrap();
|
||||
let beta_i = try_deserialize_g1_projective(
|
||||
&beta_i_bytes,
|
||||
@@ -220,6 +238,8 @@ impl TryFrom<&[u8]> for VerificationKey {
|
||||
for i in 0..betas_len {
|
||||
let start = (beta_g1_end + i * 96) as usize;
|
||||
let end = start + 96;
|
||||
// we're using a constant 96 byte offset (which is the size of G2 compressed) so unwrap is fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let beta_i_bytes = bytes[start..end].try_into().unwrap();
|
||||
let beta_i = try_deserialize_g2_projective(
|
||||
&beta_i_bytes,
|
||||
@@ -392,7 +412,11 @@ impl Bytable for VerificationKey {
|
||||
impl Base58 for VerificationKey {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq, Clone))]
|
||||
#[cfg_attr(
|
||||
feature = "key-zeroize",
|
||||
derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop)
|
||||
)]
|
||||
pub struct KeyPair {
|
||||
secret_key: SecretKey,
|
||||
verification_key: VerificationKey,
|
||||
@@ -429,12 +453,12 @@ impl KeyPair {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn secret_key(&self) -> SecretKey {
|
||||
self.secret_key.clone()
|
||||
pub fn secret_key(&self) -> &SecretKey {
|
||||
&self.secret_key
|
||||
}
|
||||
|
||||
pub fn verification_key(&self) -> VerificationKey {
|
||||
self.verification_key.clone()
|
||||
pub fn verification_key(&self) -> &VerificationKey {
|
||||
&self.verification_key
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
@@ -488,6 +512,8 @@ impl TryFrom<&[u8]> for KeyPair {
|
||||
});
|
||||
}
|
||||
|
||||
// safety: we made bound check and we're using constant offest
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let secret_key_len =
|
||||
u64::from_le_bytes(bytes[header_len..header_len + 8].try_into().unwrap()) as usize;
|
||||
let secret_key_start = header_len + 8;
|
||||
@@ -503,6 +529,8 @@ impl TryFrom<&[u8]> for KeyPair {
|
||||
});
|
||||
}
|
||||
|
||||
// safety: we made bound check
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let verification_key_len = u64::from_le_bytes(
|
||||
bytes[secret_key_start + secret_key_len..secret_key_start + secret_key_len + 8]
|
||||
.try_into()
|
||||
@@ -515,6 +543,7 @@ impl TryFrom<&[u8]> for KeyPair {
|
||||
)?;
|
||||
let consumed_bytes = verification_key_start + verification_key_len;
|
||||
let index = if consumed_bytes < bytes.len() && [consumed_bytes..].len() == 8 {
|
||||
#[allow(clippy::unwrap_used)]
|
||||
Some(u64::from_le_bytes(
|
||||
bytes[consumed_bytes..consumed_bytes + 8]
|
||||
.try_into()
|
||||
|
||||
@@ -44,7 +44,10 @@ impl TryFrom<&[u8]> for Signature {
|
||||
)));
|
||||
}
|
||||
|
||||
// safety: we just checked for the length so the unwraps are fine
|
||||
#[allow(clippy::expect_used)]
|
||||
let sig1_bytes: &[u8; 48] = &bytes[..48].try_into().expect("Slice size != 48");
|
||||
#[allow(clippy::expect_used)]
|
||||
let sig2_bytes: &[u8; 48] = &bytes[48..].try_into().expect("Slice size != 48");
|
||||
|
||||
let sig1 = try_deserialize_g1_projective(
|
||||
@@ -88,6 +91,45 @@ impl Signature {
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Signature> {
|
||||
Signature::try_from(bytes)
|
||||
}
|
||||
|
||||
pub fn verify(
|
||||
&self,
|
||||
params: &Parameters,
|
||||
partial_verification_key: &VerificationKey,
|
||||
private_attributes: &[&Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
commitment_hash: &G1Projective,
|
||||
) -> Result<()> {
|
||||
// Verify the commitment hash
|
||||
if !(commitment_hash == &self.0) {
|
||||
return Err(CoconutError::Verification(
|
||||
"Verification of commitment hash from signature failed".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let alpha = partial_verification_key.alpha;
|
||||
|
||||
let signed_attributes = private_attributes
|
||||
.iter()
|
||||
.chain(public_attributes.iter())
|
||||
.zip(partial_verification_key.beta_g2.iter())
|
||||
.map(|(&attr, beta_i)| beta_i * attr)
|
||||
.sum::<G2Projective>();
|
||||
|
||||
// Verify the signature share
|
||||
if !check_bilinear_pairing(
|
||||
&self.0.to_affine(),
|
||||
&G2Prepared::from((alpha + signed_attributes).to_affine()),
|
||||
&self.1.to_affine(),
|
||||
params.prepared_miller_g2(),
|
||||
) {
|
||||
return Err(CoconutError::Unblind(
|
||||
"Verification of signature share failed".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Bytable for Signature {
|
||||
@@ -102,8 +144,7 @@ impl Bytable for Signature {
|
||||
|
||||
impl Base58 for Signature {}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct BlindedSignature(G1Projective, G1Projective);
|
||||
|
||||
impl Bytable for BlindedSignature {
|
||||
@@ -129,7 +170,10 @@ impl TryFrom<&[u8]> for BlindedSignature {
|
||||
)));
|
||||
}
|
||||
|
||||
// safety: we just checked for the length so the unwraps are fine
|
||||
#[allow(clippy::expect_used)]
|
||||
let h_bytes: &[u8; 48] = &bytes[..48].try_into().expect("Slice size != 48");
|
||||
#[allow(clippy::expect_used)]
|
||||
let sig_bytes: &[u8; 48] = &bytes[48..].try_into().expect("Slice size != 48");
|
||||
|
||||
let h = try_deserialize_g1_projective(
|
||||
@@ -148,24 +192,12 @@ impl TryFrom<&[u8]> for BlindedSignature {
|
||||
impl BlindedSignature {
|
||||
pub fn unblind(
|
||||
&self,
|
||||
params: &Parameters,
|
||||
partial_verification_key: &VerificationKey,
|
||||
private_attributes: &[Attribute],
|
||||
public_attributes: &[Attribute],
|
||||
commitment_hash: &G1Projective,
|
||||
pedersen_commitments_openings: &[Scalar],
|
||||
) -> Result<Signature> {
|
||||
// parse the signature
|
||||
let h = &self.0;
|
||||
let c = &self.1;
|
||||
|
||||
// Verify the commitment hash
|
||||
if !(commitment_hash == h) {
|
||||
return Err(CoconutError::Unblind(
|
||||
"Verification of commitment hash from signature failed".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let blinding_removers = partial_verification_key
|
||||
.beta_g1
|
||||
.iter()
|
||||
@@ -175,30 +207,29 @@ impl BlindedSignature {
|
||||
|
||||
let unblinded_c = c - blinding_removers;
|
||||
|
||||
let alpha = partial_verification_key.alpha;
|
||||
|
||||
let signed_attributes = private_attributes
|
||||
.iter()
|
||||
.chain(public_attributes.iter())
|
||||
.zip(partial_verification_key.beta_g2.iter())
|
||||
.map(|(attr, beta_i)| beta_i * attr)
|
||||
.sum::<G2Projective>();
|
||||
|
||||
// Verify the signature share
|
||||
if !check_bilinear_pairing(
|
||||
&h.to_affine(),
|
||||
&G2Prepared::from((alpha + signed_attributes).to_affine()),
|
||||
&unblinded_c.to_affine(),
|
||||
params.prepared_miller_g2(),
|
||||
) {
|
||||
return Err(CoconutError::Unblind(
|
||||
"Verification of signature share failed".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Signature(*h, unblinded_c))
|
||||
}
|
||||
|
||||
pub fn unblind_and_verify(
|
||||
&self,
|
||||
params: &Parameters,
|
||||
partial_verification_key: &VerificationKey,
|
||||
private_attributes: &[&Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
commitment_hash: &G1Projective,
|
||||
pedersen_commitments_openings: &[Scalar],
|
||||
) -> Result<Signature> {
|
||||
let unblinded = self.unblind(partial_verification_key, pedersen_commitments_openings)?;
|
||||
unblinded.verify(
|
||||
params,
|
||||
partial_verification_key,
|
||||
private_attributes,
|
||||
public_attributes,
|
||||
commitment_hash,
|
||||
)?;
|
||||
Ok(unblinded)
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 96] {
|
||||
let mut bytes = [0u8; 96];
|
||||
bytes[..48].copy_from_slice(&self.0.to_affine().to_compressed());
|
||||
@@ -237,25 +268,25 @@ impl SignatureShare {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::hash_to_scalar;
|
||||
use crate::scheme::aggregation::{aggregate_signatures, aggregate_verification_keys};
|
||||
use crate::scheme::issuance::{blind_sign, compute_hash, prepare_blind_sign, sign};
|
||||
use crate::scheme::keygen::{keygen, ttp_keygen};
|
||||
use crate::scheme::verification::{prove_bandwidth_credential, verify, verify_credential};
|
||||
|
||||
use super::*;
|
||||
use crate::tests::helpers::random_scalars_refs;
|
||||
|
||||
#[test]
|
||||
fn unblind_returns_error_if_integrity_check_on_commitment_hash_fails() {
|
||||
let params = Parameters::new(2).unwrap();
|
||||
let private_attributes = params.n_random_scalars(2_usize);
|
||||
random_scalars_refs!(private_attributes, params, 2);
|
||||
|
||||
let (_commitments_openings, lambda) =
|
||||
prepare_blind_sign(¶ms, &private_attributes, &[]).unwrap();
|
||||
|
||||
let keypair1 = keygen(¶ms);
|
||||
|
||||
let sig1 = blind_sign(¶ms, &keypair1.secret_key(), &lambda, &[]).unwrap();
|
||||
let sig1 = blind_sign(¶ms, keypair1.secret_key(), &lambda, &[]).unwrap();
|
||||
|
||||
let wrong_commitment_opening = params.random_scalar();
|
||||
let wrong_commitment = params.gen1() * wrong_commitment_opening;
|
||||
@@ -263,9 +294,9 @@ mod tests {
|
||||
let wrong_commitments_openings = params.n_random_scalars(private_attributes.len());
|
||||
|
||||
assert!(sig1
|
||||
.unblind(
|
||||
.unblind_and_verify(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&private_attributes,
|
||||
&[],
|
||||
&fake_commitment_hash,
|
||||
@@ -277,20 +308,23 @@ mod tests {
|
||||
#[test]
|
||||
fn unblind_returns_error_if_signature_verification_fails() {
|
||||
let params = Parameters::new(2).unwrap();
|
||||
let private_attributes = vec![hash_to_scalar("Attribute1"), hash_to_scalar("Attribute2")];
|
||||
let private_attributes2 = vec![hash_to_scalar("Attribute3"), hash_to_scalar("Attribute4")];
|
||||
let p = [hash_to_scalar("Attribute1"), hash_to_scalar("Attribute2")];
|
||||
let private_attributes = vec![&p[0], &p[1]];
|
||||
|
||||
let p2 = [hash_to_scalar("Attribute3"), hash_to_scalar("Attribute4")];
|
||||
let private_attributes2 = vec![&p2[0], &p2[1]];
|
||||
|
||||
let (commitments_openings, lambda) =
|
||||
prepare_blind_sign(¶ms, &private_attributes, &[]).unwrap();
|
||||
|
||||
let keypair1 = keygen(¶ms);
|
||||
|
||||
let sig1 = blind_sign(¶ms, &keypair1.secret_key(), &lambda, &[]).unwrap();
|
||||
let sig1 = blind_sign(¶ms, keypair1.secret_key(), &lambda, &[]).unwrap();
|
||||
|
||||
assert!(sig1
|
||||
.unblind(
|
||||
.unblind_and_verify(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&private_attributes2,
|
||||
&[],
|
||||
&lambda.get_commitment_hash(),
|
||||
@@ -304,7 +338,7 @@ mod tests {
|
||||
let params = Parameters::new(2).unwrap();
|
||||
let serial_number = params.random_scalar();
|
||||
let binding_number = params.random_scalar();
|
||||
let private_attributes = vec![serial_number, binding_number];
|
||||
let private_attributes = vec![&serial_number, &binding_number];
|
||||
|
||||
let keypair1 = keygen(¶ms);
|
||||
let keypair2 = keygen(¶ms);
|
||||
@@ -312,11 +346,11 @@ mod tests {
|
||||
let (commitments_openings, lambda) =
|
||||
prepare_blind_sign(¶ms, &private_attributes, &[]).unwrap();
|
||||
|
||||
let sig1 = blind_sign(¶ms, &keypair1.secret_key(), &lambda, &[])
|
||||
let sig1 = blind_sign(¶ms, keypair1.secret_key(), &lambda, &[])
|
||||
.unwrap()
|
||||
.unblind(
|
||||
.unblind_and_verify(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&private_attributes,
|
||||
&[],
|
||||
&lambda.get_commitment_hash(),
|
||||
@@ -324,11 +358,11 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let sig2 = blind_sign(¶ms, &keypair2.secret_key(), &lambda, &[])
|
||||
let sig2 = blind_sign(¶ms, keypair2.secret_key(), &lambda, &[])
|
||||
.unwrap()
|
||||
.unblind(
|
||||
.unblind_and_verify(
|
||||
¶ms,
|
||||
&keypair2.verification_key(),
|
||||
keypair2.verification_key(),
|
||||
&private_attributes,
|
||||
&[],
|
||||
&lambda.get_commitment_hash(),
|
||||
@@ -338,39 +372,39 @@ mod tests {
|
||||
|
||||
let theta1 = prove_bandwidth_credential(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&sig1,
|
||||
serial_number,
|
||||
binding_number,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let theta2 = prove_bandwidth_credential(
|
||||
¶ms,
|
||||
&keypair2.verification_key(),
|
||||
keypair2.verification_key(),
|
||||
&sig2,
|
||||
serial_number,
|
||||
binding_number,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(verify_credential(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&theta1,
|
||||
&[],
|
||||
));
|
||||
|
||||
assert!(verify_credential(
|
||||
¶ms,
|
||||
&keypair2.verification_key(),
|
||||
keypair2.verification_key(),
|
||||
&theta2,
|
||||
&[],
|
||||
));
|
||||
|
||||
assert!(!verify_credential(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&theta2,
|
||||
&[],
|
||||
));
|
||||
@@ -379,30 +413,30 @@ mod tests {
|
||||
#[test]
|
||||
fn verification_on_two_public_attributes() {
|
||||
let mut params = Parameters::new(2).unwrap();
|
||||
let attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(attributes, params, 2);
|
||||
|
||||
let keypair1 = keygen(¶ms);
|
||||
let keypair2 = keygen(¶ms);
|
||||
let sig1 = sign(&mut params, &keypair1.secret_key(), &attributes).unwrap();
|
||||
let sig2 = sign(&mut params, &keypair2.secret_key(), &attributes).unwrap();
|
||||
let sig1 = sign(&mut params, keypair1.secret_key(), &attributes).unwrap();
|
||||
let sig2 = sign(&mut params, keypair2.secret_key(), &attributes).unwrap();
|
||||
|
||||
assert!(verify(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&attributes,
|
||||
&sig1,
|
||||
));
|
||||
|
||||
assert!(!verify(
|
||||
¶ms,
|
||||
&keypair2.verification_key(),
|
||||
keypair2.verification_key(),
|
||||
&attributes,
|
||||
&sig1,
|
||||
));
|
||||
|
||||
assert!(!verify(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&attributes,
|
||||
&sig2,
|
||||
));
|
||||
@@ -411,10 +445,11 @@ mod tests {
|
||||
#[test]
|
||||
fn verification_on_two_public_and_two_private_attributes() {
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let public_attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(public_attributes, params, 2);
|
||||
|
||||
let serial_number = params.random_scalar();
|
||||
let binding_number = params.random_scalar();
|
||||
let private_attributes = vec![serial_number, binding_number];
|
||||
let private_attributes = vec![&serial_number, &binding_number];
|
||||
|
||||
let keypair1 = keygen(¶ms);
|
||||
let keypair2 = keygen(¶ms);
|
||||
@@ -422,11 +457,11 @@ mod tests {
|
||||
let (commitments_openings, lambda) =
|
||||
prepare_blind_sign(¶ms, &private_attributes, &public_attributes).unwrap();
|
||||
|
||||
let sig1 = blind_sign(¶ms, &keypair1.secret_key(), &lambda, &public_attributes)
|
||||
let sig1 = blind_sign(¶ms, keypair1.secret_key(), &lambda, &public_attributes)
|
||||
.unwrap()
|
||||
.unblind(
|
||||
.unblind_and_verify(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&private_attributes,
|
||||
&public_attributes,
|
||||
&lambda.get_commitment_hash(),
|
||||
@@ -434,11 +469,11 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let sig2 = blind_sign(¶ms, &keypair2.secret_key(), &lambda, &public_attributes)
|
||||
let sig2 = blind_sign(¶ms, keypair2.secret_key(), &lambda, &public_attributes)
|
||||
.unwrap()
|
||||
.unblind(
|
||||
.unblind_and_verify(
|
||||
¶ms,
|
||||
&keypair2.verification_key(),
|
||||
keypair2.verification_key(),
|
||||
&private_attributes,
|
||||
&public_attributes,
|
||||
&lambda.get_commitment_hash(),
|
||||
@@ -448,39 +483,39 @@ mod tests {
|
||||
|
||||
let theta1 = prove_bandwidth_credential(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&sig1,
|
||||
serial_number,
|
||||
binding_number,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let theta2 = prove_bandwidth_credential(
|
||||
¶ms,
|
||||
&keypair2.verification_key(),
|
||||
keypair2.verification_key(),
|
||||
&sig2,
|
||||
serial_number,
|
||||
binding_number,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(verify_credential(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&theta1,
|
||||
&public_attributes,
|
||||
));
|
||||
|
||||
assert!(verify_credential(
|
||||
¶ms,
|
||||
&keypair2.verification_key(),
|
||||
keypair2.verification_key(),
|
||||
&theta2,
|
||||
&public_attributes,
|
||||
));
|
||||
|
||||
assert!(!verify_credential(
|
||||
¶ms,
|
||||
&keypair1.verification_key(),
|
||||
keypair1.verification_key(),
|
||||
&theta2,
|
||||
&public_attributes,
|
||||
));
|
||||
@@ -489,10 +524,11 @@ mod tests {
|
||||
#[test]
|
||||
fn verification_on_two_public_and_two_private_attributes_from_two_signers() {
|
||||
let params = Parameters::new(4).unwrap();
|
||||
let public_attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(public_attributes, params, 2);
|
||||
|
||||
let serial_number = params.random_scalar();
|
||||
let binding_number = params.random_scalar();
|
||||
let private_attributes = vec![serial_number, binding_number];
|
||||
let private_attributes = vec![&serial_number, &binding_number];
|
||||
|
||||
let keypairs = ttp_keygen(¶ms, 2, 3).unwrap();
|
||||
|
||||
@@ -502,11 +538,11 @@ mod tests {
|
||||
let sigs = keypairs
|
||||
.iter()
|
||||
.map(|keypair| {
|
||||
blind_sign(¶ms, &keypair.secret_key(), &lambda, &public_attributes)
|
||||
blind_sign(¶ms, keypair.secret_key(), &lambda, &public_attributes)
|
||||
.unwrap()
|
||||
.unblind(
|
||||
.unblind_and_verify(
|
||||
¶ms,
|
||||
&keypair.verification_key(),
|
||||
keypair.verification_key(),
|
||||
&private_attributes,
|
||||
&public_attributes,
|
||||
&lambda.get_commitment_hash(),
|
||||
@@ -518,7 +554,7 @@ mod tests {
|
||||
|
||||
let vks = keypairs
|
||||
.into_iter()
|
||||
.map(|keypair| keypair.verification_key())
|
||||
.map(|keypair| keypair.verification_key().clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut attributes = Vec::with_capacity(private_attributes.len() + public_attributes.len());
|
||||
@@ -530,9 +566,14 @@ mod tests {
|
||||
aggregate_signatures(¶ms, &aggr_vk, &attributes, &sigs[..2], Some(&[1, 2]))
|
||||
.unwrap();
|
||||
|
||||
let theta =
|
||||
prove_bandwidth_credential(¶ms, &aggr_vk, &aggr_sig, serial_number, binding_number)
|
||||
.unwrap();
|
||||
let theta = prove_bandwidth_credential(
|
||||
¶ms,
|
||||
&aggr_vk,
|
||||
&aggr_sig,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(verify_credential(
|
||||
¶ms,
|
||||
@@ -547,9 +588,14 @@ mod tests {
|
||||
aggregate_signatures(¶ms, &aggr_vk, &attributes, &sigs[1..], Some(&[2, 3]))
|
||||
.unwrap();
|
||||
|
||||
let theta =
|
||||
prove_bandwidth_credential(¶ms, &aggr_vk, &aggr_sig, serial_number, binding_number)
|
||||
.unwrap();
|
||||
let theta = prove_bandwidth_credential(
|
||||
¶ms,
|
||||
&aggr_vk,
|
||||
&aggr_sig,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(verify_credential(
|
||||
¶ms,
|
||||
|
||||
@@ -43,6 +43,8 @@ impl TryFrom<&[u8]> for Theta {
|
||||
));
|
||||
}
|
||||
|
||||
// safety: we just checked for the length so the unwraps are fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let blinded_message_bytes = bytes[..96].try_into().unwrap();
|
||||
let blinded_message = try_deserialize_g2_projective(
|
||||
&blinded_message_bytes,
|
||||
@@ -51,6 +53,8 @@ impl TryFrom<&[u8]> for Theta {
|
||||
),
|
||||
)?;
|
||||
|
||||
// safety: we just checked for the length so the unwraps are fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let blinded_serial_number_bytes = bytes[96..192].try_into().unwrap();
|
||||
let blinded_serial_number = try_deserialize_g2_projective(
|
||||
&blinded_serial_number_bytes,
|
||||
@@ -130,7 +134,7 @@ impl Base58 for Theta {}
|
||||
pub fn compute_kappa(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKey,
|
||||
private_attributes: &[Attribute],
|
||||
private_attributes: &[&Attribute],
|
||||
blinding_factor: Scalar,
|
||||
) -> G2Projective {
|
||||
params.gen2() * blinding_factor
|
||||
@@ -138,11 +142,11 @@ pub fn compute_kappa(
|
||||
+ private_attributes
|
||||
.iter()
|
||||
.zip(verification_key.beta_g2.iter())
|
||||
.map(|(priv_attr, beta_i)| beta_i * priv_attr)
|
||||
.map(|(&priv_attr, beta_i)| beta_i * priv_attr)
|
||||
.sum::<G2Projective>()
|
||||
}
|
||||
|
||||
pub fn compute_zeta(params: &Parameters, serial_number: Attribute) -> G2Projective {
|
||||
pub fn compute_zeta(params: &Parameters, serial_number: &Attribute) -> G2Projective {
|
||||
params.gen2() * serial_number
|
||||
}
|
||||
|
||||
@@ -150,8 +154,8 @@ pub fn prove_bandwidth_credential(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKey,
|
||||
signature: &Signature,
|
||||
serial_number: Attribute,
|
||||
binding_number: Attribute,
|
||||
serial_number: &Attribute,
|
||||
binding_number: &Attribute,
|
||||
) -> Result<Theta> {
|
||||
if verification_key.beta_g2.len() < 2 {
|
||||
return Err(
|
||||
@@ -171,7 +175,7 @@ pub fn prove_bandwidth_credential(
|
||||
// Thus, we need kappa which allows us to verify sigma'. In particular,
|
||||
// kappa is computed on m as input, but thanks to the use or random value r,
|
||||
// it does not reveal any information about m.
|
||||
let private_attributes = vec![serial_number, binding_number];
|
||||
let private_attributes = [serial_number, binding_number];
|
||||
let blinded_message = compute_kappa(
|
||||
params,
|
||||
verification_key,
|
||||
@@ -185,8 +189,8 @@ pub fn prove_bandwidth_credential(
|
||||
let pi_v = ProofKappaZeta::construct(
|
||||
params,
|
||||
verification_key,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
serial_number,
|
||||
binding_number,
|
||||
&sign_blinding_factor,
|
||||
&blinded_message,
|
||||
&blinded_serial_number,
|
||||
@@ -221,7 +225,10 @@ pub fn check_vk_pairing(
|
||||
if values_len == 0 || values_len - 1 != vk.beta_g1.len() || values_len - 1 != vk.beta_g2.len() {
|
||||
return false;
|
||||
}
|
||||
if vk.alpha != *dkg_values.last().unwrap() {
|
||||
|
||||
// safety: we made an explicit check for if the length of the slice is 0, thus unwrap here is fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
if &vk.alpha != *dkg_values.last().as_ref().unwrap() {
|
||||
return false;
|
||||
}
|
||||
if dkg_values
|
||||
@@ -249,7 +256,7 @@ pub fn verify_credential(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKey,
|
||||
theta: &Theta,
|
||||
public_attributes: &[Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
) -> bool {
|
||||
if public_attributes.len() + theta.pi_v.private_attributes_len()
|
||||
> verification_key.beta_g2.len()
|
||||
@@ -272,7 +279,7 @@ pub fn verify_credential(
|
||||
.iter()
|
||||
.skip(theta.pi_v.private_attributes_len()),
|
||||
)
|
||||
.map(|(pub_attr, beta_i)| beta_i * pub_attr)
|
||||
.map(|(&pub_attr, beta_i)| beta_i * pub_attr)
|
||||
.sum::<G2Projective>();
|
||||
|
||||
theta.blinded_message + signed_public_attributes
|
||||
@@ -291,14 +298,14 @@ pub fn verify_credential(
|
||||
pub fn verify(
|
||||
params: &Parameters,
|
||||
verification_key: &VerificationKey,
|
||||
public_attributes: &[Attribute],
|
||||
public_attributes: &[&Attribute],
|
||||
sig: &Signature,
|
||||
) -> bool {
|
||||
let kappa = (verification_key.alpha
|
||||
+ public_attributes
|
||||
.iter()
|
||||
.zip(verification_key.beta_g2.iter())
|
||||
.map(|(m_i, b_i)| b_i * m_i)
|
||||
.map(|(&m_i, b_i)| b_i * m_i)
|
||||
.sum::<G2Projective>())
|
||||
.to_affine();
|
||||
|
||||
@@ -320,10 +327,11 @@ mod tests {
|
||||
#[test]
|
||||
fn vk_pairing() {
|
||||
let params = setup(2).unwrap();
|
||||
let vk = keygen(¶ms).verification_key();
|
||||
let keypair = keygen(¶ms);
|
||||
let vk = keypair.verification_key();
|
||||
let mut dkg_values = vk.beta_g2.clone();
|
||||
dkg_values.push(vk.alpha);
|
||||
assert!(check_vk_pairing(¶ms, &dkg_values, &vk));
|
||||
assert!(check_vk_pairing(¶ms, &dkg_values, vk));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -340,10 +348,10 @@ mod tests {
|
||||
|
||||
let theta = prove_bandwidth_credential(
|
||||
¶ms,
|
||||
&keypair.verification_key(),
|
||||
keypair.verification_key(),
|
||||
&signature,
|
||||
serial_number,
|
||||
binding_number,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::random_scalars_refs;
|
||||
use crate::tests::helpers::tests::generate_dkg_keys;
|
||||
use crate::{
|
||||
aggregate_verification_keys, setup, tests::helpers::*, ttp_keygen, verify_credential,
|
||||
@@ -12,14 +13,14 @@ fn keygen() -> Result<(), CoconutError> {
|
||||
let params = setup(5)?;
|
||||
let node_indices = vec![15u64, 248, 33521];
|
||||
|
||||
let public_attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(public_attributes, params, 2);
|
||||
|
||||
// generate_keys
|
||||
let coconut_keypairs = ttp_keygen(¶ms, 2, 3)?;
|
||||
|
||||
let verification_keys: Vec<VerificationKey> = coconut_keypairs
|
||||
.iter()
|
||||
.map(|keypair| keypair.verification_key())
|
||||
.map(|keypair| keypair.verification_key().clone())
|
||||
.collect();
|
||||
|
||||
// aggregate verification keys
|
||||
@@ -50,14 +51,14 @@ fn dkg() -> Result<(), CoconutError> {
|
||||
let params = setup(5)?;
|
||||
let node_indices = vec![15u64, 248, 33521];
|
||||
|
||||
let public_attributes = params.n_random_scalars(2);
|
||||
random_scalars_refs!(public_attributes, params, 2);
|
||||
|
||||
// generate_keys
|
||||
let coconut_keypairs = generate_dkg_keys(5, &node_indices);
|
||||
|
||||
let verification_keys: Vec<VerificationKey> = coconut_keypairs
|
||||
.iter()
|
||||
.map(|keypair| keypair.verification_key())
|
||||
.map(|keypair| keypair.verification_key().clone())
|
||||
.collect();
|
||||
|
||||
// aggregate verification keys
|
||||
|
||||
@@ -5,15 +5,17 @@ use crate::*;
|
||||
use itertools::izip;
|
||||
use std::fmt::Debug;
|
||||
|
||||
// unwraps are fine in the test code
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub fn theta_from_keys_and_attributes(
|
||||
params: &Parameters,
|
||||
coconut_keypairs: &Vec<KeyPair>,
|
||||
indices: &[scheme::SignerIndex],
|
||||
public_attributes: &Vec<PublicAttribute>,
|
||||
public_attributes: &[&PublicAttribute],
|
||||
) -> Result<Theta, CoconutError> {
|
||||
let serial_number = params.random_scalar();
|
||||
let binding_number = params.random_scalar();
|
||||
let private_attributes = vec![serial_number, binding_number];
|
||||
let private_attributes = vec![&serial_number, &binding_number];
|
||||
|
||||
// generate commitment
|
||||
let (commitments_openings, blind_sign_request) =
|
||||
@@ -21,7 +23,7 @@ pub fn theta_from_keys_and_attributes(
|
||||
|
||||
let verification_keys: Vec<VerificationKey> = coconut_keypairs
|
||||
.iter()
|
||||
.map(|keypair| keypair.verification_key())
|
||||
.map(|keypair| keypair.verification_key().clone())
|
||||
.collect();
|
||||
|
||||
// aggregate verification keys
|
||||
@@ -33,7 +35,7 @@ pub fn theta_from_keys_and_attributes(
|
||||
for keypair in coconut_keypairs {
|
||||
let blinded_signature = blind_sign(
|
||||
params,
|
||||
&keypair.secret_key(),
|
||||
keypair.secret_key(),
|
||||
&blind_sign_request,
|
||||
public_attributes,
|
||||
)?;
|
||||
@@ -49,7 +51,7 @@ pub fn theta_from_keys_and_attributes(
|
||||
.map(|(idx, s, vk)| {
|
||||
(
|
||||
*idx,
|
||||
s.unblind(
|
||||
s.unblind_and_verify(
|
||||
params,
|
||||
vk,
|
||||
&private_attributes,
|
||||
@@ -81,13 +83,15 @@ pub fn theta_from_keys_and_attributes(
|
||||
params,
|
||||
&verification_key,
|
||||
&signature,
|
||||
serial_number,
|
||||
binding_number,
|
||||
&serial_number,
|
||||
&binding_number,
|
||||
)?;
|
||||
|
||||
Ok(theta)
|
||||
}
|
||||
|
||||
// unwraps are fine in the test code
|
||||
#[allow(clippy::unwrap_used)]
|
||||
pub fn transpose_matrix<T: Debug>(matrix: Vec<Vec<T>>) -> Vec<Vec<T>> {
|
||||
if matrix.is_empty() {
|
||||
return vec![];
|
||||
@@ -104,6 +108,17 @@ pub fn transpose_matrix<T: Debug>(matrix: Vec<Vec<T>>) -> Vec<Vec<T>> {
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! random_scalars_refs {
|
||||
( $x: ident, $params: expr, $n: expr ) => {
|
||||
let _vec = $params.n_random_scalars($n);
|
||||
#[allow(clippy::map_identity)]
|
||||
let $x = _vec.iter().collect::<Vec<_>>();
|
||||
};
|
||||
}
|
||||
|
||||
pub use random_scalars_refs;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![warn(clippy::expect_used)]
|
||||
#![warn(clippy::unwrap_used)]
|
||||
|
||||
use crate::CoconutError;
|
||||
use bls12_381::{G1Affine, G1Projective, Scalar};
|
||||
use group::GroupEncoding;
|
||||
use std::convert::TryInto;
|
||||
|
||||
pub trait Bytable
|
||||
where
|
||||
@@ -14,9 +23,67 @@ where
|
||||
Self: Bytable,
|
||||
{
|
||||
fn try_from_bs58<S: AsRef<str>>(x: S) -> Result<Self, CoconutError> {
|
||||
Self::try_from_byte_slice(&bs58::decode(x.as_ref()).into_vec().unwrap())
|
||||
let bs58_decoded = &bs58::decode(x.as_ref()).into_vec()?;
|
||||
Self::try_from_byte_slice(bs58_decoded)
|
||||
}
|
||||
fn to_bs58(&self) -> String {
|
||||
bs58::encode(self.to_byte_vec()).into_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Bytable for Scalar {
|
||||
fn to_byte_vec(&self) -> Vec<u8> {
|
||||
self.to_bytes().to_vec()
|
||||
}
|
||||
|
||||
fn try_from_byte_slice(slice: &[u8]) -> Result<Self, CoconutError> {
|
||||
let received = slice.len();
|
||||
let Ok(arr) = slice.try_into() else {
|
||||
return Err(CoconutError::UnexpectedArrayLength {
|
||||
typ: "Scalar".to_string(),
|
||||
received,
|
||||
expected: 32,
|
||||
});
|
||||
};
|
||||
|
||||
let maybe_scalar = Scalar::from_bytes(arr);
|
||||
if maybe_scalar.is_none().into() {
|
||||
Err(CoconutError::ScalarDeserializationFailure)
|
||||
} else {
|
||||
// safety: this unwrap is fine as we've just checked the element is not none
|
||||
#[allow(clippy::unwrap_used)]
|
||||
Ok(maybe_scalar.unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Base58 for Scalar {}
|
||||
|
||||
impl Bytable for G1Projective {
|
||||
fn to_byte_vec(&self) -> Vec<u8> {
|
||||
self.to_bytes().as_ref().to_vec()
|
||||
}
|
||||
|
||||
fn try_from_byte_slice(slice: &[u8]) -> Result<Self, CoconutError> {
|
||||
let received = slice.len();
|
||||
let arr: Result<[u8; 48], _> = slice.try_into();
|
||||
let Ok(bytes) = arr else {
|
||||
return Err(CoconutError::UnexpectedArrayLength {
|
||||
typ: "G1Projective".to_string(),
|
||||
received,
|
||||
expected: 48,
|
||||
});
|
||||
};
|
||||
|
||||
let maybe_g1 = G1Affine::from_compressed(&bytes);
|
||||
if maybe_g1.is_none().into() {
|
||||
Err(CoconutError::G1ProjectiveDeserializationFailure)
|
||||
} else {
|
||||
// safety: this unwrap is fine as we've just checked the element is not none
|
||||
#[allow(clippy::unwrap_used)]
|
||||
Ok(maybe_g1.unwrap().into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Base58 for G1Projective {}
|
||||
|
||||
@@ -34,6 +34,7 @@ impl Polynomial {
|
||||
// just return the last term of the polynomial
|
||||
} else if x.is_zero().into() {
|
||||
// we checked that coefficients are not empty so unwrap here is fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
*self.coefficients.first().unwrap()
|
||||
} else {
|
||||
self.coefficients
|
||||
@@ -148,6 +149,8 @@ pub(crate) fn try_deserialize_scalar_vec(
|
||||
|
||||
let mut out = Vec::with_capacity(expected_len as usize);
|
||||
for i in 0..expected_len as usize {
|
||||
// we just checked we have exactly the amount of bytes we need and thus the unwrap is fine
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let s_bytes = bytes[i * 32..(i + 1) * 32].try_into().unwrap();
|
||||
let s = match Into::<Option<Scalar>>::into(Scalar::from_bytes(&s_bytes)) {
|
||||
None => return Err(err),
|
||||
|
||||
@@ -9,7 +9,7 @@ repository = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
bytes = "1.0"
|
||||
tokio-util = { version = "0.7.4", features = ["codec"] }
|
||||
tokio-util = { workspace = true, features = ["codec"] }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
nym-sphinx-types = { path = "../types", features = ["sphinx", "outfox"] }
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
[package]
|
||||
name = "nyxd-scraper"
|
||||
version = "0.1.0"
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
homepage.workspace = true
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-trait.workspace = true
|
||||
const_format = "0.2.32"
|
||||
cosmrs.workspace = true
|
||||
eyre = "0.6.9"
|
||||
futures.workspace = true
|
||||
sha2 = "0.10.8"
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate", "time"] }
|
||||
tendermint.workspace = true
|
||||
tendermint-rpc = { workspace = true, features = ["websocket-client", "http-client"] }
|
||||
thiserror.workspace = true
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tokio-stream = "0.1.14"
|
||||
tokio-util = { version = "0.7.10", features = ["rt"]}
|
||||
tracing.workspace = true
|
||||
url.workspace = true
|
||||
|
||||
|
||||
# TEMP
|
||||
nym-bin-common = { path = "../bin-common", features = ["basic_tracing"]}
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
sqlx = { workspace = true, features = ["runtime-tokio-rustls", "sqlite", "macros", "migrate"] }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
use sqlx::{Connection, SqliteConnection};
|
||||
use std::env;
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let database_path = format!("{out_dir}/scraper-example.sqlite");
|
||||
|
||||
let mut conn = SqliteConnection::connect(&format!("sqlite://{database_path}?mode=rwc"))
|
||||
.await
|
||||
.expect("Failed to create SQLx database connection");
|
||||
|
||||
sqlx::migrate!("./sql_migrations")
|
||||
.run(&mut conn)
|
||||
.await
|
||||
.expect("Failed to perform SQLx migrations");
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
println!("cargo:rustc-env=DATABASE_URL=sqlite://{}", &database_path);
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
// for some strange reason we need to add a leading `/` to the windows path even though it's
|
||||
// not a valid windows path... but hey, it works...
|
||||
println!("cargo:rustc-env=DATABASE_URL=sqlite:///{}", &database_path);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
CREATE TABLE METADATA
|
||||
(
|
||||
id INTEGER PRIMARY KEY CHECK (id = 0),
|
||||
last_processed_height INTEGER NOT NULL
|
||||
);
|
||||
@@ -0,0 +1,76 @@
|
||||
CREATE TABLE validator
|
||||
(
|
||||
consensus_address TEXT NOT NULL PRIMARY KEY, /* Validator consensus address */
|
||||
consensus_pubkey TEXT NOT NULL UNIQUE /* Validator consensus public key */
|
||||
);
|
||||
|
||||
CREATE TABLE pre_commit
|
||||
(
|
||||
validator_address TEXT NOT NULL REFERENCES validator (consensus_address),
|
||||
height BIGINT NOT NULL,
|
||||
timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||
voting_power BIGINT NOT NULL,
|
||||
proposer_priority BIGINT NOT NULL,
|
||||
UNIQUE (validator_address, timestamp)
|
||||
);
|
||||
CREATE INDEX pre_commit_validator_address_index ON pre_commit (validator_address);
|
||||
CREATE INDEX pre_commit_height_index ON pre_commit (height);
|
||||
|
||||
CREATE TABLE block
|
||||
(
|
||||
height BIGINT UNIQUE PRIMARY KEY,
|
||||
hash TEXT NOT NULL UNIQUE,
|
||||
num_txs INTEGER DEFAULT 0,
|
||||
total_gas BIGINT DEFAULT 0,
|
||||
proposer_address TEXT REFERENCES validator (consensus_address),
|
||||
timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL
|
||||
);
|
||||
CREATE INDEX block_height_index ON block (height);
|
||||
CREATE INDEX block_hash_index ON block (hash);
|
||||
CREATE INDEX block_proposer_address_index ON block (proposer_address);
|
||||
|
||||
-- no JSONB in sqlite (yet) : )
|
||||
CREATE TABLE "transaction"
|
||||
(
|
||||
hash TEXT UNIQUE NOT NULL,
|
||||
height BIGINT NOT NULL REFERENCES block (height),
|
||||
"index" INTEGER NOT NULL,
|
||||
success BOOLEAN NOT NULL,
|
||||
|
||||
/* Body */
|
||||
num_messages INTEGER NOT NULL,
|
||||
-- messages JSONB NOT NULL,-- DEFAULT '[]'::JSONB,
|
||||
memo TEXT,
|
||||
-- signatures TEXT[] NOT NULL,
|
||||
|
||||
/* AuthInfo */
|
||||
-- signer_infos JSONB NOT NULL,-- DEFAULT '[]'::JSONB,
|
||||
-- fee JSONB NOT NULL,-- DEFAULT '{}'::JSONB,
|
||||
|
||||
/* Tx response */
|
||||
gas_wanted BIGINT DEFAULT 0,
|
||||
gas_used BIGINT DEFAULT 0,
|
||||
raw_log TEXT
|
||||
-- logs JSONB
|
||||
);
|
||||
CREATE INDEX transaction_hash_index ON "transaction" (hash);
|
||||
CREATE INDEX transaction_height_index ON "transaction" (height);
|
||||
|
||||
CREATE TABLE message
|
||||
(
|
||||
transaction_hash TEXT NOT NULL REFERENCES "transaction" (hash),
|
||||
"index" BIGINT NOT NULL,
|
||||
type TEXT NOT NULL,
|
||||
-- value JSONB NOT NULL,
|
||||
-- involved_accounts_addresses TEXT[] NOT NULL,
|
||||
|
||||
height BIGINT NOT NULL,
|
||||
CONSTRAINT unique_message_per_tx UNIQUE (transaction_hash, "index")
|
||||
);
|
||||
CREATE INDEX message_transaction_hash_index ON message (transaction_hash);
|
||||
CREATE INDEX message_type_index ON message (type);
|
||||
|
||||
CREATE TABLE pruning
|
||||
(
|
||||
last_pruned_height BIGINT NOT NULL
|
||||
);
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::block_processor::MAX_RANGE_SIZE;
|
||||
use std::cmp::min;
|
||||
use std::collections::VecDeque;
|
||||
use std::ops::Range;
|
||||
|
||||
pub(crate) fn split_request_range(request_range: Range<u32>) -> VecDeque<Range<u32>> {
|
||||
let mut requests = VecDeque::new();
|
||||
|
||||
let mut start = request_range.start;
|
||||
let mut end = min(request_range.end, start + MAX_RANGE_SIZE as u32);
|
||||
|
||||
loop {
|
||||
requests.push_back(start..end);
|
||||
start = min(start + MAX_RANGE_SIZE as u32, request_range.end);
|
||||
end = min(end + MAX_RANGE_SIZE as u32, request_range.end);
|
||||
|
||||
if start == end {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
requests
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn splitting_request_range() {
|
||||
let range = 0..100;
|
||||
let mut expected = VecDeque::new();
|
||||
expected.push_back(0..30);
|
||||
expected.push_back(30..60);
|
||||
expected.push_back(60..90);
|
||||
expected.push_back(90..100);
|
||||
assert_eq!(expected, split_request_range(range));
|
||||
|
||||
let range = 0..30;
|
||||
let mut expected = VecDeque::new();
|
||||
expected.push_back(0..30);
|
||||
assert_eq!(expected, split_request_range(range));
|
||||
|
||||
let range = 0..60;
|
||||
let mut expected = VecDeque::new();
|
||||
expected.push_back(0..30);
|
||||
expected.push_back(30..60);
|
||||
assert_eq!(expected, split_request_range(range));
|
||||
|
||||
let range = 0..5;
|
||||
let mut expected = VecDeque::new();
|
||||
expected.push_back(0..5);
|
||||
assert_eq!(expected, split_request_range(range));
|
||||
|
||||
let range = 123..200;
|
||||
let mut expected = VecDeque::new();
|
||||
expected.push_back(123..153);
|
||||
expected.push_back(153..183);
|
||||
expected.push_back(183..200);
|
||||
assert_eq!(expected, split_request_range(range));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::block_processor::helpers::split_request_range;
|
||||
use crate::block_processor::types::BlockToProcess;
|
||||
use crate::block_requester::BlockRequest;
|
||||
use crate::error::ScraperError;
|
||||
use crate::modules::{BlockModule, MsgModule, TxModule};
|
||||
use crate::rpc_client::RpcClient;
|
||||
use crate::storage::{persist_block, ScraperStorage};
|
||||
use futures::StreamExt;
|
||||
use std::collections::{BTreeMap, HashSet, VecDeque};
|
||||
use std::ops::{Add, Range};
|
||||
use std::time::Duration;
|
||||
use tokio::sync::mpsc::{Sender, UnboundedReceiver};
|
||||
use tokio::time::{interval_at, Instant};
|
||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
mod helpers;
|
||||
pub(crate) mod types;
|
||||
|
||||
const MISSING_BLOCKS_CHECK_INTERVAL: Duration = Duration::from_secs(30);
|
||||
const MAX_MISSING_BLOCKS_DELAY: Duration = Duration::from_secs(15);
|
||||
const MAX_RANGE_SIZE: usize = 30;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct PendingSync {
|
||||
request_in_flight: HashSet<u32>,
|
||||
queued_requests: VecDeque<Range<u32>>,
|
||||
}
|
||||
|
||||
impl PendingSync {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.request_in_flight.is_empty() && self.queued_requests.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlockProcessor {
|
||||
cancel: CancellationToken,
|
||||
last_processed_height: u32,
|
||||
last_processed_at: Instant,
|
||||
pending_sync: PendingSync,
|
||||
queued_blocks: BTreeMap<u32, BlockToProcess>,
|
||||
|
||||
rpc_client: RpcClient,
|
||||
incoming: UnboundedReceiverStream<BlockToProcess>,
|
||||
block_requester: Sender<BlockRequest>,
|
||||
storage: ScraperStorage,
|
||||
|
||||
// future work: rather than sending each msg to every msg module,
|
||||
// let them subscribe based on `type_url` inside the message itself
|
||||
// (like "/cosmwasm.wasm.v1.MsgExecuteContract")
|
||||
block_modules: Vec<Box<dyn BlockModule + Send>>,
|
||||
tx_modules: Vec<Box<dyn TxModule + Send>>,
|
||||
msg_modules: Vec<Box<dyn MsgModule + Send>>,
|
||||
}
|
||||
|
||||
impl BlockProcessor {
|
||||
pub async fn new(
|
||||
cancel: CancellationToken,
|
||||
incoming: UnboundedReceiver<BlockToProcess>,
|
||||
block_requester: Sender<BlockRequest>,
|
||||
storage: ScraperStorage,
|
||||
rpc_client: RpcClient,
|
||||
) -> Result<Self, ScraperError> {
|
||||
let last_processed = storage.get_last_processed_height().await?;
|
||||
|
||||
Ok(BlockProcessor {
|
||||
cancel,
|
||||
last_processed_height: last_processed.try_into().unwrap_or_default(),
|
||||
last_processed_at: Instant::now(),
|
||||
pending_sync: Default::default(),
|
||||
queued_blocks: Default::default(),
|
||||
rpc_client,
|
||||
incoming: incoming.into(),
|
||||
block_requester,
|
||||
storage,
|
||||
block_modules: vec![],
|
||||
tx_modules: vec![],
|
||||
msg_modules: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
async fn process_block(&mut self, block: BlockToProcess) -> Result<(), ScraperError> {
|
||||
info!("processing block at height {}", block.height);
|
||||
|
||||
let full_info = self.rpc_client.try_get_full_details(block).await?;
|
||||
|
||||
debug!(
|
||||
"this block has {} transaction(s)",
|
||||
full_info.transactions.len()
|
||||
);
|
||||
for tx in &full_info.transactions {
|
||||
debug!("{} has {} message(s)", tx.hash, tx.tx.body.messages.len());
|
||||
for (index, msg) in tx.tx.body.messages.iter().enumerate() {
|
||||
debug!("{index}: {:?}", msg.type_url)
|
||||
}
|
||||
}
|
||||
|
||||
// process the entire block as a transaction so that if anything fails,
|
||||
// we won't end up with a corrupted storage.
|
||||
let mut tx = self.storage.begin_processing_tx().await?;
|
||||
|
||||
persist_block(&full_info, &mut tx).await?;
|
||||
|
||||
// let the modules do whatever they want
|
||||
// the ones wanting the full block:
|
||||
for block_module in &mut self.block_modules {
|
||||
block_module.handle_block(&full_info, &mut tx).await?;
|
||||
}
|
||||
|
||||
// the ones wanting transactions:
|
||||
for block_tx in full_info.transactions {
|
||||
for tx_module in &mut self.tx_modules {
|
||||
tx_module.handle_tx(&block_tx, &mut tx).await?;
|
||||
}
|
||||
// the ones concerned with individual messages
|
||||
for (index, msg) in block_tx.tx.body.messages.iter().enumerate() {
|
||||
for msg_module in &mut self.msg_modules {
|
||||
msg_module
|
||||
.handle_msg(index, msg, &block_tx, &mut tx)
|
||||
.await?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tx.commit()
|
||||
.await
|
||||
.map_err(|source| ScraperError::StorageTxCommitFailure { source })?;
|
||||
|
||||
self.last_processed_height = full_info.block.header.height.value() as u32;
|
||||
self.last_processed_at = Instant::now();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_block_modules(&mut self, modules: Vec<Box<dyn BlockModule + Send>>) {
|
||||
self.block_modules = modules;
|
||||
}
|
||||
|
||||
pub fn set_tx_modules(&mut self, modules: Vec<Box<dyn TxModule + Send>>) {
|
||||
self.tx_modules = modules;
|
||||
}
|
||||
|
||||
pub fn set_msg_modules(&mut self, modules: Vec<Box<dyn MsgModule + Send>>) {
|
||||
self.msg_modules = modules;
|
||||
}
|
||||
|
||||
async fn maybe_request_missing_blocks(&mut self) -> Result<(), ScraperError> {
|
||||
// we're still processing, so we're good
|
||||
if self.last_processed_at.elapsed() < MAX_MISSING_BLOCKS_DELAY {
|
||||
debug!("no need to request missing blocks");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.try_request_pending().await {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// TODO: properly fill in the gaps later with BlockRequest::Specific,
|
||||
let request_range = if let Some((next_available, _)) = self.queued_blocks.first_key_value()
|
||||
{
|
||||
self.last_processed_height + 1..*next_available
|
||||
} else {
|
||||
let current_height = self.rpc_client.current_block_height().await? as u32;
|
||||
self.last_processed_height + 1..current_height + 1
|
||||
};
|
||||
|
||||
self.request_missing_blocks(request_range).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn request_missing_blocks(
|
||||
&mut self,
|
||||
request_range: Range<u32>,
|
||||
) -> Result<(), ScraperError> {
|
||||
let request_range = if request_range.len() > MAX_RANGE_SIZE {
|
||||
let mut split = split_request_range(request_range);
|
||||
|
||||
// SAFETY: we know that after the split of a non-empty range we have AT LEAST one value
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let first = split.pop_front().unwrap();
|
||||
self.pending_sync.queued_requests = split;
|
||||
self.pending_sync.request_in_flight = first.clone().collect();
|
||||
|
||||
first
|
||||
} else {
|
||||
request_range
|
||||
};
|
||||
|
||||
self.send_blocks_request(request_range).await
|
||||
}
|
||||
|
||||
// technically we're not mutating self here,
|
||||
// but we need it to help the compiler figure out the future is `Send`
|
||||
async fn send_blocks_request(&mut self, request_range: Range<u32>) -> Result<(), ScraperError> {
|
||||
debug!("requesting missing blocks: {request_range:?}");
|
||||
|
||||
self.block_requester
|
||||
.send(BlockRequest::Range(request_range))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn next_incoming(&mut self, block: BlockToProcess) {
|
||||
let height = block.height;
|
||||
|
||||
self.pending_sync.request_in_flight.remove(&height);
|
||||
|
||||
if self.last_processed_height == 0 {
|
||||
// this is the first time we've started up the process
|
||||
debug!("setting up initial processing height");
|
||||
self.last_processed_height = height - 1
|
||||
}
|
||||
|
||||
if height <= self.last_processed_height {
|
||||
warn!("we have already processed block for height {height}");
|
||||
return;
|
||||
}
|
||||
|
||||
if self.last_processed_height + 1 != height {
|
||||
if self.queued_blocks.insert(height, block).is_some() {
|
||||
warn!("we have already queued up block for height {height}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = self.process_block(block).await {
|
||||
error!("failed to process block at height {height}: {err}");
|
||||
return;
|
||||
}
|
||||
|
||||
// process as much as we can from the queue
|
||||
let mut next = height + 1;
|
||||
while let Some(next_block) = self.queued_blocks.remove(&next) {
|
||||
if let Err(err) = self.process_block(next_block).await {
|
||||
error!("failed to process queued-up block at height {next}: {err}")
|
||||
}
|
||||
next += 1;
|
||||
}
|
||||
|
||||
self.try_request_pending().await;
|
||||
}
|
||||
|
||||
async fn try_request_pending(&mut self) -> bool {
|
||||
if self.pending_sync.request_in_flight.is_empty() {
|
||||
if let Some(next_sync) = self.pending_sync.queued_requests.pop_front() {
|
||||
debug!(
|
||||
"current request range has been resolved. requesting another bunch of blocks"
|
||||
);
|
||||
if let Err(err) = self.send_blocks_request(next_sync.clone()).await {
|
||||
error!("failed to request resync blocks: {err}");
|
||||
self.pending_sync.queued_requests.push_front(next_sync);
|
||||
} else {
|
||||
self.pending_sync.request_in_flight = next_sync.collect()
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
// technically we're not mutating self here,
|
||||
// but we need it to help the compiler figure out the future is `Send`
|
||||
async fn startup_resync(&mut self) -> Result<(), ScraperError> {
|
||||
assert!(self.pending_sync.is_empty());
|
||||
|
||||
let latest_block = self.rpc_client.current_block_height().await? as u32;
|
||||
if latest_block > self.last_processed_height && self.last_processed_height != 0 {
|
||||
let request_range = self.last_processed_height + 1..latest_block + 1;
|
||||
info!("we need to request {request_range:?} to resync");
|
||||
self.request_missing_blocks(request_range).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn run(&mut self) {
|
||||
info!("starting processing loop");
|
||||
|
||||
// sure, we could be more efficient and reset it on every processed block,
|
||||
// but the overhead is so minimal that it doesn't matter
|
||||
let mut missing_check_interval = interval_at(
|
||||
Instant::now().add(MISSING_BLOCKS_CHECK_INTERVAL),
|
||||
MISSING_BLOCKS_CHECK_INTERVAL,
|
||||
);
|
||||
|
||||
if let Err(err) = self.startup_resync().await {
|
||||
error!("failed to perform startup sync: {err}");
|
||||
self.cancel.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = self.cancel.cancelled() => {
|
||||
info!("received cancellation token");
|
||||
break
|
||||
}
|
||||
_ = missing_check_interval.tick() => {
|
||||
if let Err(err) = self.maybe_request_missing_blocks().await {
|
||||
error!("failed to request missing blocks: {err}")
|
||||
}
|
||||
}
|
||||
block = self.incoming.next() => {
|
||||
match block {
|
||||
Some(block) => self.next_incoming(block).await,
|
||||
None => {
|
||||
warn!("stopped receiving new blocks");
|
||||
self.cancel.cancel();
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::error::ScraperError;
|
||||
use crate::helpers;
|
||||
use tendermint::{abci, block, tx, Block, Hash};
|
||||
use tendermint_rpc::endpoint::{block as block_endpoint, block_results, validators};
|
||||
use tendermint_rpc::event::{Event, EventData};
|
||||
|
||||
// just get all everything out of tx::Response, but parse raw `tx` bytes
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ParsedTransactionResponse {
|
||||
/// The hash of the transaction.
|
||||
///
|
||||
/// Deserialized from a hex-encoded string (there is a discrepancy between
|
||||
/// the format used for the request and the format used for the response in
|
||||
/// the Tendermint RPC).
|
||||
pub hash: Hash,
|
||||
|
||||
pub height: block::Height,
|
||||
|
||||
pub index: u32,
|
||||
|
||||
pub tx_result: abci::types::ExecTxResult,
|
||||
|
||||
pub tx: cosmrs::tx::Tx,
|
||||
|
||||
pub proof: Option<tx::Proof>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FullBlockInformation {
|
||||
/// Basic block information, including its signers.
|
||||
pub block: Block,
|
||||
|
||||
/// All of the emitted events alongside any tx results.
|
||||
pub results: block_results::Response,
|
||||
|
||||
/// Validator set for this particular block
|
||||
pub validators: validators::Response,
|
||||
|
||||
/// Transaction results from this particular block
|
||||
pub transactions: Vec<ParsedTransactionResponse>,
|
||||
}
|
||||
|
||||
impl FullBlockInformation {
|
||||
pub fn ensure_proposer(&self) -> Result<(), ScraperError> {
|
||||
let block_proposer = self.block.header.proposer_address;
|
||||
if !self
|
||||
.validators
|
||||
.validators
|
||||
.iter()
|
||||
.any(|v| v.address == block_proposer)
|
||||
{
|
||||
let proposer = helpers::validator_consensus_address(block_proposer)?;
|
||||
return Err(ScraperError::BlockProposerNotInValidatorSet {
|
||||
height: self.block.header.height.value() as u32,
|
||||
proposer: proposer.to_string(),
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct BlockToProcess {
|
||||
pub(crate) height: u32,
|
||||
pub(crate) block: Block,
|
||||
}
|
||||
|
||||
impl From<Block> for BlockToProcess {
|
||||
fn from(block: Block) -> Self {
|
||||
BlockToProcess {
|
||||
height: block.header.height.value() as u32,
|
||||
block,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Event> for BlockToProcess {
|
||||
type Error = ScraperError;
|
||||
|
||||
fn try_from(event: Event) -> Result<Self, Self::Error> {
|
||||
let query = event.query.clone();
|
||||
|
||||
// TODO: we're losing `result_begin_block` and `result_end_block` here but maybe that's fine?
|
||||
let maybe_block = match event.data {
|
||||
// we don't care about `NewBlock` until CometBFT 0.38, i.e. until we upgrade to wasmd 0.50
|
||||
EventData::NewBlock { .. } => {
|
||||
return Err(ScraperError::InvalidSubscriptionEvent {
|
||||
query,
|
||||
kind: "NewBlock".to_string(),
|
||||
})
|
||||
}
|
||||
EventData::LegacyNewBlock { block, .. } => block,
|
||||
EventData::Tx { .. } => {
|
||||
return Err(ScraperError::InvalidSubscriptionEvent {
|
||||
query,
|
||||
kind: "Tx".to_string(),
|
||||
})
|
||||
}
|
||||
EventData::GenericJsonEvent(_) => {
|
||||
return Err(ScraperError::InvalidSubscriptionEvent {
|
||||
query,
|
||||
kind: "GenericJsonEvent".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let Some(block) = maybe_block else {
|
||||
return Err(ScraperError::EmptyBlockData { query });
|
||||
};
|
||||
|
||||
Ok((*block).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<block_endpoint::Response> for BlockToProcess {
|
||||
fn from(value: block_endpoint::Response) -> Self {
|
||||
value.block.into()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::block_processor::types::BlockToProcess;
|
||||
use crate::error::ScraperError;
|
||||
use crate::rpc_client::RpcClient;
|
||||
use futures::StreamExt;
|
||||
use std::ops::Range;
|
||||
use tokio::sync::mpsc::{Receiver, UnboundedSender};
|
||||
use tokio_stream::wrappers::ReceiverStream;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{error, info, instrument, warn};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BlockRequest {
|
||||
Range(Range<u32>),
|
||||
|
||||
// UNIMPLEMENTED:
|
||||
#[allow(dead_code)]
|
||||
Specific(Vec<u32>),
|
||||
}
|
||||
|
||||
pub(crate) struct BlockRequester {
|
||||
cancel: CancellationToken,
|
||||
rpc_client: RpcClient,
|
||||
requests: ReceiverStream<BlockRequest>,
|
||||
blocks: UnboundedSender<BlockToProcess>,
|
||||
}
|
||||
|
||||
impl BlockRequester {
|
||||
pub(crate) fn new(
|
||||
cancel: CancellationToken,
|
||||
rpc_client: RpcClient,
|
||||
requests: Receiver<BlockRequest>,
|
||||
blocks: UnboundedSender<BlockToProcess>,
|
||||
) -> Self {
|
||||
BlockRequester {
|
||||
cancel,
|
||||
rpc_client,
|
||||
requests: requests.into(),
|
||||
blocks,
|
||||
}
|
||||
}
|
||||
|
||||
async fn request_and_send(&self, height: u32) -> Result<(), ScraperError> {
|
||||
let block = self.rpc_client.get_basic_block_details(height).await?;
|
||||
self.blocks.send(block.into())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn request_blocks<I: IntoIterator<Item = u32>>(&self, heights: I) {
|
||||
futures::stream::iter(heights)
|
||||
.for_each_concurrent(4, |height| async move {
|
||||
if let Err(err) = self.request_and_send(height).await {
|
||||
error!("failed to request block data: {err}")
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip(self))]
|
||||
async fn handle_blocks_request(&self, request: BlockRequest) {
|
||||
info!("received request for missed blocks");
|
||||
|
||||
match request {
|
||||
BlockRequest::Range(range) => self.request_blocks(range).await,
|
||||
BlockRequest::Specific(heights) => self.request_blocks(heights).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn run(&mut self) {
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = self.cancel.cancelled() => {
|
||||
info!("received cancellation token");
|
||||
break
|
||||
}
|
||||
maybe_request = self.requests.next() => {
|
||||
match maybe_request {
|
||||
Some(request) => self.handle_blocks_request(request).await,
|
||||
None => {
|
||||
warn!("stopped receiving new requests");
|
||||
self.cancel.cancel();
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use const_format::concatcp;
|
||||
|
||||
// TODO: make those configurable via 'NymNetworkDetails'
|
||||
|
||||
// BECH32_PREFIX defines the main SDK Bech32 prefix of an account's address
|
||||
pub const BECH32_PREFIX: &str = "n";
|
||||
|
||||
// ACCOUNT_PREFIX is the prefix for account keys
|
||||
pub const ACCOUNT_PREFIX: &str = "acc";
|
||||
// VALIDATOR_PREFIX is the prefix for validator keys
|
||||
pub const VALIDATOR_PREFIX: &str = "val";
|
||||
// CONSENSUS_PREFIX is the prefix for consensus keys
|
||||
pub const CONSENSUS_PREFIX: &str = "cons";
|
||||
// PUBKEY_PREFIX is the prefix for public keys
|
||||
pub const PUBKEY_PREFIX: &str = "pub";
|
||||
// OPERATOR_PREFIX is the prefix for operator keys
|
||||
pub const OPERATOR_PREFIX: &str = "oper";
|
||||
// ADDRESS_PREFIX is the prefix for addresses
|
||||
pub const ADDRESS_PREFIX: &str = "addr";
|
||||
|
||||
// BECH32_ACCOUNT_ADDRESS_PREFIX defines the Bech32 prefix of an account's address
|
||||
pub const BECH32_ACCOUNT_ADDRESS_PREFIX: &str = BECH32_PREFIX;
|
||||
// BECH32_ACCOUNT_PUBKEY_PREFIX defines the Bech32 prefix of an account's public key
|
||||
pub const BECH32_ACCOUNT_PUBKEY_PREFIX: &str = concatcp!(BECH32_PREFIX, PUBKEY_PREFIX);
|
||||
// BECH32_VALIDATOR_ADDRESS_PREFIX defines the Bech32 prefix of a validator's operator address
|
||||
pub const BECH32_VALIDATOR_ADDRESS_PREFIX: &str =
|
||||
concatcp!(BECH32_PREFIX, VALIDATOR_PREFIX, OPERATOR_PREFIX);
|
||||
// BECH32_VALIDATOR_PUBKEY_PREFIX defines the Bech32 prefix of a validator's operator public key
|
||||
pub const BECH32_VALIDATOR_PUBKEY_PREFIX: &str = concatcp!(
|
||||
BECH32_PREFIX,
|
||||
VALIDATOR_PREFIX,
|
||||
OPERATOR_PREFIX,
|
||||
PUBKEY_PREFIX
|
||||
);
|
||||
// BECH32_CONSENSUS_ADDRESS_PREFIX defines the Bech32 prefix of a consensus node address
|
||||
pub const BECH32_CONSENSUS_ADDRESS_PREFIX: &str =
|
||||
concatcp!(BECH32_PREFIX, VALIDATOR_PREFIX, CONSENSUS_PREFIX);
|
||||
// BECH32_CONESNSUS_PUBKEY_PREFIX defines the Bech32 prefix of a consensus node public key
|
||||
pub const BECH32_CONESNSUS_PUBKEY_PREFIX: &str = concatcp!(
|
||||
BECH32_PREFIX,
|
||||
VALIDATOR_PREFIX,
|
||||
CONSENSUS_PREFIX,
|
||||
PUBKEY_PREFIX
|
||||
);
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use tendermint::Hash;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc::error::SendError;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ScraperError {
|
||||
#[error("experienced internal database error: {0}")]
|
||||
InternalDatabaseError(#[from] sqlx::Error),
|
||||
|
||||
#[error("failed to perform startup SQL migration: {0}")]
|
||||
StartupMigrationFailure(#[from] sqlx::migrate::MigrateError),
|
||||
|
||||
#[error("can't add any modules to the scraper as it's already running")]
|
||||
ScraperAlreadyRunning,
|
||||
|
||||
#[error("failed to establish websocket connection to {url}: {source}")]
|
||||
WebSocketConnectionFailure {
|
||||
url: String,
|
||||
#[source]
|
||||
source: tendermint_rpc::Error,
|
||||
},
|
||||
|
||||
#[error("failed to establish rpc connection to {url}: {source}")]
|
||||
HttpConnectionFailure {
|
||||
url: String,
|
||||
#[source]
|
||||
source: tendermint_rpc::Error,
|
||||
},
|
||||
|
||||
#[error("failed to create chain subscription: {source}")]
|
||||
ChainSubscriptionFailure {
|
||||
#[source]
|
||||
source: tendermint_rpc::Error,
|
||||
},
|
||||
|
||||
#[error("could not obtain basic block information at height: {height}: {source}")]
|
||||
BlockQueryFailure {
|
||||
height: u32,
|
||||
#[source]
|
||||
source: tendermint_rpc::Error,
|
||||
},
|
||||
|
||||
#[error("could not obtain block results information at height: {height}: {source}")]
|
||||
BlockResultsQueryFailure {
|
||||
height: u32,
|
||||
#[source]
|
||||
source: tendermint_rpc::Error,
|
||||
},
|
||||
|
||||
#[error("could not obtain validators information at height: {height}: {source}")]
|
||||
ValidatorsQueryFailure {
|
||||
height: u32,
|
||||
#[source]
|
||||
source: tendermint_rpc::Error,
|
||||
},
|
||||
|
||||
#[error("could not obtain tx results for tx: {hash}: {source}")]
|
||||
TxResultsQueryFailure {
|
||||
hash: Hash,
|
||||
#[source]
|
||||
source: tendermint_rpc::Error,
|
||||
},
|
||||
|
||||
#[error("could not obtain current abci info: {source}")]
|
||||
AbciInfoQueryFailure {
|
||||
#[source]
|
||||
source: tendermint_rpc::Error,
|
||||
},
|
||||
|
||||
#[error("could not parse tx {hash}: {source}")]
|
||||
TxParseFailure {
|
||||
hash: Hash,
|
||||
#[source]
|
||||
source: cosmrs::ErrorReport,
|
||||
},
|
||||
|
||||
#[error("received an invalid chain subscription event of kind {kind} while we were waiting for new block data (query: '{query}')")]
|
||||
InvalidSubscriptionEvent { query: String, kind: String },
|
||||
|
||||
#[error("received block data was empty (query: '{query}')")]
|
||||
EmptyBlockData { query: String },
|
||||
|
||||
#[error("reached maximum number of allowed errors for subscription events")]
|
||||
MaximumSubscriptionFailures,
|
||||
|
||||
#[error("failed to begin storage tx: {source}")]
|
||||
StorageTxBeginFailure {
|
||||
#[source]
|
||||
source: sqlx::Error,
|
||||
},
|
||||
|
||||
#[error("failed to commit storage tx: {source}")]
|
||||
StorageTxCommitFailure {
|
||||
#[source]
|
||||
source: sqlx::Error,
|
||||
},
|
||||
|
||||
#[error("failed to send on a closed channel")]
|
||||
ClosedChannelError,
|
||||
|
||||
#[error("failed to parse validator's address: {source}")]
|
||||
MalformedValidatorAddress {
|
||||
#[source]
|
||||
source: eyre::Report,
|
||||
},
|
||||
|
||||
#[error("failed to parse validator's address: {source}")]
|
||||
MalformedValidatorPubkey {
|
||||
#[source]
|
||||
source: eyre::Report,
|
||||
},
|
||||
|
||||
#[error(
|
||||
"could not find the block proposer ('{proposer}') for height {height} in the validator set"
|
||||
)]
|
||||
BlockProposerNotInValidatorSet { height: u32, proposer: String },
|
||||
|
||||
#[error(
|
||||
"could not find validator information for {address}; the validator has signed a commit"
|
||||
)]
|
||||
MissingValidatorInfoCommitted { address: String },
|
||||
}
|
||||
|
||||
impl<T> From<SendError<T>> for ScraperError {
|
||||
fn from(_: SendError<T>) -> Self {
|
||||
ScraperError::ClosedChannelError
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::block_processor::types::ParsedTransactionResponse;
|
||||
use crate::constants::{BECH32_CONESNSUS_PUBKEY_PREFIX, BECH32_CONSENSUS_ADDRESS_PREFIX};
|
||||
use crate::error::ScraperError;
|
||||
use cosmrs::AccountId;
|
||||
use sha2::{Digest, Sha256};
|
||||
use tendermint::{account, PublicKey};
|
||||
use tendermint::{validator, Hash};
|
||||
use tendermint_rpc::endpoint::validators;
|
||||
|
||||
pub(crate) fn tx_hash<M: AsRef<[u8]>>(raw_tx: M) -> Hash {
|
||||
Hash::Sha256(Sha256::digest(raw_tx).into())
|
||||
}
|
||||
|
||||
pub(crate) fn validator_pubkey_to_bech32(pubkey: PublicKey) -> Result<AccountId, ScraperError> {
|
||||
// TODO: this one seem to attach additional prefix to they pubkeys, is that what we want instead maybe?
|
||||
// Ok(pubkey.to_bech32(BECH32_CONESNSUS_PUBKEY_PREFIX))
|
||||
AccountId::new(BECH32_CONESNSUS_PUBKEY_PREFIX, &pubkey.to_bytes())
|
||||
.map_err(|source| ScraperError::MalformedValidatorPubkey { source })
|
||||
}
|
||||
|
||||
pub(crate) fn validator_consensus_address(id: account::Id) -> Result<AccountId, ScraperError> {
|
||||
AccountId::new(BECH32_CONSENSUS_ADDRESS_PREFIX, id.as_ref())
|
||||
.map_err(|source| ScraperError::MalformedValidatorAddress { source })
|
||||
}
|
||||
|
||||
pub(crate) fn tx_gas_sum(txs: &[ParsedTransactionResponse]) -> i64 {
|
||||
txs.iter().map(|tx| tx.tx_result.gas_used).sum()
|
||||
}
|
||||
|
||||
pub(crate) fn validator_info(
|
||||
id: account::Id,
|
||||
validators: &validators::Response,
|
||||
) -> Result<&validator::Info, ScraperError> {
|
||||
match validators.validators.iter().find(|v| v.address == id) {
|
||||
Some(info) => Ok(info),
|
||||
None => {
|
||||
let addr = validator_consensus_address(id)?;
|
||||
Err(ScraperError::MissingValidatorInfoCommitted {
|
||||
address: addr.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#![warn(clippy::expect_used)]
|
||||
#![warn(clippy::unwrap_used)]
|
||||
|
||||
pub(crate) mod block_processor;
|
||||
pub(crate) mod block_requester;
|
||||
pub mod constants;
|
||||
pub mod error;
|
||||
pub(crate) mod helpers;
|
||||
pub mod modules;
|
||||
pub(crate) mod rpc_client;
|
||||
pub(crate) mod scraper;
|
||||
pub mod storage;
|
||||
|
||||
pub use modules::{BlockModule, MsgModule, TxModule};
|
||||
pub use scraper::{Config, NyxdScraper};
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::block_processor::types::FullBlockInformation;
|
||||
use crate::error::ScraperError;
|
||||
use crate::storage::StorageTransaction;
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
pub trait BlockModule {
|
||||
async fn handle_block(
|
||||
&mut self,
|
||||
block: &FullBlockInformation,
|
||||
storage_tx: &mut StorageTransaction,
|
||||
) -> Result<(), ScraperError>;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
mod block_module;
|
||||
mod msg_module;
|
||||
mod tx_module;
|
||||
|
||||
pub use block_module::BlockModule;
|
||||
pub use msg_module::MsgModule;
|
||||
pub use tx_module::TxModule;
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::block_processor::types::ParsedTransactionResponse;
|
||||
use crate::error::ScraperError;
|
||||
use crate::storage::StorageTransaction;
|
||||
use async_trait::async_trait;
|
||||
use cosmrs::Any;
|
||||
|
||||
#[async_trait]
|
||||
pub trait MsgModule {
|
||||
async fn handle_msg(
|
||||
&mut self,
|
||||
index: usize,
|
||||
msg: &Any,
|
||||
tx: &ParsedTransactionResponse,
|
||||
storage_tx: &mut StorageTransaction,
|
||||
) -> Result<(), ScraperError>;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user