Compare commits

..

27 Commits

Author SHA1 Message Date
Max Hampshire 9fa3d1e3e9 uncommented env load command 2022-04-01 16:30:47 +02:00
Max Hampshire d631939aa9 added new path to gitignore 2022-04-01 16:29:26 +02:00
Max Hampshire ea37628539 updated .env file + uncommented networks in hardhat config file 2022-04-01 16:29:04 +02:00
Max Hampshire 8c8a9f62b9 small updates to contract comments and fleshed out readme 2022-04-01 16:21:05 +02:00
Jon Häggblad 7a53821af9 Merge pull request #1185 from nymtech/feature/wallet-support-remove-account
wallet: support removing accounts from the wallet file
2022-04-01 14:57:32 +02:00
Jon Häggblad efe6df12c9 wallet: remove unused 2022-04-01 14:46:44 +02:00
Jon Häggblad 23fb34f564 wallet: support removing accounts from the wallet file 2022-04-01 14:42:05 +02:00
Mark Sinclair 45b41d9e20 Update README.md 2022-04-01 10:07:02 +01:00
Jon Häggblad c85b0ad07d Merge pull request #1170 from nymtech/feature/connection-test-nymd-api-urls-indep
wallet: connection test nymd and api urls independently
2022-03-30 00:11:20 +02:00
Jon Häggblad adf4537183 rustfmt 2022-03-30 00:02:34 +02:00
Jon Häggblad 1cf101d50f connection-tester: refine log statements 2022-03-29 23:57:54 +02:00
Jon Häggblad e91e6943c6 connection-tester: add nymd-client cfg 2022-03-29 23:53:09 +02:00
Jon Häggblad 700f6a4e98 connection-tester: add missing timeout for nymd test 2022-03-29 23:53:09 +02:00
Jon Häggblad b759e5e7f2 connection-tester: extract out connection test methods 2022-03-29 23:53:09 +02:00
Jon Häggblad deefa09066 connection-tester: extract out setup method 2022-03-29 23:53:09 +02:00
Jon Häggblad 3f6cb919ac connection-tester: extract or collection method 2022-03-29 23:53:09 +02:00
Jon Häggblad d08bf61905 wallet: rustfmt 2022-03-29 23:53:09 +02:00
Jon Häggblad da18a60a91 wallet: remove deprecated validator_health checks 2022-03-29 23:53:09 +02:00
Jon Häggblad cec7496794 connection-tester: cap number of urls tested async 2022-03-29 23:53:09 +02:00
Jon Häggblad dd82b24d61 wallet: skip duplicate validator url entries 2022-03-29 23:53:09 +02:00
Jon Häggblad df827b6b09 validator-client: rework connection tester 2022-03-29 23:53:09 +02:00
Mark Sinclair cb25cc2eb9 Merge pull request #1178 from nymtech/feature/wallet-storybook
Add storybook to wallet
2022-03-29 17:26:46 +01:00
Mark Sinclair abf7e1f6ad Bundle fonts into wallet 2022-03-29 17:18:33 +01:00
Mark Sinclair 0f5137ea24 Add storybook to nym-wallet 2022-03-29 17:16:00 +01:00
Bogdan-Ștefan Neacșu 34903bfae6 Remove unneeded dep in wallet 2022-03-29 16:14:30 +03:00
Bogdan-Ștefan Neacşu 9e8f550e6d Feature/signature on deposit (#1151)
* Add placeholder client for implementing coconut interactions

* Add db for persistance

* Add nymd client

* Add new coconut-bandwidth contract

* Call deposit function

* Introduce error handling

* Call the old flow of getting a signature

* List available tx hashes

* Add signed req in body

* Save signature received

* Add event generation

* Checks in validator-api

* Fail with error instead of panic in validator-api route

* Fix contract address and small bug

* Add file db for storing previous signatures

* Encrypt and store data in validator-api

* Decrypt the received signature

* Remove tx hashes after getting credentials

* Small listing changes in client

* Change response so that it easier to serialize

* Error message is sent to client for display

* Remove already signed error and return the previous sig

* Merge signature with deposit data in client

* Entrypoint for getting the encrypted signature

* Refactor blinding stuff so that it can be backed up

* Backed up the blind sign request

* Client can re-request the encrypted signature shares

* Update crypto features

* Fix clippy

* Activate instantiate test and remove unused code

* Add tx tests

* Add verification key endpoint test

* Voucher consistency test

* Test for some errors and a race condition on blind signing

* Refactor and add client trait for enabling better testing env

* Test some more of blind sign

* Finished testing all extract_encryption_key paths

* Split into function test and endpoint test

* Test for correct signature

* Test for state functions

* Remove print

* Test blind_sign endpoint

* Test for cached signature endpoint

* Stricter types in voucher

* Rename signature with partial_bandwidth_credential

* Extra route levels

* Length check and remove some unused code from coconut interface

* Renamed coconut-bandwidth common crate

* Renamed verification_key to identity_key

* Use const instead of hardcoded values

* Use type aliases for crypto algorithms

* Remove unused mods, until needed

* Remove unneeded unwrap

* Fix some coconut issues that were blocking the wasm client build

* Move from sled to existing sql database

* Update tests for new db type

* Fix wasm for coconut too

* Remove sled from dependencies
2022-03-29 15:03:38 +03:00
Mark Sinclair 8ad3565f2c Create nym-release-publish.yml 2022-03-28 18:16:41 +01:00
125 changed files with 8312 additions and 1359 deletions
+43
View File
@@ -0,0 +1,43 @@
name: Publish Nym binaries
on:
release:
types: [created]
jobs:
publish-nym:
strategy:
fail-fast: false
matrix:
platform: [ubuntu-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
- name: Check the release tag starts with `nym-binaries-`
if: startsWith(github.ref, 'refs/tags/nym-binaries-') == false
uses: actions/github-script@v3
with:
script: |
core.setFailed('Release tag did not start with nym-binaries-...')
- name: Install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build all binaries
uses: actions-rs/cargo@v1
with:
command: build
args: --workspace --release
- name: Upload to release based on tag name
uses: softprops/action-gh-release@v1
with:
files: |
target/release/nym-client
target/release/nym-gateway
target/release/nym-mixnode
target/release/nym-socks5-client
target/release/nym-validator-api
@@ -0,0 +1,55 @@
name: Nym Wallet Storybook
on:
push:
paths:
- 'nym-wallet/**'
jobs:
build:
runs-on: custom-runner-linux
steps:
- uses: actions/checkout@v2
- name: Install rsync
run: sudo apt-get install rsync
- uses: rlespinasse/github-slug-action@v3.x
- uses: actions/setup-node@v2
with:
node-version: '16'
- name: Setup yarn
run: npm install -g yarn
- name: Build dependencies
run: yarn && yarn build
- name: Build storybook
run: yarn storybook:build
working-directory: ./nym-wallet
- name: Deploy branch to CI www (storybook)
continue-on-error: true
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.CI_WWW_SSH_PRIVATE_KEY }}
ARGS: "-rltgoDzvO --delete"
SOURCE: "nym-wallet/storybook-static/"
REMOTE_HOST: ${{ secrets.CI_WWW_REMOTE_HOST }}
REMOTE_USER: ${{ secrets.CI_WWW_REMOTE_USER }}
TARGET: ${{ secrets.CI_WWW_REMOTE_TARGET }}/wallet-${{ env.GITHUB_REF_SLUG }}
EXCLUDE: "/dist/, /node_modules/"
- name: Keybase - Node Install
run: npm install
working-directory: .github/workflows/support-files
- name: Keybase - Send Notification
env:
NYM_NOTIFICATION_KIND: nym-wallet
NYM_PROJECT_NAME: "nym-wallet"
NYM_CI_WWW_BASE: "${{ secrets.NYM_CI_WWW_BASE }}"
NYM_CI_WWW_LOCATION: "wallet-${{ env.GITHUB_REF_SLUG }}"
GIT_COMMIT_MESSAGE: "${{ github.event.head_commit.message }}"
GIT_BRANCH: "${GITHUB_REF##*/}"
KEYBASE_NYMBOT_USERNAME: "${{ secrets.KEYBASE_NYMBOT_USERNAME }}"
KEYBASE_NYMBOT_PAPERKEY: "${{ secrets.KEYBASE_NYMBOT_PAPERKEY }}"
KEYBASE_NYMBOT_TEAM: "${{ secrets.KEYBASE_NYMBOT_TEAM }}"
KEYBASE_NYM_CHANNEL: "ci-nym-wallet"
IS_SUCCESS: "${{ job.status == 'success' }}"
uses: docker://keybaseio/client:stable-node
with:
args: .github/workflows/support-files/notifications/entry_point.sh
@@ -3,7 +3,7 @@ require('dotenv').config();
const Bot = require('keybase-bot');
let context = {
kinds: ['ts-packages', 'network-explorer', 'nightly'],
kinds: ['nym-wallet', 'ts-packages', 'network-explorer', 'nightly'],
};
/**
@@ -0,0 +1,29 @@
const Handlebars = require('handlebars');
const fs = require('fs');
const path = require('path');
async function addToContextAndValidate(context) {
if (!context.env.NYM_CI_WWW_LOCATION) {
throw new Error('Please ensure the env var NYM_CI_WWW_LOCATION is set');
}
if (!context.env.NYM_CI_WWW_BASE) {
throw new Error('Please ensure the env var NYM_CI_WWW_BASE is set');
}
}
async function getMessageBody(context) {
const source = fs
.readFileSync(
context.env.IS_SUCCESS === 'true'
? path.resolve(__dirname, 'templates', 'success')
: path.resolve(__dirname, 'templates', 'failure'),
)
.toString();
const template = Handlebars.compile(source);
return template(context);
}
module.exports = {
addToContextAndValidate,
getMessageBody,
};
@@ -0,0 +1,11 @@
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥
> :rocket: {{ env.NYM_PROJECT_NAME }}
> 🔴 **FAILURE** :cry:
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
Commit message:
```
{{ env.GIT_COMMIT_MESSAGE }}
```
@@ -0,0 +1,15 @@
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩
> :rocket: {{ env.NYM_PROJECT_NAME }}
> ✅ **SUCCESS**
> ➡️➡️➡️➡️➡️ **View output:**
> `storybook`: https://{{ env.NYM_CI_WWW_LOCATION }}.{{ env.NYM_CI_WWW_BASE }}
> `branch` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/tree/{{ env.GIT_BRANCH_NAME }}
> `commit` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/commit/{{ env.GITHUB_SHA }}
> `build ` {{ env.GITHUB_SERVER_URL }}/{{ env.GITHUB_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}
Commit message by `{{ env.GITHUB_ACTOR }}` at {{ timestamp }}:
```
{{ env.GIT_COMMIT_MESSAGE }}
```
Generated
+121 -32
View File
@@ -20,7 +20,7 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cipher 0.3.0",
"cpufeatures",
"ctr 0.8.0",
@@ -33,7 +33,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cipher 0.4.3",
"cpufeatures",
]
@@ -313,7 +313,7 @@ dependencies = [
"arrayref",
"arrayvec 0.7.2",
"cc",
"cfg-if",
"cfg-if 1.0.0",
"constant_time_eq",
"digest 0.10.3",
]
@@ -453,6 +453,12 @@ dependencies = [
"jobserver",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
@@ -581,13 +587,23 @@ dependencies = [
"bitflags",
]
[[package]]
name = "coconut-bandwidth-contract-common"
version = "0.1.0"
dependencies = [
"schemars",
"serde",
]
[[package]]
name = "coconut-interface"
version = "0.1.0"
dependencies = [
"bs58",
"getset",
"nymcoconut",
"serde",
"thiserror",
]
[[package]]
@@ -817,7 +833,30 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
name = "credential"
version = "0.1.0"
dependencies = [
"async-trait",
"bip39",
"cfg-if 0.1.10",
"clap 3.1.6",
"coconut-bandwidth-contract-common",
"coconut-interface",
"credentials",
"crypto",
"network-defaults",
"pemstore",
"pickledb",
"rand 0.7.3",
"serde",
"thiserror",
"tokio",
"url",
"validator-client",
]
[[package]]
@@ -826,8 +865,10 @@ version = "0.1.0"
dependencies = [
"bls12_381",
"coconut-interface",
"cosmrs",
"crypto",
"network-defaults",
"rand 0.7.3",
"thiserror",
"url",
"validator-client",
@@ -875,7 +916,7 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdbfe11fe19ff083c48923cf179540e8cd0535903dc35e178a1fdeeb59aef51f"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-utils",
]
@@ -885,7 +926,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-epoch",
"crossbeam-utils",
]
@@ -897,7 +938,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
dependencies = [
"autocfg 1.1.0",
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-utils",
"lazy_static",
"memoffset",
@@ -910,7 +951,7 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crossbeam-utils",
]
@@ -920,7 +961,7 @@ version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"lazy_static",
]
@@ -1132,7 +1173,7 @@ version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"num_cpus",
]
@@ -1380,7 +1421,7 @@ version = "0.8.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@@ -1588,7 +1629,7 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"crc32fast",
"libc",
"miniz_oxide",
@@ -1814,6 +1855,7 @@ name = "gateway-client"
version = "0.1.0"
dependencies = [
"coconut-interface",
"cosmrs",
"credentials",
"crypto",
"fluvio-wasm-timer",
@@ -1832,6 +1874,7 @@ dependencies = [
"tokio-tungstenite",
"tungstenite",
"url",
"validator-client",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-utils",
@@ -1896,7 +1939,7 @@ version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"js-sys",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
@@ -1909,7 +1952,7 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"js-sys",
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
@@ -2397,7 +2440,7 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
@@ -2494,7 +2537,7 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "903ae2481bcdfdb7b68e0a9baa4b7c9aff600b9ae2e8e5bb5833b8c91ab851ea"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"ecdsa 0.12.4",
"elliptic-curve 0.10.6",
"sha2",
@@ -2506,7 +2549,7 @@ version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19c3a5e0a0b8450278feda242592512e09f61c72e018b8cd5c859482802daf2d"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"ecdsa 0.13.4",
"elliptic-curve 0.11.12",
"sec1",
@@ -2579,6 +2622,12 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lioness"
version = "0.1.2"
@@ -2606,7 +2655,7 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
]
[[package]]
@@ -2615,7 +2664,7 @@ version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc5c7d328e32cc4954e8e01193d7f0ef5ab257b5090b70a964e099a36034309"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"generator",
"scoped-tls",
"serde",
@@ -2814,7 +2863,7 @@ dependencies = [
name = "network-defaults"
version = "0.1.0"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"hex-literal",
"once_cell",
"serde",
@@ -3079,9 +3128,11 @@ name = "nym-validator-api"
version = "0.12.0"
dependencies = [
"anyhow",
"async-trait",
"attohttpc",
"cfg-if",
"cfg-if 1.0.0",
"clap 2.34.0",
"coconut-bandwidth-contract-common",
"coconut-interface",
"config",
"console-subscriber",
@@ -3095,6 +3146,7 @@ dependencies = [
"humantime-serde",
"log",
"mixnet-contract-common",
"nymcoconut",
"nymsphinx",
"pin-project",
"pretty_env_logger",
@@ -3300,7 +3352,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
dependencies = [
"bitflags",
"cfg-if",
"cfg-if 1.0.0",
"foreign-types",
"libc",
"once_cell",
@@ -3404,7 +3456,7 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall",
@@ -3418,7 +3470,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"libc",
"redox_syscall",
"smallvec 1.8.0",
@@ -3567,6 +3619,19 @@ dependencies = [
"indexmap",
]
[[package]]
name = "pickledb"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9161694d67f6c5163519d42be942ae36bbdb55f439460144f105bc4f9f7d1d61"
dependencies = [
"bincode",
"serde",
"serde_cbor",
"serde_json",
"serde_yaml",
]
[[package]]
name = "pin-project"
version = "1.0.10"
@@ -4720,6 +4785,18 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a521f2940385c165a24ee286aa8599633d162077a54bdcae2a6fd5a7bfa7a0"
dependencies = [
"indexmap",
"ryu",
"serde",
"yaml-rust",
]
[[package]]
name = "serial_test"
version = "0.5.1"
@@ -4761,7 +4838,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.9.0",
"opaque-debug 0.3.0",
@@ -4773,7 +4850,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.10.3",
]
@@ -4800,7 +4877,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cfg-if 1.0.0",
"cpufeatures",
"digest 0.9.0",
"opaque-debug 0.3.0",
@@ -5249,7 +5326,7 @@ version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"fastrand",
"libc",
"redox_syscall",
@@ -5727,7 +5804,7 @@ version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a1bdf54a7c28a2bbf701e1d2233f6c77f473486b94bee4f9678da5a148dca7f"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"log",
"pin-project-lite",
"tracing-attributes",
@@ -5991,10 +6068,12 @@ dependencies = [
"base64",
"bip39",
"coconut-interface",
"colored",
"config",
"cosmrs",
"cosmwasm-std",
"flate2",
"futures",
"itertools",
"log",
"mixnet-contract-common",
@@ -6005,6 +6084,7 @@ dependencies = [
"serde_json",
"sha2",
"thiserror",
"tokio",
"ts-rs",
"url",
"validator-api-requests",
@@ -6037,7 +6117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cf88d94e969e7956d924ba70741316796177fa0c79a2c9f4ab04998d96e966e"
dependencies = [
"anyhow",
"cfg-if",
"cfg-if 1.0.0",
"chrono",
"enum-iterator",
"getset",
@@ -6132,7 +6212,7 @@ version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"serde",
"serde_json",
"wasm-bindgen-macro",
@@ -6159,7 +6239,7 @@ version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39"
dependencies = [
"cfg-if",
"cfg-if 1.0.0",
"js-sys",
"wasm-bindgen",
"web-sys",
@@ -6412,6 +6492,15 @@ dependencies = [
"zeroize",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]]
name = "yansi"
version = "0.5.0"
+2
View File
@@ -14,6 +14,7 @@ panic = "abort"
resolver = "2"
members = [
"clients/client-core",
"clients/credential",
"clients/native",
"clients/native/websocket-requests",
"clients/socks5",
@@ -26,6 +27,7 @@ members = [
"common/credentials",
"common/crypto",
"common/bandwidth-claim-contract",
"common/cosmwasm-smart-contracts/coconut-bandwidth-contract",
"common/cosmwasm-smart-contracts/contracts-common",
"common/cosmwasm-smart-contracts/mixnet-contract",
"common/cosmwasm-smart-contracts/vesting-contract",
+1 -1
View File
@@ -1,4 +1,4 @@
<svg viewBox="0 0 210 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg width="210" height="56" viewBox="0 0 210 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M45.8829 0.142822H45.7169V0.28114V48.637L25.3289 0.225818L25.3012 0.142822H25.1905H13.6272H0.652966H0.514648V0.28114V55.7189V55.8572H0.652966H13.6272H13.7655V55.7189V7.28002L34.2365 55.7742L34.2642 55.8572H34.3748H45.8829H58.8294H58.9677V55.7189V0.28114V0.142822H58.8294H45.8829Z"/>
<path d="M209.347 0.142822H184.616H184.477L184.45 0.253483L171.78 48.8583L159.082 0.253483L159.054 0.142822H158.944H134.157H133.991V0.28114V55.7189V55.8572H134.157H147.104H147.242V55.7189V7.66731L159.774 55.7466L159.801 55.8572H159.94H183.564H183.675L183.703 55.7466L196.234 7.66731V55.7189V55.8572H196.373H209.347H209.485V55.7189V0.28114V0.142822H209.347Z"/>
<path d="M112.663 0.142822H112.58L112.552 0.198153L96.8116 27.5574L80.988 0.198153L80.9604 0.142822H80.8774H65.9114H65.6348L65.7731 0.364136L90.1447 42.5787V55.7189V55.8572H90.283H103.257H103.396V55.7189V42.5787L127.767 0.364136L127.905 0.142822H127.629H112.663Z"/>

Before

Width:  |  Height:  |  Size: 1011 B

After

Width:  |  Height:  |  Size: 1.0 KiB

+1 -1
View File
@@ -32,4 +32,4 @@ validator-client = { path = "../../common/client-libs/validator-client" }
tempfile = "3.1.0"
[features]
coconut = []
coconut = ["gateway-client/coconut", "gateway-requests/coconut"]
+3453
View File
File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
[package]
name = "credential"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-trait = "0.1.52"
bip39 = "1.0.1"
cfg-if = "0.1"
clap = { version = "3.0.10", features = ["cargo", "derive"] }
pickledb = "0.4.1"
rand = "0.7.3"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1.0"
url = "2.2"
tokio = { version = "1.4", features = ["rt-multi-thread", "net", "signal", "macros"] } # async runtime
coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
coconut-interface = { path = "../../common/coconut-interface" }
credentials = { path = "../../common/credentials" }
crypto = { path = "../../common/crypto", features = ["rand", "asymmetric", "symmetric", "aes", "hashing"] }
network-defaults = { path = "../../common/network-defaults" }
pemstore = { path = "../../common/pemstore" }
validator-client = { path = "../../common/client-libs/validator-client", features = ["nymd-client"] }
[features]
coconut = ["credentials/coconut"]
+69
View File
@@ -0,0 +1,69 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bip39::Mnemonic;
use coconut_bandwidth_contract_common::deposit::DepositData;
use std::str::FromStr;
use url::Url;
use crate::error::Result;
use crate::{CONTRACT_ADDRESS, MNEMONIC, NYMD_URL};
use coconut_bandwidth_contract_common::msg::ExecuteMsg;
use network_defaults::DEFAULT_NETWORK;
use validator_client::nymd::{
AccountId, CosmosCoin, Decimal, Denom, NymdClient, SigningNymdClient,
};
pub(crate) struct Client {
nymd_client: NymdClient<SigningNymdClient>,
denom: Denom,
contract_address: AccountId,
}
impl Client {
pub fn new() -> Self {
let nymd_url = Url::from_str(NYMD_URL).unwrap();
let mnemonic = Mnemonic::from_str(MNEMONIC).unwrap();
let nymd_client = NymdClient::connect_with_mnemonic(
DEFAULT_NETWORK,
nymd_url.as_ref(),
None,
None,
None,
mnemonic,
None,
)
.unwrap();
let denom = Denom::from_str(network_defaults::DENOM).unwrap();
let contract_address = AccountId::from_str(CONTRACT_ADDRESS).unwrap();
Client {
nymd_client,
denom,
contract_address,
}
}
pub async fn deposit(
&self,
amount: u64,
info: &str,
verification_key: String,
encryption_key: String,
) -> Result<String> {
let req = ExecuteMsg::DepositFunds {
data: DepositData::new(info.to_string(), verification_key, encryption_key),
};
let funds = vec![CosmosCoin {
denom: self.denom.clone(),
amount: Decimal::from(amount),
}];
Ok(self
.nymd_client
.execute(&self.contract_address, &req, Default::default(), "", funds)
.await?
.transaction_hash
.to_string())
}
}
+186
View File
@@ -0,0 +1,186 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use async_trait::async_trait;
use clap::{Args, Subcommand};
use pickledb::PickleDb;
use rand::rngs::OsRng;
use std::str::FromStr;
use url::Url;
use coconut_interface::{Attribute, Base58, BlindSignRequest, Bytable, Parameters};
use credentials::coconut::bandwidth::{BandwidthVoucher, TOTAL_ATTRIBUTES};
use credentials::coconut::utils::obtain_aggregate_signature;
use crypto::asymmetric::{encryption, identity};
use network_defaults::VOUCHER_INFO;
use validator_client::nymd::tx::Hash;
use crate::client::Client;
use crate::error::{CredentialClientError, Result};
use crate::state::{KeyPair, RequestData, State};
use crate::SIGNER_AUTHORITIES;
#[derive(Subcommand)]
pub(crate) enum Commands {
/// Deposit funds for buying coconut credential
Deposit(Deposit),
/// Lists the tx hashes of previous deposits
ListDeposits(ListDeposits),
/// Get a credential for a given deposit
GetCredential(GetCredential),
}
#[async_trait]
pub(crate) trait Execute {
async fn execute(&self, db: &mut PickleDb) -> Result<()>;
}
#[derive(Args, Clone)]
pub(crate) struct Deposit {
/// The amount that needs to be deposited
#[clap(long)]
amount: u64,
}
#[async_trait]
impl Execute for Deposit {
async fn execute(&self, db: &mut PickleDb) -> Result<()> {
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 client = Client::new();
let tx_hash = client
.deposit(
self.amount,
VOUCHER_INFO,
signing_keypair.public_key.clone(),
encryption_keypair.public_key.clone(),
)
.await?;
let state = State {
amount: self.amount,
tx_hash: tx_hash.clone(),
signing_keypair,
encryption_keypair,
blind_request_data: None,
signature: None,
};
db.set(&tx_hash, &state).unwrap();
println!("{:?}", state);
Ok(())
}
}
#[derive(Args, Clone)]
pub(crate) struct ListDeposits {}
#[async_trait]
impl Execute for ListDeposits {
async fn execute(&self, db: &mut PickleDb) -> Result<()> {
for kv in db.iter() {
println!("{:?}", kv.get_value::<State>());
}
Ok(())
}
}
#[derive(Args, Clone)]
pub(crate) struct GetCredential {
/// The hash of a successful deposit transaction
#[clap(long)]
tx_hash: String,
/// If we want to get the signature without attaching a blind sign request; it is expected that
/// there is already a signature stored on the signer
#[clap(long, parse(from_flag))]
__no_request: bool,
}
#[async_trait]
impl Execute for GetCredential {
async fn execute(&self, db: &mut PickleDb) -> Result<()> {
let mut state = db
.get::<State>(&self.tx_hash)
.ok_or(CredentialClientError::NoDeposit)?;
let urls = SIGNER_AUTHORITIES.map(|addr| Url::from_str(addr).unwrap());
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let bandwidth_credential_attributes = if self.__no_request {
if let Some(blind_request_data) = state.blind_request_data {
let serial_number =
Attribute::try_from_byte_slice(&blind_request_data.serial_number)
.map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?;
let binding_number =
Attribute::try_from_byte_slice(&blind_request_data.binding_number)
.map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?;
let pedersen_commitments_openings = vec![
Attribute::try_from_byte_slice(&blind_request_data.first_attribute)
.map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?,
Attribute::try_from_byte_slice(&blind_request_data.second_attribute)
.map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?,
];
let blind_sign_request =
BlindSignRequest::from_bytes(blind_request_data.blind_sign_req.as_slice())
.map_err(|_| CredentialClientError::CorruptedBlindSignRequest)?;
BandwidthVoucher::new_with_blind_sign_req(
[serial_number, binding_number],
[&state.amount.to_string(), VOUCHER_INFO],
Hash::from_str(&self.tx_hash)
.map_err(|_| CredentialClientError::InvalidTxHash)?,
identity::PrivateKey::from_base58_string(&state.signing_keypair.private_key)?,
encryption::PrivateKey::from_base58_string(
&state.encryption_keypair.private_key,
)?,
pedersen_commitments_openings,
blind_sign_request,
)
} else {
return Err(CredentialClientError::NoLocalBlindSignRequest);
}
} else {
BandwidthVoucher::new(
&params,
state.amount.to_string(),
VOUCHER_INFO.to_string(),
Hash::from_str(&self.tx_hash).map_err(|_| CredentialClientError::InvalidTxHash)?,
identity::PrivateKey::from_base58_string(&state.signing_keypair.private_key)?,
encryption::PrivateKey::from_base58_string(&state.encryption_keypair.private_key)?,
)
};
// Back up the blind sign req data, in case of sporadic failures
state.blind_request_data = Some(RequestData::new(
bandwidth_credential_attributes.get_private_attributes(),
bandwidth_credential_attributes.pedersen_commitments_openings(),
bandwidth_credential_attributes.blind_sign_request(),
)?);
db.set(&self.tx_hash, &state).unwrap();
let signature =
obtain_aggregate_signature(&params, &bandwidth_credential_attributes, &urls).await?;
state.signature = Some(signature.to_bs58());
db.set(&self.tx_hash, &state).unwrap();
println!("Signature: {:?}", state.signature);
Ok(())
}
}
#[derive(Args, Clone)]
pub(crate) struct SpendCredential {
/// Spend one of the acquired credentials
#[clap(long)]
id: usize,
}
#[async_trait]
impl Execute for SpendCredential {
async fn execute(&self, _db: &mut PickleDb) -> Result<()> {
Ok(())
}
}
+41
View File
@@ -0,0 +1,41 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use thiserror::Error;
use credentials::error::Error as CredentialError;
use crypto::asymmetric::encryption::KeyRecoveryError;
use crypto::asymmetric::identity::Ed25519RecoveryError;
use validator_client::nymd::error::NymdError;
pub type Result<T> = std::result::Result<T, CredentialClientError>;
#[derive(Error, Debug)]
pub enum CredentialClientError {
#[error("Nymd error: {0}")]
Nymd(#[from] NymdError),
#[error("Credential error: {0}")]
Credential(#[from] CredentialError),
#[error("No previous deposit with that tx hash")]
NoDeposit,
#[error("Wrong number of attributes")]
WrongAttributeNumber,
#[error("Could not find any backed up blind sign request data")]
NoLocalBlindSignRequest,
#[error("The local blind sign request data is corrupted")]
CorruptedBlindSignRequest,
#[error("The tx hash provided is not valid")]
InvalidTxHash,
#[error("Could not parse Ed25519 data")]
Ed25519ParseError(#[from] Ed25519RecoveryError),
#[error("Could not parse X25519 data")]
X25519ParseError(#[from] KeyRecoveryError),
}
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
cfg_if::cfg_if! {
if #[cfg(feature = "coconut")] {
mod client;
mod commands;
mod error;
mod state;
use commands::{Commands, Execute};
use error::Result;
use clap::Parser;
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
pub const MNEMONIC: &str = "sun surge soon stomach flavor country gorilla dress oblige stamp attract hip soldier agree steel prize nuclear know enjoy arm bargain always theme matter";
pub const NYMD_URL: &str = "http://127.0.0.1:26657";
pub const CONTRACT_ADDRESS: &str = "nymt1vhjnzk9ly03dugffvzfcwgry4dgc8x0sscmfl2";
pub const SIGNER_AUTHORITIES: [&str; 1] = [
"http://127.0.0.1:8080",
];
#[derive(Parser)]
#[clap(author = "Nymtech", version, about)]
struct Cli {
#[clap(subcommand)]
command: Commands,
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::parse();
let mut db = match PickleDb::load(
"credential.db",
PickleDbDumpPolicy::AutoDump,
SerializationMethod::Json,
) {
Ok(db) => db,
Err(_) => PickleDb::new(
"credential.db",
PickleDbDumpPolicy::AutoDump,
SerializationMethod::Json,
),
};
match &args.command {
Commands::Deposit(m) => m.execute(&mut db).await?,
Commands::ListDeposits(m) => m.execute(&mut db).await?,
Commands::GetCredential(m) => m.execute(&mut db).await?,
}
Ok(())
}
} else {
fn main() {
println!("Crate only designed for coconut feature");
}
}
}
+72
View File
@@ -0,0 +1,72 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use coconut_interface::{Attribute, BlindSignRequest, Bytable, PrivateAttribute};
use serde::{Deserialize, Serialize};
use crypto::asymmetric::{encryption, identity};
use crate::error::{CredentialClientError, Result};
#[derive(Clone, Debug, Deserialize, Serialize)]
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(),
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct State {
pub amount: u64,
pub tx_hash: String,
pub signing_keypair: KeyPair,
pub encryption_keypair: KeyPair,
pub blind_request_data: Option<RequestData>,
pub signature: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub(crate) struct RequestData {
pub serial_number: Vec<u8>,
pub binding_number: Vec<u8>,
pub first_attribute: Vec<u8>,
pub second_attribute: Vec<u8>,
pub blind_sign_req: Vec<u8>,
}
impl RequestData {
pub fn new(
private_attributes: Vec<PrivateAttribute>,
attributes: &[Attribute],
blind_sign_request: &BlindSignRequest,
) -> Result<Self> {
if private_attributes.len() != 2 || attributes.len() != 2 {
Err(CredentialClientError::WrongAttributeNumber)
} else {
Ok(RequestData {
serial_number: private_attributes[0].to_byte_vec(),
binding_number: private_attributes[1].to_byte_vec(),
first_attribute: attributes[0].to_byte_vec(),
second_attribute: attributes[1].to_byte_vec(),
blind_sign_req: blind_sign_request.to_bytes(),
})
}
}
}
+1 -1
View File
@@ -47,7 +47,7 @@ version-checker = { path = "../../common/version-checker" }
network-defaults = { path = "../../common/network-defaults" }
[features]
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
coconut = ["coconut-interface", "credentials", "credentials/coconut", "gateway-requests/coconut", "gateway-client/coconut"]
eth = []
[dev-dependencies]
+24 -11
View File
@@ -5,11 +5,12 @@ use clap::{App, Arg, ArgMatches};
use client_core::client::key_manager::KeyManager;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
#[cfg(feature = "coconut")]
use coconut_interface::{hash_to_scalar, Credential, Parameters};
use coconut_interface::{Credential, Parameters};
use config::NymConfig;
#[cfg(feature = "coconut")]
use credentials::coconut::bandwidth::{
obtain_signature, prepare_for_spending, BandwidthVoucherAttributes, TOTAL_ATTRIBUTES,
use credentials::coconut::{
bandwidth::prepare_for_spending, bandwidth::BandwidthVoucher, bandwidth::TOTAL_ATTRIBUTES,
utils::obtain_aggregate_signature,
};
#[cfg(feature = "coconut")]
use credentials::obtain_aggregate_verification_key;
@@ -17,7 +18,7 @@ use crypto::asymmetric::{encryption, identity};
use gateway_client::GatewayClient;
use gateway_requests::registration::handshake::SharedKeys;
#[cfg(feature = "coconut")]
use network_defaults::BANDWIDTH_VALUE;
use network_defaults::{BANDWIDTH_VALUE, VOUCHER_INFO};
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
use rand::rngs::OsRng;
@@ -28,6 +29,8 @@ use std::sync::Arc;
use std::time::Duration;
use topology::{filter::VersionFilterable, gateway};
use url::Url;
#[cfg(feature = "coconut")]
use validator_client::nymd::tx::Hash;
use crate::client::config::Config;
use crate::commands::override_config;
@@ -107,15 +110,25 @@ async fn _prepare_temporary_credential(validators: &[Url], raw_identity: &[u8])
.expect("could not obtain aggregate verification key of validators");
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let bandwidth_credential_attributes = BandwidthVoucherAttributes {
serial_number: params.random_scalar(),
binding_number: params.random_scalar(),
voucher_value: hash_to_scalar(BANDWIDTH_VALUE.to_be_bytes()),
voucher_info: hash_to_scalar(String::from("BandwidthVoucher").as_bytes()),
};
let mut rng = OsRng;
let bandwidth_credential_attributes = BandwidthVoucher::new(
&params,
BANDWIDTH_VALUE.to_string(),
VOUCHER_INFO.to_string(),
Hash::new([0; 32]),
// workaround for putting a valid value here, without deriving clone for the private
// key, until we have actual useful values
identity::PrivateKey::from_base58_string(
identity::KeyPair::new(&mut rng)
.private_key()
.to_base58_string(),
)
.unwrap(),
encryption::KeyPair::new(&mut rng).private_key().clone(),
);
let bandwidth_credential =
obtain_signature(&params, &bandwidth_credential_attributes, validators)
obtain_aggregate_signature(&params, &bandwidth_credential_attributes, validators)
.await
.expect("could not obtain bandwidth credential");
+1 -1
View File
@@ -42,7 +42,7 @@ version-checker = { path = "../../common/version-checker" }
network-defaults = { path = "../../common/network-defaults" }
[features]
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut"]
coconut = ["coconut-interface", "credentials", "gateway-requests/coconut", "gateway-client/coconut", "credentials/coconut"]
eth = []
[build-dependencies]
+23 -10
View File
@@ -5,11 +5,12 @@ use clap::{App, Arg, ArgMatches};
use client_core::client::key_manager::KeyManager;
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
#[cfg(feature = "coconut")]
use coconut_interface::{hash_to_scalar, Credential, Parameters};
use coconut_interface::{Credential, Parameters};
use config::NymConfig;
#[cfg(feature = "coconut")]
use credentials::coconut::bandwidth::{
obtain_signature, prepare_for_spending, BandwidthVoucherAttributes, TOTAL_ATTRIBUTES,
use credentials::coconut::{
bandwidth::prepare_for_spending, bandwidth::BandwidthVoucher, bandwidth::TOTAL_ATTRIBUTES,
utils::obtain_aggregate_signature,
};
#[cfg(feature = "coconut")]
use credentials::obtain_aggregate_verification_key;
@@ -26,6 +27,8 @@ use std::sync::Arc;
use std::time::Duration;
use topology::{filter::VersionFilterable, gateway};
use url::Url;
#[cfg(feature = "coconut")]
use validator_client::nymd::tx::Hash;
use crate::client::config::Config;
use crate::commands::override_config;
@@ -107,15 +110,25 @@ async fn _prepare_temporary_credential(validators: &[Url], raw_identity: &[u8])
.expect("could not obtain aggregate verification key of validators");
let params = Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let bandwidth_credential_attributes = BandwidthVoucherAttributes {
serial_number: params.random_scalar(),
binding_number: params.random_scalar(),
voucher_value: hash_to_scalar(BANDWIDTH_VALUE.to_be_bytes()),
voucher_info: hash_to_scalar("BandwidthVoucher"),
};
let mut rng = OsRng;
let bandwidth_credential_attributes = BandwidthVoucher::new(
&params,
BANDWIDTH_VALUE.to_string(),
network_defaults::VOUCHER_INFO.to_string(),
Hash::new([0; 32]),
// workaround for putting a valid value here, without deriving clone for the private
// key, until we have actual useful values
identity::PrivateKey::from_base58_string(
identity::KeyPair::new(&mut rng)
.private_key()
.to_base58_string(),
)
.unwrap(),
encryption::KeyPair::new(&mut rng).private_key().clone(),
);
let bandwidth_credential =
obtain_signature(&params, &bandwidth_credential_attributes, validators)
obtain_aggregate_signature(&params, &bandwidth_credential_attributes, validators)
.await
.expect("could not obtain bandwidth credential");
+3 -1
View File
@@ -19,6 +19,7 @@ secp256k1 = "0.20.3"
web3 = { version = "0.17.0", default-features = false }
# internal
cosmrs = { version = "0.4.1", optional = true }
credentials = { path = "../../credentials" }
crypto = { path = "../../crypto" }
gateway-requests = { path = "../../../gateway/gateway-requests" }
@@ -26,6 +27,7 @@ nymsphinx = { path = "../../nymsphinx" }
pemstore = { path = "../../pemstore" }
coconut-interface = { path = "../../coconut-interface", optional = true }
network-defaults = { path = "../../network-defaults" }
validator-client = { path = "../validator-client", optional = true }
[dependencies.tungstenite]
version = "0.13"
@@ -67,6 +69,6 @@ features = ["js"]
#url = "2.1"
[features]
coconut = ["gateway-requests/coconut", "coconut-interface"]
coconut = ["gateway-requests/coconut", "coconut-interface", "validator-client", "credentials/coconut", "cosmrs"]
wasm = ["web3/wasm", "web3/http", "web3/signing"]
default = ["web3/default"]
@@ -1,18 +1,18 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "coconut")]
use cosmrs::tx::Hash;
#[cfg(feature = "coconut")]
use credentials::coconut::{
bandwidth::{
obtain_signature, prepare_for_spending, BandwidthVoucherAttributes, TOTAL_ATTRIBUTES,
},
utils::obtain_aggregate_verification_key,
bandwidth::{prepare_for_spending, BandwidthVoucher, TOTAL_ATTRIBUTES},
utils::{obtain_aggregate_signature, obtain_aggregate_verification_key},
};
#[cfg(not(feature = "coconut"))]
use credentials::token::bandwidth::TokenCredential;
#[cfg(not(feature = "coconut"))]
#[cfg(feature = "coconut")]
use crypto::asymmetric::encryption;
use crypto::asymmetric::identity;
use crypto::asymmetric::identity::PublicKey;
use network_defaults::BANDWIDTH_VALUE;
#[cfg(not(feature = "coconut"))]
use network_defaults::{
@@ -22,7 +22,6 @@ use network_defaults::{
};
#[cfg(not(feature = "coconut"))]
use pemstore::traits::PemStorableKeyPair;
#[cfg(not(feature = "coconut"))]
use rand::rngs::OsRng;
#[cfg(not(feature = "coconut"))]
use secp256k1::SecretKey;
@@ -73,7 +72,7 @@ pub struct BandwidthController {
#[cfg(feature = "coconut")]
validator_endpoints: Vec<url::Url>,
#[cfg(feature = "coconut")]
identity: PublicKey,
identity: identity::PublicKey,
#[cfg(not(feature = "coconut"))]
contract: Contract<Http>,
#[cfg(not(feature = "coconut"))]
@@ -86,7 +85,7 @@ pub struct BandwidthController {
impl BandwidthController {
#[cfg(feature = "coconut")]
pub fn new(validator_endpoints: Vec<url::Url>, identity: PublicKey) -> Self {
pub fn new(validator_endpoints: Vec<url::Url>, identity: identity::PublicKey) -> Self {
BandwidthController {
validator_endpoints,
identity,
@@ -175,17 +174,25 @@ impl BandwidthController {
let verification_key = obtain_aggregate_verification_key(&self.validator_endpoints).await?;
let params = coconut_interface::Parameters::new(TOTAL_ATTRIBUTES).unwrap();
let mut rng = OsRng;
// TODO: Decide what is the value and additional info associated with the bandwidth voucher
let bandwidth_credential_attributes = BandwidthVoucherAttributes {
serial_number: params.random_scalar(),
binding_number: params.random_scalar(),
voucher_value: coconut_interface::hash_to_scalar(BANDWIDTH_VALUE.to_be_bytes()),
voucher_info: coconut_interface::hash_to_scalar(
String::from("BandwidthVoucher").as_bytes(),
),
};
let bandwidth_credential_attributes = BandwidthVoucher::new(
&params,
BANDWIDTH_VALUE.to_string(),
network_defaults::VOUCHER_INFO.to_string(),
Hash::new([0; 32]),
// workaround for putting a valid value here, without deriving clone for the private
// key, until we have actual useful values
identity::PrivateKey::from_base58_string(
identity::KeyPair::new(&mut rng)
.private_key()
.to_base58_string(),
)
.unwrap(),
encryption::KeyPair::new(&mut rng).private_key().clone(),
);
let bandwidth_credential = obtain_signature(
let bandwidth_credential = obtain_aggregate_signature(
&params,
&bandwidth_credential_attributes,
&self.validator_endpoints,
@@ -205,7 +212,7 @@ impl BandwidthController {
#[cfg(not(feature = "coconut"))]
pub async fn prepare_token_credential(
&self,
gateway_identity: PublicKey,
gateway_identity: identity::PublicKey,
gateway_owner: String,
) -> Result<TokenCredential, GatewayClientError> {
let kp = match self.restore_keypair() {
@@ -244,7 +251,7 @@ impl BandwidthController {
#[cfg(not(feature = "coconut"))]
pub async fn buy_token_credential(
&self,
verification_key: PublicKey,
verification_key: identity::PublicKey,
signed_verification_key: identity::Signature,
gateway_owner: String,
) -> Result<(), GatewayClientError> {
@@ -9,6 +9,7 @@ rust-version = "1.56"
[dependencies]
base64 = "0.13"
colored = "2.0"
mixnet-contract-common = { path= "../../cosmwasm-smart-contracts/mixnet-contract" }
vesting-contract-common = { path= "../../cosmwasm-smart-contracts/vesting-contract" }
vesting-contract = { path = "../../../contracts/vesting" }
@@ -18,6 +19,8 @@ reqwest = { version = "0.11", features = ["json"] }
thiserror = "1"
log = "0.4"
url = { version = "2.2", features = ["serde"] }
tokio = { version = "1.10", features = ["sync", "time"] }
futures = "0.3"
coconut-interface = { path = "../../coconut-interface" }
network-defaults = { path = "../../network-defaults" }
@@ -685,6 +685,16 @@ impl ApiClient {
Ok(self.validator_api.blind_sign(request_body).await?)
}
pub async fn partial_bandwidth_credential(
&self,
request_body: &str,
) -> Result<BlindedSignatureResponse, ValidatorClientError> {
Ok(self
.validator_api
.partial_bandwidth_credential(request_body)
.await?)
}
pub async fn get_coconut_verification_key(
&self,
) -> Result<VerificationKeyResponse, ValidatorClientError> {
@@ -0,0 +1,234 @@
use crate::nymd::error::NymdError;
use crate::nymd::{NymdClient, QueryNymdClient};
use crate::ApiClient;
use network_defaults::all::Network;
use colored::Colorize;
use core::fmt;
use itertools::Itertools;
use std::collections::HashMap;
use std::hash::BuildHasher;
use std::time::Duration;
use tokio::time::timeout;
use url::Url;
const MAX_URLS_TESTED: usize = 200;
const CONNECTION_TEST_TIMEOUT_SEC: u64 = 2;
// Run connection tests for all specified nymd and api urls. These are all run concurrently.
pub async fn run_validator_connection_test<H: BuildHasher + 'static>(
nymd_urls: impl Iterator<Item = (Network, Url)>,
api_urls: impl Iterator<Item = (Network, Url)>,
mixnet_contract_address: HashMap<Network, Option<cosmrs::AccountId>, H>,
) -> (
HashMap<Network, Vec<(Url, bool)>>,
HashMap<Network, Vec<(Url, bool)>>,
) {
// Setup all the clients for the connection tests
let connection_test_clients =
setup_connection_tests(nymd_urls, api_urls, mixnet_contract_address);
// Run all tests concurrently
let connection_results = futures::future::join_all(
connection_test_clients
.into_iter()
.take(MAX_URLS_TESTED)
.map(ClientForConnectionTest::run_connection_check),
)
.await;
// Seperate and collect results into HashMaps
(
extract_and_collect_results_into_map(&connection_results, &UrlType::Nymd),
extract_and_collect_results_into_map(&connection_results, &UrlType::Api),
)
}
fn setup_connection_tests<H: BuildHasher + 'static>(
nymd_urls: impl Iterator<Item = (Network, Url)>,
api_urls: impl Iterator<Item = (Network, Url)>,
mixnet_contract_address: HashMap<Network, Option<cosmrs::AccountId>, H>,
) -> impl Iterator<Item = ClientForConnectionTest> {
let nymd_connection_test_clients = nymd_urls.filter_map(move |(network, url)| {
let address = mixnet_contract_address
.get(&network)
.expect("No configured contract address")
.clone();
NymdClient::<QueryNymdClient>::connect(url.as_str(), address, None, None)
.map(move |client| ClientForConnectionTest::Nymd(network, url, Box::new(client)))
.ok()
});
let api_connection_test_clients = api_urls.map(|(network, url)| {
ClientForConnectionTest::Api(network, url.clone(), ApiClient::new(url))
});
nymd_connection_test_clients.chain(api_connection_test_clients)
}
fn extract_and_collect_results_into_map(
connection_results: &[ConnectionResult],
url_type: &UrlType,
) -> HashMap<Network, Vec<(Url, bool)>> {
connection_results
.iter()
.filter(|c| &c.url_type() == url_type)
.map(|c| {
let (network, url, result) = c.result();
(*network, (url.clone(), *result))
})
.into_group_map()
}
async fn test_nymd_connection(
network: Network,
url: &Url,
client: &NymdClient<QueryNymdClient>,
) -> ConnectionResult {
let result = match timeout(
Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC),
client.get_mixnet_contract_version(),
)
.await
{
Ok(Err(NymdError::TendermintError(e))) => {
// If we get a tendermint-rpc error, we classify the node as not contactable
log::debug!("Checking: nymd_url: {network}: {url}: failed: {}", e);
false
}
Ok(Err(NymdError::AbciError(code, log))) => {
// We accept the mixnet contract not found as ok from a connection standpoint. This happens
// for example on a pre-launch network.
log::debug!(
"Checking: nymd_url: {network}: {url}: {}, but with abci error: {code}: {log}",
"success".green()
);
code == 18
}
Ok(Err(error @ NymdError::NoContractAddressAvailable)) => {
log::debug!(
"Checking: nymd_url: {network}: {url}: {}: {error}",
"failed".red()
);
false
}
Ok(Err(e)) => {
// For any other error, we're optimistic and just try anyway.
log::debug!(
"Checking: nymd_url: {network}: {url}: {}, but with error: {e}",
"success".green()
);
true
}
Ok(Ok(_)) => {
log::debug!(
"Checking: nymd_url: {network}: {url}: {}",
"success".green()
);
true
}
Err(e) => {
log::debug!(
"Checking: nymd_url: {network}: {url}: {}: {e}",
"failed".red()
);
false
}
};
ConnectionResult::Nymd(network, url.clone(), result)
}
async fn test_api_connection(network: Network, url: &Url, client: &ApiClient) -> ConnectionResult {
let result = match timeout(
Duration::from_secs(CONNECTION_TEST_TIMEOUT_SEC),
client.get_cached_mixnodes(),
)
.await
{
Ok(Ok(_)) => {
log::debug!("Checking: api_url: {network}: {url}: {}", "success".green());
true
}
Ok(Err(e)) => {
log::debug!(
"Checking: api_url: {network}: {url}: {}: {e}",
"failed".red()
);
false
}
Err(e) => {
log::debug!(
"Checking: api_url: {network}: {url}: {}: {e}",
"failed".red()
);
false
}
};
ConnectionResult::Api(network, url.clone(), result)
}
enum ClientForConnectionTest {
Nymd(Network, Url, Box<NymdClient<QueryNymdClient>>),
Api(Network, Url, ApiClient),
}
impl ClientForConnectionTest {
async fn run_connection_check(self) -> ConnectionResult {
match self {
ClientForConnectionTest::Nymd(network, ref url, ref client) => {
test_nymd_connection(network, url, client).await
}
ClientForConnectionTest::Api(network, ref url, ref client) => {
test_api_connection(network, url, client).await
}
}
}
}
#[derive(Debug, PartialEq, Eq)]
enum UrlType {
Nymd,
Api,
}
impl fmt::Display for UrlType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UrlType::Nymd => write!(f, "nymd"),
UrlType::Api => write!(f, "api"),
}
}
}
#[derive(Debug)]
enum ConnectionResult {
Nymd(Network, Url, bool),
Api(Network, Url, bool),
}
impl ConnectionResult {
fn result(&self) -> (&Network, &Url, &bool) {
match self {
ConnectionResult::Nymd(network, url, result)
| ConnectionResult::Api(network, url, result) => (network, url, result),
}
}
fn url_type(&self) -> UrlType {
match self {
ConnectionResult::Nymd(..) => UrlType::Nymd,
ConnectionResult::Api(..) => UrlType::Api,
}
}
}
impl fmt::Display for ConnectionResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (network, url, result) = self.result();
let url_type = self.url_type();
write!(
f,
"{network}: {url}: {url_type}: connection is successful: {result}"
)
}
}
@@ -2,6 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
pub mod client;
#[cfg(feature = "nymd-client")]
pub mod connection_tester;
mod error;
#[cfg(feature = "nymd-client")]
pub mod nymd;
@@ -29,9 +29,12 @@ pub use crate::nymd::cosmwasm_client::client::CosmWasmClient;
pub use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
pub use crate::nymd::fee::Fee;
use crate::nymd::fee::DEFAULT_SIMULATED_GAS_MULTIPLIER;
pub use cosmrs::rpc::endpoint::tx::Response as TxResponse;
pub use cosmrs::rpc::endpoint::validators::Response as ValidatorResponse;
pub use cosmrs::rpc::HttpClient as QueryNymdClient;
pub use cosmrs::rpc::Paging;
pub use cosmrs::tendermint::abci::responses::{DeliverTx, Event};
pub use cosmrs::tendermint::abci::tag::Tag;
pub use cosmrs::tendermint::block::Height;
pub use cosmrs::tendermint::hash;
pub use cosmrs::tendermint::validator::Info as TendermintValidatorInfo;
@@ -274,6 +277,13 @@ impl<C> NymdClient<C> {
self.client.get_balance(address, denom).await
}
pub async fn get_tx(&self, id: tx::Hash) -> Result<TxResponse, NymdError>
where
C: CosmWasmClient + Sync,
{
self.client.get_tx(id).await
}
pub async fn get_total_supply(&self) -> Result<Vec<Coin>, NymdError>
where
C: CosmWasmClient + Sync,
@@ -7,4 +7,7 @@ pub enum ValidatorAPIError {
#[from]
source: reqwest::Error,
},
#[error("Request failed with error message - {0}")]
GenericRequestFailure(String),
}
@@ -14,7 +14,7 @@ use validator_api_requests::models::{
};
pub mod error;
pub(crate) mod routes;
pub mod routes;
type PathSegments<'a> = &'a [&'a str];
type Params<'a, K, V> = &'a [(K, V)];
@@ -66,14 +66,14 @@ impl Client {
V: AsRef<str>,
{
let url = create_api_url(&self.url, path, params);
Ok(self
.reqwest_client
.post(url)
.json(json_body)
.send()
.await?
.json()
.await?)
let response = self.reqwest_client.post(url).json(json_body).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
Err(ValidatorAPIError::GenericRequestFailure(
response.text().await?,
))
}
}
pub async fn get_mixnodes(&self) -> Result<Vec<MixNodeBond>, ValidatorAPIError> {
@@ -254,7 +254,29 @@ impl Client {
request_body: &BlindSignRequestBody,
) -> Result<BlindedSignatureResponse, ValidatorAPIError> {
self.post_validator_api(
&[routes::API_VERSION, routes::COCONUT_BLIND_SIGN],
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_BLIND_SIGN,
],
NO_PARAMS,
request_body,
)
.await
}
pub async fn partial_bandwidth_credential(
&self,
request_body: &str,
) -> Result<BlindedSignatureResponse, ValidatorAPIError> {
self.post_validator_api(
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL,
],
NO_PARAMS,
request_body,
)
@@ -265,7 +287,12 @@ impl Client {
&self,
) -> Result<VerificationKeyResponse, ValidatorAPIError> {
self.query_validator_api(
&[routes::API_VERSION, routes::COCONUT_VERIFICATION_KEY],
&[
routes::API_VERSION,
routes::COCONUT_ROUTES,
routes::BANDWIDTH,
routes::COCONUT_VERIFICATION_KEY,
],
NO_PARAMS,
)
.await
@@ -10,7 +10,11 @@ pub const GATEWAYS: &str = "gateways";
pub const ACTIVE: &str = "active";
pub const REWARDED: &str = "rewarded";
pub const COCONUT_ROUTES: &str = "coconut";
pub const BANDWIDTH: &str = "bandwidth";
pub const COCONUT_BLIND_SIGN: &str = "blind-sign";
pub const COCONUT_PARTIAL_BANDWIDTH_CREDENTIAL: &str = "partial-bandwidth-credential";
pub const COCONUT_VERIFICATION_KEY: &str = "verification-key";
pub const STATUS_ROUTES: &str = "status";
+3 -1
View File
@@ -5,7 +5,9 @@ edition = "2021"
description = "Crutch library until there is proper SerDe support for coconut structs"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
bs58 = "0.4.0"
getset = "0.1.1"
serde = { version = "1.0", features = ["derive"] }
thiserror = "1"
nymcoconut = {path = "../nymcoconut" }
+4 -16
View File
@@ -5,21 +5,9 @@ use thiserror::Error;
#[derive(Debug, Error)]
pub enum CoconutInterfaceError {
#[error("could not parse validator URL: {source}")]
UrlParsingError {
#[from]
source: url::ParseError,
},
#[error("not enough bytes: {0} received, minimum {1} required")]
InvalidByteLength(usize, usize),
#[error("could not aggregate verification key: {0}")]
AggregateVerificationKeyError(coconut_rs::CoconutError),
#[error("could not prove credential: {0}")]
ProveCredentialError(coconut_rs::CoconutError),
#[error("got invalid signature index: {0}")]
InvalidSignatureIdx(usize),
#[error("got too many total attributes(public + private): {0} received, {1} is the maximum")]
TooManyTotalAttributes(usize, u32),
#[error("Could not decode base 58 string - {0}")]
MalformedString(#[from] bs58::decode::Error),
}
+53 -5
View File
@@ -1,9 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod error;
use getset::{CopyGetters, Getters};
use serde::{Deserialize, Serialize};
use error::CoconutInterfaceError;
pub use nymcoconut::*;
#[derive(Serialize, Deserialize, Getters, CopyGetters, Clone)]
@@ -79,27 +83,39 @@ impl VerifyCredentialBody {
}
}
// All strings are base58 encoded representations of structs
#[derive(Serialize, Deserialize, Debug, Getters, CopyGetters)]
#[derive(Clone, Serialize, Deserialize, Debug, Getters, CopyGetters)]
pub struct BlindSignRequestBody {
#[getset(get = "pub")]
blind_sign_request: BlindSignRequest,
#[getset(get = "pub")]
tx_hash: String,
#[getset(get = "pub")]
signature: String,
public_attributes: Vec<String>,
#[getset(get = "pub")]
public_attributes_plain: Vec<String>,
#[getset(get = "pub")]
total_params: u32,
}
impl BlindSignRequestBody {
pub fn new(
blind_sign_request: &BlindSignRequest,
tx_hash: String,
signature: String,
public_attributes: &[Attribute],
public_attributes_plain: Vec<String>,
total_params: u32,
) -> BlindSignRequestBody {
BlindSignRequestBody {
blind_sign_request: blind_sign_request.clone(),
tx_hash,
signature,
public_attributes: public_attributes
.iter()
.map(|attr| attr.to_bs58())
.collect(),
public_attributes_plain,
total_params,
}
}
@@ -112,14 +128,46 @@ impl BlindSignRequestBody {
}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct BlindedSignatureResponse {
pub blinded_signature: BlindedSignature,
pub remote_key: [u8; 32],
pub encrypted_signature: Vec<u8>,
}
impl BlindedSignatureResponse {
pub fn new(blinded_signature: BlindedSignature) -> BlindedSignatureResponse {
BlindedSignatureResponse { blinded_signature }
pub fn new(encrypted_signature: Vec<u8>, remote_key: [u8; 32]) -> BlindedSignatureResponse {
BlindedSignatureResponse {
encrypted_signature,
remote_key,
}
}
pub fn to_base58_string(&self) -> String {
bs58::encode(&self.to_bytes()).into_string()
}
pub fn from_base58_string<I: AsRef<[u8]>>(val: I) -> Result<Self, CoconutInterfaceError> {
let bytes = bs58::decode(val).into_vec()?;
Self::from_bytes(&bytes)
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = self.remote_key.to_vec();
bytes.extend_from_slice(&self.encrypted_signature);
bytes
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CoconutInterfaceError> {
if bytes.len() < 32 {
return Err(CoconutInterfaceError::InvalidByteLength(bytes.len(), 32));
}
let mut remote_key = [0u8; 32];
remote_key.copy_from_slice(&bytes[..32]);
let encrypted_signature = bytes[32..].to_vec();
Ok(BlindedSignatureResponse {
remote_key,
encrypted_signature,
})
}
}
@@ -0,0 +1,10 @@
[package]
name = "coconut-bandwidth-contract-common"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
@@ -0,0 +1,34 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct DepositData {
deposit_info: String,
identity_key: String,
encryption_key: String,
}
impl DepositData {
pub fn new(deposit_info: String, identity_key: String, encryption_key: String) -> Self {
DepositData {
deposit_info,
identity_key,
encryption_key,
}
}
pub fn deposit_info(&self) -> &str {
&self.deposit_info
}
pub fn identity_key(&self) -> &str {
&self.identity_key
}
pub fn encryption_key(&self) -> &str {
&self.encryption_key
}
}
@@ -0,0 +1,11 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
// event types
pub const DEPOSITED_FUNDS_EVENT_TYPE: &str = "deposited-funds";
// attributes that are used in multiple places
pub const DEPOSIT_VALUE: &str = "deposit-value";
pub const DEPOSIT_INFO: &str = "deposit-info";
pub const DEPOSIT_IDENTITY_KEY: &str = "deposit-identity-key";
pub const DEPOSIT_ENCRYPTION_KEY: &str = "deposit-encryption-key";
@@ -0,0 +1,3 @@
pub mod deposit;
pub mod events;
pub mod msg;
@@ -0,0 +1,24 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::deposit::DepositData;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
DepositFunds { data: DepositData },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct MigrateMsg {}
+8 -1
View File
@@ -7,11 +7,18 @@ edition = "2021"
[dependencies]
bls12_381 = { version = "0.5", default-features = false, features = ["pairings", "alloc", "experimental"] }
cosmrs = { version = "0.4.1", optional = true }
thiserror = "1.0"
url = "2.2"
# I guess temporarily until we get serde support in coconut up and running
coconut-interface = { path = "../coconut-interface" }
crypto = { path = "../crypto", features = ["asymmetric"] }
crypto = { path = "../crypto", features = ["rand", "asymmetric", "symmetric", "hashing"] }
network-defaults = { path = "../network-defaults" }
validator-client = { path = "../client-libs/validator-client" }
[dev-dependencies]
rand = "0.7.3"
[features]
coconut = ["cosmrs"]
+192 -22
View File
@@ -7,56 +7,167 @@
// it's the simplest possible case
use coconut_interface::{
Credential, Parameters, PrivateAttribute, PublicAttribute, Signature, VerificationKey,
hash_to_scalar, prepare_blind_sign, Attribute, BlindSignRequest, Credential, Parameters,
PrivateAttribute, PublicAttribute, Signature, VerificationKey,
};
use crypto::asymmetric::{encryption, identity};
use network_defaults::BANDWIDTH_VALUE;
use url::Url;
use cosmrs::tx::Hash;
use super::utils::prepare_credential_for_spending;
use crate::error::Error;
use super::utils::{obtain_aggregate_signature, prepare_credential_for_spending};
pub const PUBLIC_ATTRIBUTES: u32 = 2;
pub const PRIVATE_ATTRIBUTES: u32 = 2;
pub const TOTAL_ATTRIBUTES: u32 = PUBLIC_ATTRIBUTES + PRIVATE_ATTRIBUTES;
pub struct BandwidthVoucherAttributes {
pub struct BandwidthVoucher {
// a random secret value generated by the client used for double-spending detection
pub serial_number: PrivateAttribute,
serial_number: PrivateAttribute,
// a random secret value generated by the client used to bind multiple credentials together
pub binding_number: PrivateAttribute,
binding_number: PrivateAttribute,
// the value (e.g., bandwidth) encoded in this voucher
pub voucher_value: PublicAttribute,
voucher_value: PublicAttribute,
// the plain text value (e.g., bandwidth) encoded in this voucher
voucher_value_plain: String,
// a field with public information, e.g., type of voucher, interval etc.
pub voucher_info: PublicAttribute,
voucher_info: PublicAttribute,
// the plain text information
voucher_info_plain: String,
// the hash of the deposit transaction
tx_hash: Hash,
// 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,
pedersen_commitments_openings: Vec<Attribute>,
blind_sign_request: BlindSignRequest,
use_request: bool,
}
impl BandwidthVoucherAttributes {
impl BandwidthVoucher {
pub fn new_with_blind_sign_req(
private_attributes: [PrivateAttribute; PRIVATE_ATTRIBUTES as usize],
public_attributes_plain: [&str; PUBLIC_ATTRIBUTES as usize],
tx_hash: Hash,
signing_key: identity::PrivateKey,
encryption_key: encryption::PrivateKey,
pedersen_commitments_openings: Vec<Attribute>,
blind_sign_request: BlindSignRequest,
) -> Self {
let voucher_value = public_attributes_plain[0];
let voucher_info = public_attributes_plain[1];
let voucher_value_plain = voucher_value.to_string();
let voucher_info_plain = voucher_info.to_string();
let voucher_value = hash_to_scalar(voucher_value.as_bytes());
let voucher_info = hash_to_scalar(voucher_info.as_bytes());
BandwidthVoucher {
serial_number: private_attributes[0],
binding_number: private_attributes[1],
voucher_value,
voucher_value_plain,
voucher_info,
voucher_info_plain,
tx_hash,
signing_key,
encryption_key,
pedersen_commitments_openings,
blind_sign_request,
use_request: false,
}
}
pub fn new(
params: &Parameters,
voucher_value: String,
voucher_info: String,
tx_hash: Hash,
signing_key: identity::PrivateKey,
encryption_key: encryption::PrivateKey,
) -> Self {
let serial_number = params.random_scalar();
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 (pedersen_commitments_openings, blind_sign_request) = prepare_blind_sign(
params,
&[serial_number, binding_number],
&[voucher_value, voucher_info],
)
.unwrap();
BandwidthVoucher {
serial_number,
binding_number,
voucher_value,
voucher_value_plain,
voucher_info,
voucher_info_plain,
tx_hash,
signing_key,
encryption_key,
pedersen_commitments_openings,
blind_sign_request,
use_request: true,
}
}
/// Check if the plain values correspond to the PublicAttributes
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])
}
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 encryption_key(&self) -> &encryption::PrivateKey {
&self.encryption_key
}
pub fn pedersen_commitments_openings(&self) -> &Vec<Attribute> {
&self.pedersen_commitments_openings
}
pub fn blind_sign_request(&self) -> &BlindSignRequest {
&self.blind_sign_request
}
pub fn use_request(&self) -> bool {
self.use_request
}
pub fn get_public_attributes_plain(&self) -> Vec<String> {
vec![
self.voucher_value_plain.clone(),
self.voucher_info_plain.clone(),
]
}
pub fn get_private_attributes(&self) -> Vec<PrivateAttribute> {
vec![self.serial_number, self.binding_number]
}
}
// TODO: this definitely has to be moved somewhere else. It's just a temporary solution
pub async fn obtain_signature(
params: &Parameters,
attributes: &BandwidthVoucherAttributes,
validators: &[Url],
) -> Result<Signature, Error> {
let public_attributes = attributes.get_public_attributes();
let private_attributes = attributes.get_private_attributes();
obtain_aggregate_signature(params, &public_attributes, &private_attributes, validators).await
pub fn sign(&self, request: &BlindSignRequest) -> identity::Signature {
let mut message = request.to_bytes();
message.extend_from_slice(self.tx_hash.as_bytes());
self.signing_key.sign(&message)
}
}
pub fn prepare_for_spending(
raw_identity: &[u8],
signature: &Signature,
attributes: &BandwidthVoucherAttributes,
attributes: &BandwidthVoucher,
verification_key: &VerificationKey,
) -> Result<Credential, Error> {
let public_attributes = vec![
@@ -75,3 +186,62 @@ pub fn prepare_for_spending(
verification_key,
)
}
#[cfg(test)]
mod test {
use super::*;
use rand::rngs::OsRng;
#[test]
fn voucher_consistency() {
let params = Parameters::new(4).unwrap();
let mut rng = OsRng;
let voucher = BandwidthVoucher::new(
&params,
"1234".to_string(),
"voucher info".to_string(),
Hash::new([0; 32]),
identity::PrivateKey::from_base58_string(
identity::KeyPair::new(&mut rng)
.private_key()
.to_base58_string(),
)
.unwrap(),
encryption::KeyPair::new(&mut rng).private_key().clone(),
);
assert!(!BandwidthVoucher::verify_against_plain(
&[],
&voucher.get_public_attributes_plain()
));
assert!(!BandwidthVoucher::verify_against_plain(
&voucher.get_public_attributes(),
&vec![],
));
assert!(!BandwidthVoucher::verify_against_plain(
&voucher.get_public_attributes(),
&[
voucher.get_public_attributes_plain()[0].clone(),
String::new()
]
));
assert!(!BandwidthVoucher::verify_against_plain(
&voucher.get_public_attributes(),
&[
String::new(),
voucher.get_public_attributes_plain()[1].clone()
]
));
assert!(!BandwidthVoucher::verify_against_plain(
&[voucher.get_public_attributes()[0], Attribute::one()],
&voucher.get_public_attributes_plain()
));
assert!(!BandwidthVoucher::verify_against_plain(
&[Attribute::one(), voucher.get_public_attributes()[1]],
&voucher.get_public_attributes_plain()
));
assert!(BandwidthVoucher::verify_against_plain(
&voucher.get_public_attributes(),
&voucher.get_public_attributes_plain()
));
}
}
+1
View File
@@ -2,4 +2,5 @@
// SPDX-License-Identifier: Apache-2.0
pub mod bandwidth;
pub mod params;
pub mod utils;
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crypto::aes::Aes128;
use crypto::blake3;
use crypto::ctr;
type Aes128Ctr = ctr::Ctr64LE<Aes128>;
/// Hashing algorithm used during hkdf for ephemeral shared key generation per blinded signature
/// response encryption.
pub type ValidatorApiCredentialHkdfAlgorithm = blake3::Hasher;
/// Encryption algorithm used for end-to-end encryption of blinded signature response
pub type ValidatorApiCredentialEncryptionAlgorithm = Aes128Ctr;
+57 -47
View File
@@ -1,15 +1,20 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use bls12_381::Scalar;
use coconut_interface::{
aggregate_signature_shares, aggregate_verification_keys, prepare_blind_sign,
prove_bandwidth_credential, Attribute, BlindSignRequest, BlindSignRequestBody, Credential,
Parameters, Signature, SignatureShare, VerificationKey,
aggregate_signature_shares, aggregate_verification_keys, prove_bandwidth_credential, Attribute,
BlindSignRequestBody, BlindedSignature, Credential, Parameters, Signature, SignatureShare,
VerificationKey,
};
use crypto::asymmetric::encryption::PublicKey;
use crypto::shared_key::recompute_shared_key;
use crypto::symmetric::stream_cipher;
use url::Url;
use crate::coconut::bandwidth::PRIVATE_ATTRIBUTES;
use crate::coconut::bandwidth::{BandwidthVoucher, PRIVATE_ATTRIBUTES};
use crate::coconut::params::{
ValidatorApiCredentialEncryptionAlgorithm, ValidatorApiCredentialHkdfAlgorithm,
};
use crate::error::Error;
/// Contacts all provided validators and then aggregate their verification keys.
@@ -63,31 +68,53 @@ pub async fn obtain_aggregate_verification_key(
async fn obtain_partial_credential(
params: &Parameters,
public_attributes: &[Attribute],
private_attributes: &[Attribute],
pedersen_commitments_openings: &[Scalar],
blind_sign_request: &BlindSignRequest,
attributes: &BandwidthVoucher,
client: &validator_client::ApiClient,
validator_vk: &VerificationKey,
) -> Result<Signature, Error> {
let blind_sign_request_body = BlindSignRequestBody::new(
blind_sign_request,
public_attributes,
(public_attributes.len() + private_attributes.len()) as u32,
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 response = if attributes.use_request() {
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,
public_attributes_plain,
(public_attributes.len() + private_attributes.len()) as u32,
);
client.blind_sign(&blind_sign_request_body).await?
} else {
client
.partial_bandwidth_credential(&attributes.tx_hash().to_string())
.await?
};
let encrypted_signature = response.encrypted_signature;
let remote_key = PublicKey::from_bytes(&response.remote_key)?;
let encryption_key = recompute_shared_key::<
ValidatorApiCredentialEncryptionAlgorithm,
ValidatorApiCredentialHkdfAlgorithm,
>(&remote_key, attributes.encryption_key());
let zero_iv = stream_cipher::zero_iv::<ValidatorApiCredentialEncryptionAlgorithm>();
let blinded_signature_bytes = stream_cipher::decrypt::<ValidatorApiCredentialEncryptionAlgorithm>(
&encryption_key,
&zero_iv,
&encrypted_signature,
);
let blinded_signature = client
.blind_sign(&blind_sign_request_body)
.await?
.blinded_signature;
let blinded_signature = BlindedSignature::from_bytes(&blinded_signature_bytes)?;
let unblinded_signature = blinded_signature.unblind(
params,
validator_vk,
private_attributes,
public_attributes,
&private_attributes,
&public_attributes,
&blind_sign_request.get_commitment_hash(),
&*pedersen_commitments_openings,
attributes.pedersen_commitments_openings(),
)?;
Ok(unblinded_signature)
@@ -95,13 +122,14 @@ async fn obtain_partial_credential(
pub async fn obtain_aggregate_signature(
params: &Parameters,
public_attributes: &[Attribute],
private_attributes: &[Attribute],
attributes: &BandwidthVoucher,
validators: &[Url],
) -> Result<Signature, Error> {
if validators.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(validators.len());
let mut validators_partial_vks: Vec<VerificationKey> = Vec::with_capacity(validators.len());
@@ -110,42 +138,24 @@ pub async fn obtain_aggregate_signature(
let validator_partial_vk = client.get_coconut_verification_key().await?;
validators_partial_vks.push(validator_partial_vk.key.clone());
let (pedersen_commitments_openings, blind_sign_request) =
prepare_blind_sign(params, private_attributes, public_attributes)?;
let first = obtain_partial_credential(
params,
public_attributes,
private_attributes,
&pedersen_commitments_openings,
&blind_sign_request,
&client,
&validator_partial_vk.key,
)
.await?;
let first =
obtain_partial_credential(params, attributes, &client, &validator_partial_vk.key).await?;
shares.push(SignatureShare::new(first, 1));
for (id, validator_url) in validators.iter().enumerate().skip(1) {
client.change_validator_api(validator_url.clone());
let validator_partial_vk = client.get_coconut_verification_key().await?;
validators_partial_vks.push(validator_partial_vk.key.clone());
let signature = obtain_partial_credential(
params,
public_attributes,
private_attributes,
&pedersen_commitments_openings,
&blind_sign_request,
&client,
&validator_partial_vk.key,
)
.await?;
let signature =
obtain_partial_credential(params, attributes, &client, &validator_partial_vk.key)
.await?;
let share = SignatureShare::new(signature, (id + 1) as u64);
shares.push(share)
}
let mut attributes = Vec::with_capacity(private_attributes.len() + public_attributes.len());
attributes.extend_from_slice(private_attributes);
attributes.extend_from_slice(public_attributes);
attributes.extend_from_slice(&private_attributes);
attributes.extend_from_slice(&public_attributes);
let mut indices: Vec<u64> = Vec::with_capacity(validators_partial_vks.len());
for i in 0..validators_partial_vks.len() {
+8 -1
View File
@@ -1,10 +1,13 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "coconut")]
use coconut_interface::CoconutError;
use thiserror::Error;
use crypto::asymmetric::encryption::KeyRecoveryError;
use validator_client::ValidatorClientError;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("The detailed description is yet to be determined")]
@@ -13,6 +16,7 @@ pub enum Error {
#[error("Could not contact any validator")]
NoValidatorsAvailable,
#[cfg(feature = "coconut")]
#[error("Run into a coconut error - {0}")]
CoconutError(#[from] CoconutError),
@@ -30,4 +34,7 @@ pub enum Error {
#[error("There is not associated bandwidth for the given client")]
MissingBandwidth,
#[error("Could not parse the key - {0}")]
ParsePublicKey(#[from] KeyRecoveryError),
}
+3
View File
@@ -1,8 +1,11 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(feature = "coconut")]
pub mod coconut;
pub mod error;
#[cfg(not(feature = "coconut"))]
pub mod token;
#[cfg(feature = "coconut")]
pub use coconut::utils::{obtain_aggregate_signature, obtain_aggregate_verification_key};
@@ -6,7 +6,6 @@ use crypto::asymmetric::identity::{PublicKey, Signature, PUBLIC_KEY_LENGTH, SIGN
use crate::error::Error;
use std::convert::TryInto;
#[cfg(not(feature = "coconut"))]
pub struct TokenCredential {
verification_key: PublicKey,
gateway_identity: PublicKey,
@@ -14,7 +13,6 @@ pub struct TokenCredential {
signature: Signature,
}
#[cfg(not(feature = "coconut"))]
impl TokenCredential {
pub fn new(
verification_key: PublicKey,
@@ -99,7 +97,6 @@ impl TokenCredential {
mod tests {
use super::*;
#[cfg(not(feature = "coconut"))]
#[test]
fn token_serde() {
// pre-generated, valid values
+2
View File
@@ -147,6 +147,8 @@ pub const UTOKENS_TO_BURN: u64 = TOKENS_TO_BURN * 1000000;
/// Default bandwidth (in bytes) that we try to buy
pub const BANDWIDTH_VALUE: u64 = TOKENS_TO_BURN * BYTES_PER_TOKEN;
pub const VOUCHER_INFO: &str = "BandwidthVoucher";
pub const ETH_MIN_BLOCK_DEPTH: usize = 7;
/// Defaults Cosmos Hub/ATOM path
+1 -2
View File
@@ -5,6 +5,7 @@ use std::convert::TryInto;
use bls12_381::Scalar;
pub use crate::traits::Bytable;
pub use elgamal::elgamal_keygen;
pub use elgamal::ElGamalKeyPair;
pub use elgamal::PublicKey;
@@ -28,8 +29,6 @@ pub use scheme::SignatureShare;
pub use traits::Base58;
pub use utils::hash_to_scalar;
use crate::traits::Bytable;
pub mod elgamal;
mod error;
mod impls;
+22
View File
@@ -191,6 +191,28 @@ dependencies = [
"generic-array 0.14.5",
]
[[package]]
name = "coconut-bandwidth"
version = "0.1.0"
dependencies = [
"bandwidth-claim-contract",
"coconut-bandwidth-contract-common",
"config",
"cosmwasm-std",
"cosmwasm-storage",
"schemars",
"serde",
"thiserror",
]
[[package]]
name = "coconut-bandwidth-contract-common"
version = "0.1.0"
dependencies = [
"schemars",
"serde",
]
[[package]]
name = "config"
version = "0.1.0"
+1 -1
View File
@@ -1,5 +1,5 @@
[workspace]
members = ["bandwidth-claim", "mixnet", "vesting"]
members = ["bandwidth-claim", "coconut-bandwidth", "mixnet", "vesting"]
[profile.release]
opt-level = 3
@@ -1,5 +1,5 @@
node_modules
../.env
.env
.to_do.md
#Hardhat files
+15 -3
View File
@@ -2,10 +2,8 @@
This directory contains the contract and unit tests for the `BandwidthGenerator` smart contract.
This contract allows users to generate Basic Bandwidth Credentials (BBCs) on the Nym cosmos blockchain using ERC20 representations of NYM as payment, utilising the Cosmos Gravity Bridge for cross-chain payment.
This contract allows users to generate Basic Bandwidth Credentials (BBCs) on the Nyx blockchain using ERC20 NYM as payment, utilising the Gravity Bridge for cross-chain payment.
BBCs are credentials that will be presented to Gateways by a Nym Client, and represent a certain amount of bandwidth which can be sent through the Nym Mixnet.
By default 1 NYM = 1 GB of bandwidth. The ratio of NYM - bandwidth is denominated in bytes, and represented in the smart contract by the `BytesPerToken` variable. This variable can be adjusted by the contract owner.
The amount of bandwidth bought is calculated according to the following formula:
@@ -15,3 +13,17 @@ The amount of bandwidth bought is calculated according to the following formula:
* `npm install`
* `npx hardhat compile`
* `npx hardhat test`
If you are planning to interact with contracts on either Rinkeby or mainnet, or deploy new contract instances, create an `.env` file with the following:
```
RINKEBY_URL=https://rinkeby.infura.io/v3/<INFURA_KEY>
MAINNET_URL=https://mainnet.infura.io/v3/<INFURA_KEY>
PRIV_KEY=<YOUR_PRIVATE_KEY>
ETHERSCAN_API_KEY=<ETHERSCAN_API_KEY>
```
## Deployed addresses
Find deployed instances of the smart contract on the Rinkeby testnet and the Ethereum mainnet in `contractAddresses.json`.
This json file is automatically updated when any of the scripts in `scripts/mainnet` or `scripts/rinkeby` are run.
@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../../build-info/1f95016aa87f8376998977fedfcae017.json"
"buildInfo": "../../../../../build-info/42a2c6b57bbdc8d38c975cb15800dd3b.json"
}
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../../build-info/1f95016aa87f8376998977fedfcae017.json"
"buildInfo": "../../../../../build-info/42a2c6b57bbdc8d38c975cb15800dd3b.json"
}
@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../../../build-info/1f95016aa87f8376998977fedfcae017.json"
"buildInfo": "../../../../../../build-info/42a2c6b57bbdc8d38c975cb15800dd3b.json"
}
@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/1f95016aa87f8376998977fedfcae017.json"
"buildInfo": "../../../../build-info/42a2c6b57bbdc8d38c975cb15800dd3b.json"
}
@@ -1,9 +1,9 @@
{
"_format": "hh-sol-cache-2",
"files": {
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/BandwidthGenerator.sol": {
"lastModificationDate": 1643129770695,
"contentHash": "642b72e2d50d565db7bc994cace3f5a0",
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/contracts/BandwidthGenerator.sol": {
"lastModificationDate": 1648818985185,
"contentHash": "cc45839db61ac60d7e8fe32ef9005eb3",
"sourceName": "contracts/BandwidthGenerator.sol",
"solcConfig": {
"version": "0.8.10",
@@ -40,8 +40,8 @@
"BandwidthGenerator"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/CosmosToken.sol": {
"lastModificationDate": 1642773899716,
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/contracts/CosmosToken.sol": {
"lastModificationDate": 1648465653397,
"contentHash": "fc5dd09fe73bc6cfece970f702a3aba1",
"sourceName": "contracts/CosmosToken.sol",
"solcConfig": {
@@ -76,8 +76,8 @@
"CosmosERC20"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/Gravity.sol": {
"lastModificationDate": 1642773899716,
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/contracts/Gravity.sol": {
"lastModificationDate": 1648465653397,
"contentHash": "0d6dae561f7b541bafb892b8593a08ac",
"sourceName": "contracts/Gravity.sol",
"solcConfig": {
@@ -117,9 +117,9 @@
"Gravity"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/access/Ownable.sol": {
"lastModificationDate": 1641812554274,
"contentHash": "4fe56b59ced59d87df6b796758f62895",
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/access/Ownable.sol": {
"lastModificationDate": 1648741041841,
"contentHash": "8398972af73b4e9e5ff3b31cad86234f",
"sourceName": "@openzeppelin/contracts/access/Ownable.sol",
"solcConfig": {
"version": "0.8.10",
@@ -153,9 +153,9 @@
"Ownable"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/math/SafeMath.sol": {
"lastModificationDate": 1641812554378,
"contentHash": "5365090efc586b728719e562ebfed0d6",
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/math/SafeMath.sol": {
"lastModificationDate": 1648741041848,
"contentHash": "a6a9d6c1f662e922b82c1ba4d81cdd48",
"sourceName": "@openzeppelin/contracts/utils/math/SafeMath.sol",
"solcConfig": {
"version": "0.8.10",
@@ -187,9 +187,9 @@
"SafeMath"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
"lastModificationDate": 1641812553614,
"contentHash": "2cd550cedf51b8d294607dad5023d717",
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
"lastModificationDate": 1648741041814,
"contentHash": "f48de0eaae9544072b8766e2eac528a7",
"sourceName": "@openzeppelin/contracts/token/ERC20/ERC20.sol",
"solcConfig": {
"version": "0.8.10",
@@ -225,9 +225,9 @@
"ERC20"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"lastModificationDate": 1641812554110,
"contentHash": "0eac3e1a83ee62326ca007811285b274",
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"lastModificationDate": 1648741041831,
"contentHash": "302d9755e46bc69d7058b0cbe7185e37",
"sourceName": "@openzeppelin/contracts/token/ERC20/IERC20.sol",
"solcConfig": {
"version": "0.8.10",
@@ -259,9 +259,9 @@
"IERC20"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"lastModificationDate": 1641812554118,
"contentHash": "aa1be06992a99bb7393b26a6af3c4dfb",
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"lastModificationDate": 1648741041831,
"contentHash": "909ab67fc5c25033fe6cd364f8c056f9",
"sourceName": "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol",
"solcConfig": {
"version": "0.8.10",
@@ -295,9 +295,9 @@
"IERC20Metadata"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/Context.sol": {
"lastModificationDate": 1641812553346,
"contentHash": "851485d5b925529b1a2f34a0be077891",
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/Context.sol": {
"lastModificationDate": 1648741041804,
"contentHash": "5f2c5c4b6af2dd4551027144797bc8be",
"sourceName": "@openzeppelin/contracts/utils/Context.sol",
"solcConfig": {
"version": "0.8.10",
@@ -329,9 +329,9 @@
"Context"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
"lastModificationDate": 1641812554370,
"contentHash": "d37406082a74a9b6b114de522fcb6349",
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
"lastModificationDate": 1648741041848,
"contentHash": "76814c83c32552ed2b521c816b4d801a",
"sourceName": "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol",
"solcConfig": {
"version": "0.8.10",
@@ -366,9 +366,9 @@
"SafeERC20"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol": {
"lastModificationDate": 1641812554322,
"contentHash": "53fbff678f378956efcb207fa748eaa6",
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/security/ReentrancyGuard.sol": {
"lastModificationDate": 1648741041844,
"contentHash": "92f9448b23a90ea3bb932ee55cc3ccce",
"sourceName": "@openzeppelin/contracts/security/ReentrancyGuard.sol",
"solcConfig": {
"version": "0.8.10",
@@ -400,9 +400,9 @@
"ReentrancyGuard"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/Address.sol": {
"lastModificationDate": 1641812553270,
"contentHash": "c5f6c4e4df069c789e7d84b4c3011913",
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/Address.sol": {
"lastModificationDate": 1648741041804,
"contentHash": "4cff8b2ea0d958750786b4e3ca308445",
"sourceName": "@openzeppelin/contracts/utils/Address.sol",
"solcConfig": {
"version": "0.8.10",
@@ -434,9 +434,9 @@
"Address"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol": {
"lastModificationDate": 1641812553414,
"contentHash": "197dbfaf7146845fa76331a4c9980e9f",
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/cryptography/ECDSA.sol": {
"lastModificationDate": 1648741041808,
"contentHash": "395edffd6bb2b3ba29970d5814039654",
"sourceName": "@openzeppelin/contracts/utils/cryptography/ECDSA.sol",
"solcConfig": {
"version": "0.8.10",
@@ -460,16 +460,52 @@
}
}
},
"imports": [
"../Strings.sol"
],
"versionPragmas": [
"^0.8.0"
],
"artifacts": [
"ECDSA"
]
},
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/node_modules/@openzeppelin/contracts/utils/Strings.sol": {
"lastModificationDate": 1648741041848,
"contentHash": "9c54c6c065d9e590fdcdd72c451425b9",
"sourceName": "@openzeppelin/contracts/utils/Strings.sol",
"solcConfig": {
"version": "0.8.10",
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"abi",
"evm.bytecode",
"evm.deployedBytecode",
"evm.methodIdentifiers"
],
"": [
"ast"
]
}
}
}
},
"imports": [],
"versionPragmas": [
"^0.8.0"
],
"artifacts": [
"ECDSA"
"Strings"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/test-contracts/TestCosmosToken.sol": {
"lastModificationDate": 1642773899716,
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/contracts/test-contracts/TestCosmosToken.sol": {
"lastModificationDate": 1648465653397,
"contentHash": "957b6079ee0d5a6e048fce8555c14dee",
"sourceName": "contracts/test-contracts/TestCosmosToken.sol",
"solcConfig": {
@@ -504,8 +540,8 @@
"TestCosmosERC20"
]
},
"/home/max/dev/nymtech/nym/contracts/basic-bandwidth-generation/contracts/test-contracts/TestGravity.sol": {
"lastModificationDate": 1642773899716,
"/home/mx/dev/nym/nym/contracts/basic-bandwidth-generation/contracts/test-contracts/TestGravity.sol": {
"lastModificationDate": 1648465653397,
"contentHash": "f24299c3acb20aff23914e16b7ba95e1",
"sourceName": "contracts/test-contracts/TestGravity.sol",
"solcConfig": {
@@ -80,12 +80,12 @@ contract BandwidthGenerator is Ownable {
}
/**
* @dev Function to create a BBC for account owning the verification key on the Nym Cosmos Blockchain
* @dev Function to create a BBC for account owning the verification key on the Nyx Blockchain
* by transfering erc20NYM via the Gravity Bridge.
* @param _amount Amount of erc20NYM tokens to spend on BBC - denominated in wei.
* @param _verificationKey Verification key of account on Nym blockchain who is purchasing BBC.
* @param _verificationKey Verification key of account on Nyx blockchain who is purchasing BBC.
* @param _signedVerificationKey Number of erc20NYMs to spend signed by _verificationKey for auth on Cosmos Blockchain.
* @param _cosmosRecipient Address of the recipient of payment on Nym Cosmos Blockchain.
* @param _cosmosRecipient Address of the recipient of payment on Nyx Blockchain.
*/
function generateBasicBandwidthCredential(uint256 _amount, uint256 _verificationKey, bytes memory _signedVerificationKey, string calldata _cosmosRecipient) public checkEnabled {
require(_signedVerificationKey.length == 64, "BandwidthGenerator: Signature doesn't have 64 bytes");
+867
View File
@@ -0,0 +1,867 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "block-buffer"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
dependencies = [
"block-padding",
"byte-tools",
"byteorder",
"generic-array 0.12.4",
]
[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array 0.14.4",
]
[[package]]
name = "block-padding"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
dependencies = [
"byte-tools",
]
[[package]]
name = "byte-tools"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "config"
version = "0.1.0"
dependencies = [
"handlebars",
"humantime-serde",
"network-defaults",
"serde",
"toml",
"url",
]
[[package]]
name = "const-oid"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdab415d6744056100f40250a66bc430c1a46f7a02e20bc11c94c79a0f0464df"
[[package]]
name = "cosmos_contract"
version = "0.1.0"
dependencies = [
"config",
"cosmwasm-std",
"cosmwasm-storage",
"erc20-bridge-contract",
"schemars",
"serde",
"thiserror",
]
[[package]]
name = "cosmwasm-crypto"
version = "1.0.0-beta2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c16b255449b3f5cd7fa4b79acd5225b5185655261087a3d8aaac44f88a0e23e9"
dependencies = [
"digest 0.9.0",
"ed25519-zebra",
"k256",
"rand_core 0.5.1",
"thiserror",
]
[[package]]
name = "cosmwasm-derive"
version = "1.0.0-beta2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abad1a6ff427a2f66890a4dce6354b4563cd07cee91a942300e011c921c09ed2"
dependencies = [
"syn",
]
[[package]]
name = "cosmwasm-std"
version = "1.0.0-beta2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1660ee3d5734672e1eb4f0ceda403e2d83345e15143a48845f340f3252ce99a6"
dependencies = [
"base64",
"cosmwasm-crypto",
"cosmwasm-derive",
"schemars",
"serde",
"serde-json-wasm",
"thiserror",
"uint",
]
[[package]]
name = "cosmwasm-storage"
version = "1.0.0-beta2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf3b4efe3b4f86df668520a02e9a29c23eea99b64dfcacb0e59b98346418af7f"
dependencies = [
"cosmwasm-std",
"serde",
]
[[package]]
name = "cpufeatures"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
dependencies = [
"libc",
]
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto-bigint"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d12477e115c0d570c12a2dfd859f80b55b60ddb5075df210d3af06d133a69f45"
dependencies = [
"generic-array 0.14.4",
"rand_core 0.6.3",
"subtle",
"zeroize",
]
[[package]]
name = "crypto-mac"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
dependencies = [
"generic-array 0.14.4",
"subtle",
]
[[package]]
name = "curve25519-dalek"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
dependencies = [
"byteorder",
"digest 0.9.0",
"rand_core 0.5.1",
"subtle",
"zeroize",
]
[[package]]
name = "der"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28e98c534e9c8a0483aa01d6f6913bc063de254311bd267c9cf535e9b70e15b2"
dependencies = [
"const-oid",
]
[[package]]
name = "digest"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
dependencies = [
"generic-array 0.12.4",
]
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array 0.14.4",
]
[[package]]
name = "dyn-clone"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf"
[[package]]
name = "ecdsa"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43ee23aa5b4f68c7a092b5c3beb25f50c406adc75e2363634f242f28ab255372"
dependencies = [
"der",
"elliptic-curve",
"hmac",
"signature",
]
[[package]]
name = "ed25519-zebra"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a128b76af6dd4b427e34a6fd43dc78dbfe73672ec41ff615a2414c1a0ad0409"
dependencies = [
"curve25519-dalek",
"hex",
"rand_core 0.5.1",
"serde",
"sha2",
"thiserror",
]
[[package]]
name = "elliptic-curve"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beca177dcb8eb540133e7680baff45e7cc4d93bf22002676cec549f82343721b"
dependencies = [
"crypto-bigint",
"ff",
"generic-array 0.14.4",
"group",
"pkcs8",
"rand_core 0.6.3",
"subtle",
"zeroize",
]
[[package]]
name = "erc20-bridge-contract"
version = "0.1.0"
dependencies = [
"schemars",
"serde",
]
[[package]]
name = "fake-simd"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "ff"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f"
dependencies = [
"rand_core 0.6.3",
"subtle",
]
[[package]]
name = "form_urlencoded"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
dependencies = [
"matches",
"percent-encoding",
]
[[package]]
name = "generic-array"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
dependencies = [
"typenum",
]
[[package]]
name = "generic-array"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
]
[[package]]
name = "group"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c363a5301b8f153d80747126a04b3c82073b9fe3130571a9d170cacdeaf7912"
dependencies = [
"ff",
"rand_core 0.6.3",
"subtle",
]
[[package]]
name = "handlebars"
version = "3.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3"
dependencies = [
"log",
"pest",
"pest_derive",
"quick-error",
"serde",
"serde_json",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hex-literal"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21e4590e13640f19f249fe3e4eca5113bc4289f2497710378190e7f4bd96f45b"
[[package]]
name = "hmac"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
dependencies = [
"crypto-mac",
"digest 0.9.0",
]
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "humantime-serde"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac34a56cfd4acddb469cc7fff187ed5ac36f498ba085caf8bbc725e3ff474058"
dependencies = [
"humantime",
"serde",
]
[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "k256"
version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "903ae2481bcdfdb7b68e0a9baa4b7c9aff600b9ae2e8e5bb5833b8c91ab851ea"
dependencies = [
"cfg-if",
"ecdsa",
"elliptic-curve",
"sha2",
]
[[package]]
name = "libc"
version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "maplit"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "network-defaults"
version = "0.1.0"
dependencies = [
"hex-literal",
"serde",
"time",
"url",
]
[[package]]
name = "opaque-debug"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
dependencies = [
"maplit",
"pest",
"sha-1",
]
[[package]]
name = "pkcs8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee3ef9b64d26bad0536099c816c6734379e45bbd5f14798def6809e5cc350447"
dependencies = [
"der",
"spki",
]
[[package]]
name = "proc-macro2"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom 0.1.16",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom 0.2.3",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "schemars"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7a48d098c2a7fdf5740b19deb1181b4fb8a9e68e03ae517c14cde04b5725409"
dependencies = [
"dyn-clone",
"schemars_derive",
"serde",
"serde_json",
]
[[package]]
name = "schemars_derive"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a9ea2a613fe4cd7118b2bb101a25d8ae6192e1975179b67b2f17afd11e70ac8"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn",
]
[[package]]
name = "serde"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde-json-wasm"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50eef3672ec8fa45f3457fd423ba131117786784a895548021976117c1ded449"
dependencies = [
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_derive_internals"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "sha-1"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
dependencies = [
"block-buffer 0.7.3",
"digest 0.8.1",
"fake-simd",
"opaque-debug 0.2.3",
]
[[package]]
name = "sha2"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cpufeatures",
"digest 0.9.0",
"opaque-debug 0.3.0",
]
[[package]]
name = "signature"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19772be3c4dd2ceaacf03cb41d5885f2a02c4d8804884918e3a258480803335"
dependencies = [
"digest 0.9.0",
"rand_core 0.6.3",
]
[[package]]
name = "spki"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c01a0c15da1b0b0e1494112e7af814a678fec9bd157881b49beac661e9b6f32"
dependencies = [
"der",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "thiserror"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99beeb0daeac2bd1e86ac2c21caddecb244b39a093594da1a661ec2060c7aedd"
dependencies = [
"libc",
"time-macros",
]
[[package]]
name = "time-macros"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6"
[[package]]
name = "tinyvec"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "toml"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde",
]
[[package]]
name = "typenum"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "uint"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6470ab50f482bde894a037a57064480a246dbfdd5960bd65a44824693f08da5f"
dependencies = [
"byteorder",
"crunchy",
"hex",
"static_assertions",
]
[[package]]
name = "unicode-bidi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-normalization"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "url"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
dependencies = [
"form_urlencoded",
"idna",
"matches",
"percent-encoding",
]
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "zeroize"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
+21
View File
@@ -0,0 +1,21 @@
[package]
name = "coconut-bandwidth"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
bandwidth-claim-contract = { path = "../../common/bandwidth-claim-contract" }
coconut-bandwidth-contract-common = { path = "../../common/cosmwasm-smart-contracts/coconut-bandwidth-contract" }
config = { path = "../../common/config"}
cosmwasm-std = "1.0.0-beta3"
cosmwasm-storage = "1.0.0-beta3"
schemars = "0.8"
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
thiserror = "1.0.23"
+26
View File
@@ -0,0 +1,26 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::StdError;
use thiserror::Error;
use config::defaults::DENOM;
/// Custom errors for contract failure conditions.
///
/// Add any other custom errors you like here.
/// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details.
#[derive(Error, Debug, PartialEq)]
pub enum ContractError {
#[error("{0}")]
Std(#[from] StdError),
#[error("Received multiple coin types")]
MultipleDenoms,
#[error("No coin was sent for voucher")]
NoCoin,
#[error("Wrong coin denomination, you must send {}", DENOM)]
WrongDenom,
}
+73
View File
@@ -0,0 +1,73 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
mod error;
mod support;
mod transactions;
use cosmwasm_std::{entry_point, DepsMut, Env, MessageInfo, Response};
use crate::error::ContractError;
use coconut_bandwidth_contract_common::msg::{ExecuteMsg, InstantiateMsg, MigrateMsg};
/// Instantiate the contract.
///
/// `deps` contains Storage, API and Querier
/// `env` contains block, message and contract info
/// `msg` is the contract initialization message, sort of like a constructor call.
#[entry_point]
pub fn instantiate(
_deps: DepsMut<'_>,
_env: Env,
_info: MessageInfo,
_msg: InstantiateMsg,
) -> Result<Response, ContractError> {
Ok(Response::default())
}
/// Handle an incoming message
#[entry_point]
pub fn execute(
deps: DepsMut<'_>,
env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result<Response, ContractError> {
match msg {
ExecuteMsg::DepositFunds { data } => transactions::deposit_funds(deps, env, info, data),
}
}
#[entry_point]
pub fn migrate(_deps: DepsMut<'_>, _env: Env, _msg: MigrateMsg) -> Result<Response, ContractError> {
Ok(Default::default())
}
#[cfg(test)]
mod tests {
use super::*;
use config::defaults::DENOM;
use cosmwasm_std::coins;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
#[test]
fn initialize_contract() {
let mut deps = mock_dependencies();
let env = mock_env();
let msg = InstantiateMsg {};
let info = mock_info("creator", &[]);
let res = instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
assert_eq!(0, res.messages.len());
// Contract balance should be 0
assert_eq!(
coins(0, DENOM),
vec![deps
.as_ref()
.querier
.query_balance(env.contract.address, DENOM)
.unwrap()]
);
}
}
@@ -0,0 +1,3 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
pub mod tests;
@@ -0,0 +1,19 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
#[cfg(test)]
pub mod helpers {
use crate::instantiate;
use coconut_bandwidth_contract_common::msg::InstantiateMsg;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info, MockApi, MockQuerier};
use cosmwasm_std::{Empty, MemoryStorage, OwnedDeps};
pub fn init_contract() -> OwnedDeps<MemoryStorage, MockApi, MockQuerier<Empty>> {
let mut deps = mock_dependencies();
let msg = InstantiateMsg {};
let env = mock_env();
let info = mock_info("creator", &[]);
instantiate(deps.as_mut(), env.clone(), info, msg).unwrap();
return deps;
}
}
@@ -0,0 +1,136 @@
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use cosmwasm_std::{DepsMut, Env, Event, MessageInfo, Response};
use crate::error::ContractError;
use coconut_bandwidth_contract_common::deposit::DepositData;
use coconut_bandwidth_contract_common::events::{
DEPOSITED_FUNDS_EVENT_TYPE, DEPOSIT_ENCRYPTION_KEY, DEPOSIT_IDENTITY_KEY, DEPOSIT_INFO,
DEPOSIT_VALUE,
};
use config::defaults::DENOM;
pub(crate) fn deposit_funds(
_deps: DepsMut<'_>,
_env: Env,
info: MessageInfo,
data: DepositData,
) -> Result<Response, ContractError> {
if info.funds.is_empty() {
return Err(ContractError::NoCoin);
}
if info.funds.len() > 1 {
return Err(ContractError::MultipleDenoms);
}
if info.funds[0].denom != DENOM {
return Err(ContractError::WrongDenom);
}
let voucher_value = info.funds.last().unwrap();
let event = Event::new(DEPOSITED_FUNDS_EVENT_TYPE)
.add_attribute(DEPOSIT_VALUE, voucher_value.amount)
.add_attribute(DEPOSIT_INFO, data.deposit_info())
.add_attribute(DEPOSIT_IDENTITY_KEY, data.identity_key())
.add_attribute(DEPOSIT_ENCRYPTION_KEY, data.encryption_key());
Ok(Response::new().add_event(event))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::support::tests::helpers;
use cosmwasm_std::testing::{mock_env, mock_info};
use cosmwasm_std::Coin;
#[test]
fn invalid_deposit() {
let mut deps = helpers::init_contract();
let env = mock_env();
let info = mock_info("requester", &[]);
let deposit_info = String::from("Deposit info");
let verification_key = String::from("Verification key");
let encryption_key = String::from("Encryption key");
let data = DepositData::new(deposit_info, verification_key, encryption_key);
assert_eq!(
deposit_funds(deps.as_mut(), env.clone(), info, data.clone()),
Err(ContractError::NoCoin)
);
let coin = Coin::new(1000000, DENOM);
let second_coin = Coin::new(1000000, "some_denom");
let info = mock_info("requester", &[coin, second_coin.clone()]);
assert_eq!(
deposit_funds(deps.as_mut(), env.clone(), info, data.clone()),
Err(ContractError::MultipleDenoms)
);
let info = mock_info("requester", &[second_coin]);
assert_eq!(
deposit_funds(deps.as_mut(), env, info, data),
Err(ContractError::WrongDenom)
);
}
#[test]
fn valid_deposit() {
let mut deps = helpers::init_contract();
let env = mock_env();
let deposit_info = String::from("Deposit info");
let verification_key = String::from("Verification key");
let encryption_key = String::from("Encryption key");
let deposit_value = 424242;
let data = DepositData::new(
deposit_info.clone(),
verification_key.clone(),
encryption_key.clone(),
);
let coin = Coin::new(deposit_value, DENOM);
let info = mock_info("requester", &[coin]);
let tx = deposit_funds(deps.as_mut(), env.clone(), info, data).unwrap();
let events: Vec<_> = tx
.events
.iter()
.filter(|event| event.ty == DEPOSITED_FUNDS_EVENT_TYPE)
.collect();
assert_eq!(events.len(), 1);
let event = events[0];
assert_eq!(event.attributes.len(), 4);
let deposit_attr = event
.attributes
.iter()
.find(|attr| attr.key == DEPOSIT_VALUE)
.unwrap();
assert_eq!(deposit_attr.value, deposit_value.to_string());
let info_attr = event
.attributes
.iter()
.find(|attr| attr.key == DEPOSIT_INFO)
.unwrap();
assert_eq!(info_attr.value, deposit_info);
let verification_key_attr = event
.attributes
.iter()
.find(|attr| attr.key == DEPOSIT_IDENTITY_KEY)
.unwrap();
assert_eq!(verification_key_attr.value, verification_key);
let encryption_key_attr = event
.attributes
.iter()
.find(|attr| attr.key == DEPOSIT_ENCRYPTION_KEY)
.unwrap();
assert_eq!(encryption_key_attr.value, encryption_key);
}
}
+2
View File
@@ -18,6 +18,8 @@ We use the following:
## Development mode
Copy the `.env.prod` file to `.env` to configure your environment. Using the live sandbox Explorer API is the best way to do development, so the prod settings are good.
Run the following:
```
+1 -1
View File
@@ -55,7 +55,7 @@ validator-client = { path = "../common/client-libs/validator-client", features =
version-checker = { path = "../common/version-checker" }
[features]
coconut = ["coconut-interface", "gateway-requests/coconut", "gateway-client/coconut"]
coconut = ["coconut-interface", "gateway-requests/coconut", "gateway-client/coconut", "credentials/coconut"]
eth = []
[build-dependencies]
+1 -1
View File
@@ -27,7 +27,7 @@ coconut-interface = { path = "../../common/coconut-interface", optional = true }
credentials = { path = "../../common/credentials" }
[features]
coconut = ["coconut-interface"]
coconut = ["coconut-interface", "credentials/coconut"]
[dependencies.tungstenite]
version = "0.13.0"
+57
View File
@@ -0,0 +1,57 @@
/* eslint-disable no-param-reassign */
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = {
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: '@storybook/react',
core: {
builder: 'webpack5',
},
// webpackFinal: async (config, { configType }) => {
// // `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
// // You can change the configuration based on that.
// // 'PRODUCTION' is used when building the static version of storybook.
webpackFinal: async (config) => {
config.module.rules.forEach((rule) => {
// look for SVG import rule and replace
// NOTE: the rule before modification is /\.(svg|ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/
if (rule.test?.toString().includes('svg')) {
rule.test = /\.(ico|jpg|jpeg|png|apng|gif|eot|otf|webp|ttf|woff|woff2|cur|ani|pdf)(\?.*)?$/;
}
});
// handle asset loading with this
config.module.rules.unshift({
test: /\.svg(\?.*)?$/i,
issuer: /\.[jt]sx?$/,
use: ['@svgr/webpack'],
});
config.resolve.extensions = ['.tsx', '.ts', '.js'];
config.resolve.plugins = [new TsconfigPathsPlugin()];
config.plugins.push(new ForkTsCheckerWebpackPlugin({
typescript: {
mode: 'write-references',
diagnosticOptions: {
semantic: true,
syntactic: true,
},
},
}));
if (!config.resolve.alias) {
config.resolve.alias = {};
}
config.resolve.alias['@tauri-apps/api'] = `${__dirname}/mocks/tauri`;
// Return the altered config
return config;
},
features: {
emotionAlias: false,
},
};
@@ -0,0 +1,13 @@
/**
* This is a mock for Tauri's API package (@tauri-apps/api), to prevent stories from being excluded, because they either use
* or import dependencies that use Tauri.
*/
module.exports = {
invoke: (operation) => {
console.error(`Tauri cannot be used in Storybook. The operation requested was "${operation}". You can add mock responses to "nym_wallet/.storybook/mocks/tauri.js" if you need. The default response is "void".`);
return new Promise((resolve, reject) => {
reject(new Error(`Tauri operation ${operation} not available in storybook.`));
});
},
}
@@ -0,0 +1,10 @@
/**
* This is a mock for Tauri's API package (@tauri-apps/api/window), to prevent stories from being excluded, because they either use
* or import dependencies that use Tauri.
*/
module.exports = {
appWindow: {
maximize: () => undefined,
}
}
+19
View File
@@ -0,0 +1,19 @@
import { NymWalletThemeWithMode } from '../src/theme/NymWalletTheme';
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
}
const withThemeProvider = (Story, context) => (
<NymWalletThemeWithMode mode="light">
<Story {...context} />
</NymWalletThemeWithMode>
);
export const decorators = [withThemeProvider];
+46 -195
View File
@@ -38,7 +38,6 @@ dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"ctr",
"opaque-debug 0.3.0",
]
@@ -53,7 +52,7 @@ dependencies = [
"cipher",
"ctr",
"ghash",
"subtle 2.4.1",
"subtle",
]
[[package]]
@@ -87,7 +86,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25df3c03f1040d0069fcd3907e24e36d59f9b6fa07ba49be0eb25a794f036ba7"
dependencies = [
"base64ct",
"blake2 0.10.4",
"blake2",
"password-hash",
]
@@ -211,7 +210,7 @@ dependencies = [
"rand_core 0.6.3",
"ripemd160",
"sha2 0.9.9",
"subtle 2.4.1",
"subtle",
"zeroize",
]
@@ -240,18 +239,6 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blake2"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330"
dependencies = [
"byte-tools",
"crypto-mac 0.7.0",
"digest 0.8.1",
"opaque-debug 0.2.3",
]
[[package]]
name = "blake2"
version = "0.10.4"
@@ -339,7 +326,7 @@ dependencies = [
"group 0.10.0",
"pairing",
"rand_core 0.6.3",
"subtle 2.4.1",
"subtle",
]
[[package]]
@@ -504,16 +491,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "chacha"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862"
dependencies = [
"byteorder",
"keystream",
]
[[package]]
name = "cipher"
version = "0.3.0"
@@ -567,9 +544,22 @@ dependencies = [
name = "coconut-interface"
version = "0.1.0"
dependencies = [
"bs58",
"getset",
"nymcoconut",
"serde",
"thiserror",
]
[[package]]
name = "colored"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
dependencies = [
"atty",
"lazy_static",
"winapi",
]
[[package]]
@@ -746,19 +736,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "credentials"
version = "0.1.0"
dependencies = [
"bls12_381",
"coconut-interface",
"crypto",
"network-defaults",
"thiserror",
"url",
"validator-client",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.2"
@@ -809,19 +786,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "crypto"
version = "0.1.0"
dependencies = [
"bs58",
"config",
"ed25519-dalek",
"nymsphinx-types",
"pemstore",
"subtle-encoding",
"x25519-dalek",
]
[[package]]
name = "crypto-bigint"
version = "0.2.11"
@@ -830,7 +794,7 @@ checksum = "f83bd3bb4314701c568e340cd8cf78c975aa0ca79e03d3f6d1677d5b0c9c0c03"
dependencies = [
"generic-array 0.14.5",
"rand_core 0.6.3",
"subtle 2.4.1",
"subtle",
"zeroize",
]
@@ -842,7 +806,7 @@ checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21"
dependencies = [
"generic-array 0.14.5",
"rand_core 0.6.3",
"subtle 2.4.1",
"subtle",
"zeroize",
]
@@ -856,16 +820,6 @@ dependencies = [
"typenum",
]
[[package]]
name = "crypto-mac"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
dependencies = [
"generic-array 0.12.4",
"subtle 1.0.0",
]
[[package]]
name = "crypto-mac"
version = "0.11.1"
@@ -873,7 +827,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
dependencies = [
"generic-array 0.14.5",
"subtle 2.4.1",
"subtle",
]
[[package]]
@@ -946,7 +900,7 @@ dependencies = [
"byteorder",
"digest 0.9.0",
"rand_core 0.5.1",
"subtle 2.4.1",
"subtle",
"zeroize",
]
@@ -1119,7 +1073,7 @@ checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer 0.10.2",
"crypto-common",
"subtle 2.4.1",
"subtle",
]
[[package]]
@@ -1169,6 +1123,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
name = "dotenv"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "dtoa"
version = "0.4.8"
@@ -1231,8 +1191,6 @@ checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
dependencies = [
"curve25519-dalek",
"ed25519",
"rand 0.7.3",
"serde",
"sha2 0.9.9",
"zeroize",
]
@@ -1269,7 +1227,7 @@ dependencies = [
"group 0.10.0",
"pkcs8 0.7.6",
"rand_core 0.6.3",
"subtle 2.4.1",
"subtle",
"zeroize",
]
@@ -1287,7 +1245,7 @@ dependencies = [
"group 0.11.0",
"rand_core 0.6.3",
"sec1",
"subtle 2.4.1",
"subtle",
"zeroize",
]
@@ -1351,7 +1309,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0f40b2dcd8bc322217a5f6559ae5f9e9d1de202a2ecee2e9eafcbece7562a4f"
dependencies = [
"rand_core 0.6.3",
"subtle 2.4.1",
"subtle",
]
[[package]]
@@ -1361,7 +1319,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2958d04124b9f27f175eaeb9a9f383d026098aa837eadd8ba22c11f13a05b9e"
dependencies = [
"rand_core 0.6.3",
"subtle 2.4.1",
"subtle",
]
[[package]]
@@ -1692,10 +1650,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
@@ -1881,7 +1837,7 @@ dependencies = [
"byteorder",
"ff 0.10.1",
"rand_core 0.6.3",
"subtle 2.4.1",
"subtle",
]
[[package]]
@@ -1892,7 +1848,7 @@ checksum = "bc5ac374b108929de78460075f3dc439fa66df9d8fc77e8f12caa5165fcf0c89"
dependencies = [
"ff 0.11.0",
"rand_core 0.6.3",
"subtle 2.4.1",
"subtle",
]
[[package]]
@@ -2056,23 +2012,13 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0"
[[package]]
name = "hkdf"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b"
dependencies = [
"digest 0.9.0",
"hmac",
]
[[package]]
name = "hmac"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
dependencies = [
"crypto-mac 0.11.1",
"crypto-mac",
"digest 0.9.0",
]
@@ -2433,12 +2379,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7"
[[package]]
name = "keystream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28"
[[package]]
name = "kuchiki"
version = "0.8.1"
@@ -2463,24 +2403,6 @@ version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "libm"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db"
[[package]]
name = "lioness"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9"
dependencies = [
"arrayref",
"blake2 0.8.1",
"chacha",
"keystream",
]
[[package]]
name = "lock_api"
version = "0.4.6"
@@ -2792,7 +2714,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg 1.1.0",
"libm",
]
[[package]]
@@ -2845,13 +2766,15 @@ dependencies = [
"bip39",
"cfg-if",
"coconut-interface",
"colored",
"config",
"cosmrs",
"cosmwasm-std",
"credentials",
"dirs",
"dotenv",
"eyre",
"futures",
"itertools",
"log",
"mixnet-contract-common",
"pretty_env_logger",
@@ -2895,13 +2818,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "nymsphinx-types"
version = "0.1.0"
dependencies = [
"sphinx",
]
[[package]]
name = "objc"
version = "0.2.7"
@@ -3054,7 +2970,7 @@ checksum = "1d791538a6dcc1e7cb7fe6f6b58aca40e7f79403c45b2bc274008b5e647af1d8"
dependencies = [
"base64ct",
"rand_core 0.6.3",
"subtle 2.4.1",
"subtle",
]
[[package]]
@@ -3075,7 +2991,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739"
dependencies = [
"crypto-mac 0.11.1",
"crypto-mac",
]
[[package]]
@@ -3105,24 +3021,6 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088"
[[package]]
name = "pem"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb"
dependencies = [
"base64",
"once_cell",
"regex",
]
[[package]]
name = "pemstore"
version = "0.1.0"
dependencies = [
"pem",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
@@ -3606,16 +3504,6 @@ dependencies = [
"getrandom 0.2.5",
]
[[package]]
name = "rand_distr"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e9532ada3929fb8b2e9dbe28d1e06c9b2cc65813f074fcb6bd5fbefeff9d56"
dependencies = [
"num-traits",
"rand 0.7.3",
]
[[package]]
name = "rand_hc"
version = "0.1.0"
@@ -3995,7 +3883,7 @@ dependencies = [
"der 0.5.1",
"generic-array 0.14.5",
"pkcs8 0.8.0",
"subtle 2.4.1",
"subtle",
"zeroize",
]
@@ -4333,29 +4221,6 @@ dependencies = [
"system-deps 5.0.0",
]
[[package]]
name = "sphinx"
version = "0.1.0"
source = "git+https://github.com/nymtech/sphinx?rev=c494250f2a78bed33a618d470792418eee932859#c494250f2a78bed33a618d470792418eee932859"
dependencies = [
"aes",
"arrayref",
"blake2 0.8.1",
"bs58",
"byteorder",
"chacha",
"curve25519-dalek",
"digest 0.9.0",
"hkdf",
"hmac",
"lioness",
"log",
"rand 0.7.3",
"rand_distr",
"sha2 0.9.9",
"subtle 2.4.1",
]
[[package]]
name = "spin"
version = "0.5.2"
@@ -4480,12 +4345,6 @@ dependencies = [
"syn",
]
[[package]]
name = "subtle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
[[package]]
name = "subtle"
version = "2.4.1"
@@ -4824,7 +4683,7 @@ dependencies = [
"serde_repr",
"sha2 0.9.9",
"signature",
"subtle 2.4.1",
"subtle",
"subtle-encoding",
"tendermint-proto",
"time 0.3.7",
@@ -5215,7 +5074,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
dependencies = [
"generic-array 0.14.5",
"subtle 2.4.1",
"subtle",
]
[[package]]
@@ -5267,10 +5126,12 @@ dependencies = [
"base64",
"bip39",
"coconut-interface",
"colored",
"config",
"cosmrs",
"cosmwasm-std",
"flate2",
"futures",
"itertools",
"log",
"mixnet-contract-common",
@@ -5281,6 +5142,7 @@ dependencies = [
"serde_json",
"sha2 0.9.9",
"thiserror",
"tokio",
"url",
"validator-api-requests",
"vesting-contract",
@@ -5747,17 +5609,6 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "x25519-dalek"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f"
dependencies = [
"curve25519-dalek",
"rand_core 0.5.1",
"zeroize",
]
[[package]]
name = "xattr"
version = "0.2.2"
+11 -2
View File
@@ -10,11 +10,15 @@
"tauri:dev": "yarn tauri dev",
"tauri:build": "yarn tauri build",
"tsc": "tsc --noEmit true",
"tsc:watch": "tsc --noEmit true --watch",
"dev": "yarn run webpack:dev & yarn run tauri:dev",
"prebuild": "yarn --cwd .. build",
"build": "run-s webpack:prod tauri:build",
"lint": "eslint src",
"lint:fix": "eslint src --fix"
"lint:fix": "eslint src --fix",
"prestorybook": "yarn --cwd .. build",
"storybook": "start-storybook -p 6006",
"storybook:build": "build-storybook"
},
"dependencies": {
"@babel/preset-typescript": "^7.15.0",
@@ -38,7 +42,6 @@
"react-hook-form": "^7.14.2",
"react-router-dom": "^5.2.0",
"semver": "^6.3.0",
"use-clipboard-copy": "^0.2.0",
"yup": "^0.32.9"
},
"devDependencies": {
@@ -48,6 +51,12 @@
"@babel/preset-react": "^7.14.5",
"@nymproject/eslint-config-react-typescript": "^1.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.4",
"@storybook/addon-actions": "^6.4.19",
"@storybook/addon-essentials": "^6.4.19",
"@storybook/addon-interactions": "^6.4.19",
"@storybook/addon-links": "^6.4.19",
"@storybook/react": "^6.4.19",
"@storybook/testing-library": "^0.0.9",
"@svgr/webpack": "^6.1.1",
"@tauri-apps/cli": "^1.0.0-rc.5",
"@testing-library/jest-dom": "^5.14.1",
+8 -14
View File
@@ -1,17 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap"
rel="stylesheet"
/>
<title>Nym Wallet</title>
</head>
<body>
<div id="root"></div>
</body>
<head>
<meta charset='utf-8' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<title>Nym Wallet</title>
</head>
<body>
<div id='root'></div>
</body>
</html>
+3 -1
View File
@@ -21,9 +21,12 @@ tauri-macros = "=1.0.0-rc.1"
[dependencies]
bip39 = "1.0"
cfg-if = "1.0.0"
colored = "2.0"
dirs = "4.0"
dotenv = "0.15.0"
eyre = "0.6.5"
futures = "0.3.15"
itertools = "0.10"
log = "0.4"
pretty_env_logger = "0.4"
rand = "0.6.5"
@@ -53,7 +56,6 @@ mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-
vesting-contract-common = { path = "../../common/cosmwasm-smart-contracts/vesting-contract" }
config = { path = "../../common/config" }
coconut-interface = { path = "../../common/coconut-interface" }
credentials = { path = "../../common/credentials" }
# Used for Type conversion, can be extracted but its a lot of work
vesting-contract = { path = "../../contracts/vesting" }
+47 -76
View File
@@ -4,10 +4,9 @@
use crate::{error::BackendError, network::Network as WalletNetwork};
use config::defaults::{all::SupportedNetworks, ValidatorDetails};
use config::NymConfig;
use reqwest::StatusCode;
use core::fmt;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::iter::zip;
use std::time::Duration;
use std::{fs, io, path::PathBuf};
use strum::IntoEnumIterator;
@@ -115,16 +114,18 @@ impl Config {
.chain(self.network.validators(network))
.cloned()
.chain(base_validators)
.unique()
}
pub fn get_validators_with_api_endpoint(
&self,
network: WalletNetwork,
) -> impl Iterator<Item = ValidatorUrlWithApiEndpoint> + '_ {
pub fn get_nymd_urls(&self, network: WalletNetwork) -> impl Iterator<Item = Url> + '_ {
self.get_validators(network).into_iter().map(|v| v.nymd_url)
}
pub fn get_api_urls(&self, network: WalletNetwork) -> impl Iterator<Item = Url> + '_ {
self
.get_validators(network)
.into_iter()
.filter_map(|validator| ValidatorUrlWithApiEndpoint::try_from(validator).ok())
.filter_map(|v| v.api_url)
}
pub fn get_mixnet_contract_address(&self, network: WalletNetwork) -> Option<cosmrs::AccountId> {
@@ -164,66 +165,24 @@ impl Config {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(3))
.build()?;
log::debug!(
"Fetching validator urls from: {}",
REMOTE_SOURCE_OF_VALIDATOR_URLS
);
let response = client
.get(REMOTE_SOURCE_OF_VALIDATOR_URLS.to_string())
.send()
.await?;
self.base.fetched_validators = serde_json::from_str(&response.text().await?)?;
Ok(())
}
pub async fn check_validator_health(
&self,
network: WalletNetwork,
) -> Result<Vec<(ValidatorUrl, StatusCode)>, BackendError> {
// Limit the number of validators we query
let max_validators = 200_usize;
let validators_to_query = || self.get_validators(network).take(max_validators);
let validator_urls = validators_to_query().map(|v| {
let mut health_url = v.nymd_url.clone();
health_url.set_path("health");
(v, health_url)
});
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(3))
.build()?;
let requests = validator_urls.map(|(_, url)| client.get(url).send());
let responses = futures::future::join_all(requests).await;
let validators_responding_success =
zip(validators_to_query(), responses).filter_map(|(v, r)| match r {
Ok(r) if r.status().is_success() => Some((v, r.status())),
_ => None,
});
Ok(validators_responding_success.collect::<Vec<_>>())
}
#[allow(unused)]
pub async fn check_validator_health_for_all_networks(
&self,
) -> Result<HashMap<WalletNetwork, Vec<(ValidatorUrl, StatusCode)>>, BackendError> {
let validator_health_requests =
WalletNetwork::iter().map(|network| self.check_validator_health(network));
let responses_keyed_by_network = zip(
WalletNetwork::iter(),
futures::future::join_all(validator_health_requests).await,
log::debug!(
"Received validator urls: \n{}",
self.base.fetched_validators
);
// Iterate and collect manually to be able to return errors in the response
let mut responses = HashMap::new();
for (network, response) in responses_keyed_by_network {
responses.insert(network, response?);
}
Ok(responses)
Ok(())
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct ValidatorUrl {
pub nymd_url: Url,
pub api_url: Option<Url>,
@@ -243,23 +202,14 @@ impl TryFrom<ValidatorDetails> for ValidatorUrl {
}
}
#[derive(Clone, Debug)]
pub struct ValidatorUrlWithApiEndpoint {
pub nymd_url: Url,
pub api_url: Url,
}
impl TryFrom<ValidatorUrl> for ValidatorUrlWithApiEndpoint {
type Error = BackendError;
fn try_from(validator: ValidatorUrl) -> Result<Self, Self::Error> {
match validator.api_url {
Some(api_url) => Ok(ValidatorUrlWithApiEndpoint {
nymd_url: validator.nymd_url,
api_url,
}),
None => Err(BackendError::NoValidatorApiUrlConfigured),
}
impl fmt::Display for ValidatorUrl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s1 = format!("nymd_url: {}", self.nymd_url);
let s2 = self
.api_url
.as_ref()
.map(|url| format!(", api_url: {}", url));
write!(f, " {}{},", s1, s2.unwrap_or_default())
}
}
@@ -285,6 +235,27 @@ impl OptionalValidators {
}
}
impl fmt::Display for OptionalValidators {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s1 = self
.mainnet
.as_ref()
.map(|validators| format!("mainnet: [\n{}\n]", validators.iter().format("\n")))
.unwrap_or_default();
let s2 = self
.sandbox
.as_ref()
.map(|validators| format!(",\nsandbox: [\n{}\n]", validators.iter().format("\n")))
.unwrap_or_default();
let s3 = self
.qa
.as_ref()
.map(|validators| format!(",\nqa: [\n{}\n]", validators.iter().format("\n")))
.unwrap_or_default();
write!(f, "{}{}{}", s1, s2, s3)
}
}
#[cfg(test)]
mod tests {
use super::*;
+2
View File
@@ -30,6 +30,7 @@ use crate::operations::vesting;
use crate::state::State;
fn main() {
dotenv::dotenv().ok();
setup_logging();
tauri::Builder::default()
@@ -42,6 +43,7 @@ fn main() {
mixnet::account::does_password_file_exist,
mixnet::account::get_balance,
mixnet::account::logout,
mixnet::account::remove_password,
mixnet::account::sign_in_with_password,
mixnet::account::switch_network,
mixnet::account::update_validator_urls,
@@ -1,25 +1,29 @@
use crate::coin::{Coin, Denom};
use crate::config::{Config, ValidatorUrlWithApiEndpoint};
use crate::config::Config;
use crate::error::BackendError;
use crate::network::Network;
use crate::network::Network as WalletNetwork;
use crate::nymd_client;
use crate::state::State;
use crate::wallet_storage::{self, DEFAULT_WALLET_ACCOUNT_ID};
use bip39::{Language, Mnemonic};
use config::defaults::all::Network;
use config::defaults::COSMOS_DERIVATION_PATH;
use cosmrs::bip32::DerivationPath;
use itertools::Itertools;
use rand::seq::SliceRandom;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::convert::TryInto;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use strum::IntoEnumIterator;
use tokio::sync::RwLock;
use validator_client::nymd::error::NymdError;
use validator_client::nymd::SigningNymdClient;
use validator_client::Client;
use url::Url;
use validator_client::{
connection_tester::run_validator_connection_test, nymd::SigningNymdClient, Client,
};
#[cfg_attr(test, derive(ts_rs::TS))]
#[cfg_attr(test, ts(export, export_to = "../src/types/rust/account.ts"))]
@@ -112,7 +116,7 @@ pub async fn create_new_mnemonic() -> Result<String, BackendError> {
#[tauri::command]
pub async fn switch_network(
state: tauri::State<'_, Arc<RwLock<State>>>,
network: Network,
network: WalletNetwork,
) -> Result<Account, BackendError> {
let account = {
let r_state = state.read().await;
@@ -158,16 +162,41 @@ async fn _connect_with_mnemonic(
state: tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<Account, BackendError> {
update_validator_urls(state.clone()).await?;
let validators = choose_validators(mnemonic.clone(), &state).await?;
let config = state.read().await.config();
let clients = create_clients(&validators, &mnemonic, &config)?;
for network in WalletNetwork::iter() {
log::debug!(
"List of validators for {network}: [\n{}\n]",
config.get_validators(network).format(",\n")
);
}
// Run connection tests on all nymd and validator-api endpoints
let (nymd_urls, api_urls) = {
let mixnet_contract_address = WalletNetwork::iter()
.map(|network| (network.into(), config.get_mixnet_contract_address(network)))
.collect::<HashMap<_, _>>();
let nymd_urls = WalletNetwork::iter().flat_map(|network| {
config
.get_nymd_urls(network)
.map(move |url| (network.into(), url))
});
let api_urls = WalletNetwork::iter().flat_map(|network| {
config
.get_api_urls(network)
.map(move |url| (network.into(), url))
});
run_validator_connection_test(nymd_urls, api_urls, mixnet_contract_address).await
};
let clients = create_clients(&nymd_urls, &api_urls, &mnemonic, &config)?;
// Set the default account
let default_network: Network = config::defaults::DEFAULT_NETWORK.into();
let default_network: WalletNetwork = config::defaults::DEFAULT_NETWORK.into();
let client_for_default_network = clients
.iter()
.find(|client| Network::from(client.network) == default_network);
.find(|client| WalletNetwork::from(client.network) == default_network);
let account_for_default_network = match client_for_default_network {
Some(client) => Ok(Account::new(
client.nymd.mixnet_contract_address()?.to_string(),
@@ -181,7 +210,7 @@ async fn _connect_with_mnemonic(
// Register all the clients
for client in clients {
let network: Network = client.network.into();
let network: WalletNetwork = client.network.into();
let mut w_state = state.write().await;
w_state.add_client(network, client);
}
@@ -189,18 +218,72 @@ async fn _connect_with_mnemonic(
account_for_default_network
}
fn select_random_responding_nymd_url(
nymd_urls: &HashMap<Network, Vec<(Url, bool)>>,
network: WalletNetwork,
config: &Config,
) -> Url {
// We pick a randon responding nymd url, and if not, fall back on the first one in the list.
nymd_urls
.get(&network.into())
.and_then(|urls| {
let nymd_urls: Vec<_> = urls
.iter()
.filter_map(|(url, result)| if *result { Some(url.clone()) } else { None })
.collect();
nymd_urls.choose(&mut rand::thread_rng()).cloned()
})
.unwrap_or_else(|| {
log::debug!("No passing nymd_urls for {network}: using default");
config
.get_nymd_urls(network)
.next()
.expect("Expected at least one hardcoded nymd url")
})
}
fn select_first_responding_api_url(
api_urls: &HashMap<Network, Vec<(Url, bool)>>,
network: WalletNetwork,
config: &Config,
) -> Url {
// We pick the first API url among the responding ones. If none exists, fall back on the first
// one in the list.
api_urls
.get(&network.into())
.and_then(|urls| {
urls
.iter()
.find_map(|(url, result)| if *result { Some(url.clone()) } else { None })
})
.unwrap_or_else(|| {
log::debug!("No passing api_urls for {network}: using default");
config
.get_api_urls(network)
.next()
.expect("Expected at least one hardcoded api url")
})
}
fn create_clients(
validators: &HashMap<Network, ValidatorUrlWithApiEndpoint>,
nymd_urls: &HashMap<Network, Vec<(Url, bool)>>,
api_urls: &HashMap<Network, Vec<(Url, bool)>>,
mnemonic: &Mnemonic,
config: &Config,
) -> Result<Vec<Client<SigningNymdClient>>, BackendError> {
let mut clients = Vec::new();
for network in Network::iter() {
for network in WalletNetwork::iter() {
let nymd_url = select_random_responding_nymd_url(nymd_urls, network, config);
let api_url = select_first_responding_api_url(api_urls, network, config);
log::info!("Connecting to: nymd_url: {nymd_url} for {network}");
log::info!("Connecting to: api_url: {api_url} for {network}");
let client = validator_client::Client::new_signing(
validator_client::Config::new(
network.into(),
validators[&network].nymd_url.clone(),
validators[&network].api_url.clone(),
nymd_url,
api_url,
config.get_mixnet_contract_address(network),
config.get_vesting_contract_address(network),
config.get_bandwidth_claim_contract_address(network),
@@ -212,122 +295,6 @@ fn create_clients(
Ok(clients)
}
async fn choose_validators(
mnemonic: Mnemonic,
state: &tauri::State<'_, Arc<RwLock<State>>>,
) -> Result<HashMap<Network, ValidatorUrlWithApiEndpoint>, BackendError> {
let config = state.read().await.config();
// Try to connect to validators on all networks
let mut validators = select_responding_validators(&config, &mnemonic).await?;
// If for a network we didn't manage to connect to any validators, just go ahead and try with the
// first in the list
for network in Network::iter() {
validators.entry(network).or_insert_with(|| {
let default_validator = config
.get_validators_with_api_endpoint(network)
.next()
// We always have at least one hardcoded default validator
.unwrap();
log::info!(
"Using default for {network}: {}, {}",
default_validator.nymd_url,
default_validator.api_url,
);
default_validator
});
}
Ok(validators)
}
// For each network, try the list of available validators one by one and use the first responding
// one.
async fn select_responding_validators(
config: &Config,
mnemonic: &Mnemonic,
) -> Result<HashMap<Network, ValidatorUrlWithApiEndpoint>, BackendError> {
use tokio::time::timeout;
let validators = futures::future::join_all(Network::iter().map(|network| {
timeout(
Duration::from_millis(3000),
try_connect_to_validators(
config.get_validators_with_api_endpoint(network),
config,
network,
mnemonic.clone(),
),
)
}))
.await;
// Drop networks that failed the global timeout
let validators = validators.into_iter().filter_map(Result::ok);
// Rewrap to return any errors during client creation
let validators = validators.collect::<Result<Vec<_>, _>>()?;
// Filter out networks where we exhausted all listed validators
let validators = validators.into_iter().flatten();
Ok(validators.collect::<HashMap<_, _>>())
}
async fn try_connect_to_validators(
validators: impl Iterator<Item = ValidatorUrlWithApiEndpoint>,
config: &Config,
network: Network,
mnemonic: Mnemonic,
) -> Result<Option<(Network, ValidatorUrlWithApiEndpoint)>, BackendError> {
for validator in validators {
if let Some(responding_validator) =
try_connect_to_validator(&validator, config, network, mnemonic.clone()).await?
{
// Pick the first successful one
return Ok(Some(responding_validator));
}
}
Ok(None)
}
async fn try_connect_to_validator(
validator: &ValidatorUrlWithApiEndpoint,
config: &Config,
network: Network,
mnemonic: Mnemonic,
) -> Result<Option<(Network, ValidatorUrlWithApiEndpoint)>, BackendError> {
let client = validator_client::Client::new_signing(
validator_client::Config::new(
network.into(),
validator.nymd_url.clone(),
validator.api_url.clone(),
config.get_mixnet_contract_address(network),
config.get_vesting_contract_address(network),
config.get_bandwidth_claim_contract_address(network),
),
mnemonic,
)?;
if is_validator_connection_ok(&client).await {
log::info!(
"Connection ok for {network}: {}, {}",
validator.nymd_url,
validator.api_url
);
Ok(Some((network, validator.clone())))
} else {
Ok(None)
}
}
// The criteria used to determina if a validator endpoint is to be used
async fn is_validator_connection_ok(client: &Client<SigningNymdClient>) -> bool {
match client.get_mixnet_contract_version().await {
Err(NymdError::TendermintError(_)) => false,
Err(_) | Ok(_) => true,
}
}
#[tauri::command]
pub fn does_password_file_exist() -> Result<bool, BackendError> {
log::info!("Checking wallet file");
@@ -369,3 +336,10 @@ pub async fn sign_in_with_password(
let stored_account = wallet_storage::load_existing_wallet_login_information(&id, &password)?;
_connect_with_mnemonic(stored_account.mnemonic().clone(), state).await
}
#[tauri::command]
pub fn remove_password() -> Result<(), BackendError> {
log::info!("Removing password");
let id = wallet_storage::WalletAccountId::new(DEFAULT_WALLET_ACCOUNT_ID.to_string());
wallet_storage::remove_wallet_login_information(&id)
}
@@ -35,6 +35,16 @@ impl StoredWallet {
self.accounts.len()
}
pub fn remove_account(&mut self, id: &WalletAccountId) -> Option<EncryptedAccount> {
if let Some(index) = self.accounts.iter().position(|account| &account.id == id) {
log::info!("Removing from wallet file: {id}");
Some(self.accounts.remove(index))
} else {
log::debug!("Tried to remove non-existent id from wallet: {id}");
None
}
}
pub fn encrypted_account_by_index(&self, index: usize) -> Option<&EncryptedAccount> {
self.accounts.get(index)
}
+65 -6
View File
@@ -10,7 +10,8 @@ use crate::wallet_storage::account_data::StoredAccount;
use crate::wallet_storage::encryption::{encrypt_struct, EncryptedData};
use cosmrs::bip32::DerivationPath;
use serde::{Deserialize, Serialize};
use std::fs::{create_dir_all, OpenOptions};
use std::fs::{self, create_dir_all, OpenOptions};
use std::os::unix::prelude::OpenOptionsExt;
use std::path::PathBuf;
use self::account_data::{EncryptedAccount, StoredWallet};
@@ -112,10 +113,43 @@ fn store_wallet_login_information_at_file(
Ok(serde_json::to_writer_pretty(file, &stored_wallet)?)
}
// this function should probably exist, but I guess we need to discuss how it should behave in the context of the UX
// pub(crate) fn remove_wallet_login_information(
//
// )
pub(crate) fn remove_wallet_login_information(id: &WalletAccountId) -> Result<(), BackendError> {
let store_dir = get_storage_directory()?;
let filepath = store_dir.join(WALLET_INFO_FILENAME);
remove_wallet_login_information_at_file(filepath, id)
}
pub(crate) fn remove_wallet_login_information_at_file(
filepath: PathBuf,
id: &WalletAccountId,
) -> Result<(), BackendError> {
let mut stored_wallet = match load_existing_wallet_at_file(filepath.clone()) {
Err(BackendError::WalletFileNotFound) => StoredWallet::default(),
result => result?,
};
if stored_wallet.is_empty() {
log::info!("Removing file: {:#?}", filepath);
return Ok(fs::remove_file(filepath)?);
}
stored_wallet
.remove_account(id)
.ok_or(BackendError::NoSuchIdInWallet)?;
if stored_wallet.is_empty() {
log::info!("Removing file: {:#?}", filepath);
Ok(fs::remove_file(filepath)?)
} else {
let file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(filepath)?;
Ok(serde_json::to_writer_pretty(file, &stored_wallet)?)
}
}
#[cfg(test)]
mod tests {
@@ -127,6 +161,7 @@ mod tests {
// I'm not 100% sure how to feel about having to touch the file system at all
#[test]
#[allow(clippy::too_many_lines)]
fn storing_wallet_information() {
let store_dir = tempdir().unwrap();
let wallet_file = store_dir.path().join(WALLET_INFO_FILENAME);
@@ -248,9 +283,33 @@ mod tests {
assert_eq!(&cosmos_hd_path, acc1.hd_path());
let loaded_account =
load_existing_wallet_login_information_at_file(wallet_file, &id2, &password).unwrap();
load_existing_wallet_login_information_at_file(wallet_file.clone(), &id2, &password).unwrap();
let StoredAccount::Mnemonic(ref acc2) = loaded_account;
assert_eq!(&dummy_account2, acc2.mnemonic());
assert_eq!(&different_hd_path, acc2.hd_path());
// Fails to delete non-existent id in the wallet
let id3 = WalletAccountId::new("phony".to_string());
assert!(matches!(
remove_wallet_login_information_at_file(wallet_file.clone(), &id3),
Err(BackendError::NoSuchIdInWallet),
));
// Delete the second account
remove_wallet_login_information_at_file(wallet_file.clone(), &id2).unwrap();
// The first account should be unchanged
let loaded_account =
load_existing_wallet_login_information_at_file(wallet_file.clone(), &id1, &password).unwrap();
let StoredAccount::Mnemonic(ref acc1) = loaded_account;
assert_eq!(&dummy_account1, acc1.mnemonic());
assert_eq!(&cosmos_hd_path, acc1.hd_path());
// Delete the first account
assert!(wallet_file.exists());
remove_wallet_login_information_at_file(wallet_file.clone(), &id1).unwrap();
// The file should now be removed
assert!(!wallet_file.exists());
}
}
@@ -1,6 +1,8 @@
// Copyright 2022 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use zeroize::Zeroize;
@@ -19,6 +21,12 @@ impl AsRef<str> for WalletAccountId {
}
}
impl fmt::Display for WalletAccountId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
// simple wrapper for String that will get zeroized on drop
#[derive(Zeroize)]
#[zeroize(drop)]
+6 -20
View File
@@ -4,10 +4,9 @@ import { useSnackbar } from 'notistack';
import { Account, Network, TCurrency, TMixnodeBondDetails } from '../types';
import { TUseuserBalance, useGetBalance } from '../hooks/useGetBalance';
import { config } from '../../config';
import { getMixnodeBondDetails, selectNetwork, signInWithMnemonic, signInWithPassword, signOut } from '../requests';
import { getMixnodeBondDetails, selectNetwork, signInWithMnemonic, signOut } from '../requests';
import { currencyMap } from '../utils';
import { Console } from '../utils/console';
import { TLoginType } from 'src/pages/welcome/types';
export const { ADMIN_ADDRESS, IS_DEV_MODE } = config;
@@ -33,14 +32,11 @@ type TClientContext = {
currency?: TCurrency;
isLoading: boolean;
error?: string;
setIsLoading: (isLoading: boolean) => void;
setError: (value?: string) => void;
switchNetwork: (network: Network) => void;
getBondDetails: () => Promise<void>;
handleShowSettings: () => void;
handleShowAdmin: () => void;
logIn: (opts: { type: 'mnemonic' | 'password'; value: string }) => void;
signInWithPassword: (password: string) => void;
logIn: (mnemonic: string) => void;
logOut: () => void;
};
@@ -94,23 +90,16 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode
refreshAccount();
}, [network]);
const logIn = async ({ type, value }: { type: TLoginType; value: string }) => {
if (value.length === 0) {
setError(`A ${type} must be provided`);
return;
}
const logIn = async (mnemonic: string) => {
try {
setIsLoading(true);
if (type === 'mnemonic') {
await signInWithMnemonic(value);
} else {
await signInWithPassword(value);
}
await signInWithMnemonic(mnemonic || '');
await getBondDetails();
setNetwork('MAINNET');
} catch (e) {
setIsLoading(false);
setError(e as string);
} finally {
setIsLoading(false);
history.push('/balance');
}
};
@@ -142,9 +131,6 @@ export const ClientContextProvider = ({ children }: { children: React.ReactNode
showSettings,
network,
currency,
setIsLoading,
setError,
signInWithPassword,
switchNetwork,
getBondDetails,
handleShowSettings,
+1 -4
View File
@@ -10,7 +10,6 @@ import { Admin, Welcome, Settings } from './pages';
import { ErrorFallback } from './components';
import { NymWalletTheme, WelcomeTheme } from './theme';
import { maximizeWindow } from './utils';
import { SignInProvider } from './pages/welcome/context';
const App = () => {
const { clientDetails } = useContext(ClientContext);
@@ -21,9 +20,7 @@ const App = () => {
return !clientDetails ? (
<WelcomeTheme>
<SignInProvider>
<Welcome />
</SignInProvider>
<Welcome />
</WelcomeTheme>
) : (
<NymWalletTheme>
@@ -16,9 +16,6 @@ export const SendConfirmation = ({
isLoading: boolean;
}) => {
const { userBalance, currency, network } = useContext(ClientContext);
if (!data && !error && !isLoading) return null;
return (
<Box
sx={{
+8 -13
View File
@@ -128,9 +128,13 @@ export const SendWizard = () => {
px: 3,
}}
>
{activeStep === 0 && <SendForm />}
{activeStep === 1 && <SendReview transferFee={transferFee} />}
<SendConfirmation data={confirmedData} isLoading={isLoading} error={requestError} />
{activeStep === 0 ? (
<SendForm />
) : activeStep === 1 ? (
<SendReview transferFee={transferFee} />
) : (
<SendConfirmation data={confirmedData} isLoading={isLoading} error={requestError} />
)}
</Box>
<Box
sx={{
@@ -150,16 +154,7 @@ export const SendWizard = () => {
color="primary"
disableElevation
data-testid="button"
onClick={() => {
switch (activeStep) {
case 0:
return handleNextStep();
case 1:
return handleSend();
default:
return handleFinish();
}
}}
onClick={activeStep === 0 ? handleNextStep : activeStep === 1 ? handleSend : handleFinish}
disabled={!!(methods.formState.errors.amount || methods.formState.errors.to || isLoading)}
size="large"
>
@@ -139,13 +139,13 @@ export const SystemVariables = ({
pt: 0,
}}
>
{nodeUpdateResponse === 'success' && (
{nodeUpdateResponse === 'success' ? (
<Typography sx={{ color: 'success.main', fontWeight: 600 }}>Node successfully updated</Typography>
)}
{nodeUpdateResponse === 'failed' && (
) : nodeUpdateResponse === 'failed' ? (
<Typography sx={{ color: 'error.main', fontWeight: 600 }}>Node update failed</Typography>
) : (
<Fee feeType="UpdateMixnodeConfig" />
)}
{!nodeUpdateResponse && <Fee feeType="UpdateMixnodeConfig" />}
<Button
variant="contained"
color="primary"
@@ -17,7 +17,7 @@ export const SignInContent: React.FC = () => {
setInputError(undefined);
try {
await logIn({ type: 'mnemonic', value: mnemonic });
await logIn(mnemonic || '');
setIsLoading(false);
} catch (error: any) {
setIsLoading(false);
@@ -1,8 +0,0 @@
import React from 'react';
import { Alert } from '@mui/material';
export const Error = ({ message }: { message: string }) => (
<Alert severity="error" variant="outlined" data-testid="error" sx={{ color: 'error.light', width: '100%' }}>
{message}
</Alert>
);
@@ -2,6 +2,3 @@ export * from './heading';
export * from './word-tiles';
export * from './render-page';
export * from './password-strength';
export * from './error';
export * from './textfields';
export * from './step';
@@ -5,8 +5,8 @@ import { LinearProgress, Stack, Typography, Box } from '@mui/material';
type TStrength = 'weak' | 'medium' | 'strong' | 'init';
const strong = /^(?=.*[a-z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/;
const medium = /^(((?=.*[a-z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[0-9])))(?=.{6,})/;
const strong = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/;
const medium = /^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})/;
const colorMap = {
init: 'inherit' as 'inherit',
@@ -41,24 +41,7 @@ const getTextColor = (strength: TStrength) => {
}
};
const getPasswordStrength = (strength: TStrength) => {
switch (strength) {
case 'strong':
return 100;
case 'medium':
return 50;
default:
return 0;
}
};
export const PasswordStrength = ({
password,
onChange,
}: {
password: string;
onChange: (isStrong: boolean) => void;
}) => {
export const PasswordStrength = ({ password }: { password: string }) => {
const [strength, setStrength] = useState<TStrength>('init');
useEffect(() => {
@@ -79,17 +62,13 @@ export const PasswordStrength = ({
setStrength('weak');
}, [password]);
useEffect(() => {
if (strength === 'strong') {
onChange(true);
} else {
onChange(false);
}
}, [strength]);
return (
<Stack spacing={0.5}>
<LinearProgress variant="determinate" color={colorMap[strength]} value={getPasswordStrength(strength)} />
<LinearProgress
variant="determinate"
color={colorMap[strength]}
value={strength === 'strong' ? 100 : strength === 'medium' ? 50 : 0}
/>
<Box display="flex" alignItems="center">
<LockOutlined sx={{ fontSize: 15, color: getTextColor(strength) }} />
<Typography variant="caption" sx={{ ml: 0.5, color: getTextColor(strength) }}>
@@ -1,27 +0,0 @@
import React, { useCallback } from 'react';
import { Typography } from '@mui/material';
import { TPages } from '../types';
export const Step = ({ currentPage, totalSteps }: { currentPage: TPages; totalSteps: number }) => {
const mapPage = useCallback(() => {
switch (currentPage) {
case 'create mnemonic':
return 1;
case 'verify mnemonic':
return 2;
case 'create password':
return 3;
default:
return 0;
}
}, [currentPage]);
if (mapPage() === 0) {
return null;
}
return (
<Typography sx={{ color: 'grey.400' }}>
Create account. Step {mapPage()}/{totalSteps}
</Typography>
);
};
@@ -1,78 +0,0 @@
import React, { useState } from 'react';
import { Box, IconButton, Link, Stack, TextField, Typography } from '@mui/material';
import { Visibility, VisibilityOff } from '@mui/icons-material';
import { Error } from './error';
export const MnemonicInput: React.FC<{
mnemonic: string;
error?: string;
onUpdateMnemonic: (mnemonic: string) => void;
}> = ({ mnemonic, error, onUpdateMnemonic }) => {
const [showPassword, setShowPassword] = useState(false);
return (
<Stack spacing={2}>
<TextField
label="Mnemonic"
type={showPassword ? 'input' : 'password'}
value={mnemonic}
onChange={(e) => onUpdateMnemonic(e.target.value)}
multiline={!!showPassword}
rows={4}
autoFocus
fullWidth
InputProps={{
endAdornment: (
<IconButton onClick={() => setShowPassword((show) => !show)}>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
),
}}
/>
{error && <Error message={error} />}
</Stack>
);
};
export const PasswordInput: React.FC<{
password: string;
error?: string;
label: string;
showForgottenPassword?: boolean;
autoFocus?: boolean;
onUpdatePassword: (password: string) => void;
}> = ({ password, label, error, showForgottenPassword, autoFocus, onUpdatePassword }) => {
const [showPassword, setShowPassword] = useState(false);
return (
<Stack spacing={2}>
<Box>
<TextField
label={label}
fullWidth
value={password}
onChange={(e) => onUpdatePassword(e.target.value)}
type={showPassword ? 'input' : 'password'}
autoFocus={autoFocus}
InputProps={{
endAdornment: (
<IconButton onClick={() => setShowPassword((show) => !show)}>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
),
}}
/>
{showForgottenPassword && (
<Link
underline="none"
variant="body2"
component="div"
sx={{ mt: 1, textAlign: 'right', color: 'info.main', cursor: 'pointer' }}
>
Forgotten password?
</Link>
)}
</Box>
{error && <Error message={error} />}
</Stack>
);
};
@@ -7,19 +7,17 @@ export const WordTile = ({
index,
disabled,
onClick,
button,
}: {
mnemonicWord: string;
index?: number;
disabled?: boolean;
onClick?: boolean;
button?: boolean;
}) => (
<Card
variant="outlined"
sx={{
background: button ? '#151A2C' : 'transparent',
border: button ? '1px solid #3A4053' : 'none',
background: '#151A2C',
border: '1px solid #3A4053',
cursor: onClick ? 'pointer' : 'default',
opacity: disabled ? 0.2 : 1,
}}
@@ -42,12 +40,10 @@ export const WordTiles = ({
mnemonicWords,
showIndex,
onClick,
buttons,
}: {
mnemonicWords?: TMnemonicWords;
showIndex?: boolean;
onClick?: ({ name, index }: { name: string; index: number }) => void;
buttons?: boolean;
}) => {
if (mnemonicWords) {
return (
@@ -59,7 +55,6 @@ export const WordTiles = ({
index={showIndex ? index : undefined}
onClick={!!onClick}
disabled={disabled}
button={buttons}
/>
</Grid>
))}
@@ -1,72 +0,0 @@
import React, { createContext, useEffect, useMemo, useState } from 'react';
import { createMnemonic, signInWithMnemonic } from 'src/requests';
import { TMnemonicWords } from '../types';
export const SignInContext = createContext({} as TSignInContent);
export type TSignInContent = {
error?: string;
password: string;
mnemonic: string;
mnemonicWords: TMnemonicWords;
setError: (err?: string) => void;
setMnemonic: (mnc: string) => void;
generateMnemonic: () => Promise<void>;
validateMnemonic: () => Promise<void>;
setPassword: (paswd: string) => void;
};
const mnemonicToArray = (mnemonic: string): TMnemonicWords =>
mnemonic
.split(' ')
.reduce((a, c: string, index) => [...a, { name: c, index: index + 1, disabled: false }], [] as TMnemonicWords);
export const SignInProvider: React.FC = ({ children }) => {
const [password, setPassword] = useState('');
const [mnemonic, setMnemonic] = useState('');
const [mnemonicWords, setMnemonicWords] = useState<TMnemonicWords>([]);
const [error, setError] = useState<string>();
const generateMnemonic = async () => {
const mnemonicPhrase = await createMnemonic();
setMnemonic(mnemonicPhrase);
};
const validateMnemonic = async () => {
try {
await signInWithMnemonic(mnemonic);
} catch (e) {
setError(e as string);
}
};
useEffect(() => {
if (mnemonic.length > 0) {
const mnemonicArray = mnemonicToArray(mnemonic);
setMnemonicWords(mnemonicArray);
} else {
setMnemonicWords([]);
}
}, [mnemonic]);
return (
<SignInContext.Provider
value={useMemo(
() => ({
error,
password,
mnemonic,
mnemonicWords,
setError,
setMnemonic,
generateMnemonic,
validateMnemonic,
setPassword,
}),
[error, password, mnemonic, mnemonicWords],
)}
>
{children}
</SignInContext.Provider>
);
};
+38 -42
View File
@@ -1,24 +1,31 @@
import React, { useContext, useState } from 'react';
import { Stack, Box, CircularProgress } from '@mui/material';
import { NymWordmark } from '@nymproject/react';
import {
CreatePassword,
ExistingAccount,
CreateMnemonic,
VerifyMnemonic,
WelcomeContent,
SignInMnemonic,
} from './pages';
import { TLoginType, TPages } from './types';
import { RenderPage, Step } from './components';
import { NymLogo } from '@nymproject/react';
import { CircularProgress, Stack, Box } from '@mui/material';
import { ExistingAccount, WelcomeContent } from './pages';
import { TPages } from './types';
import { RenderPage } from './components';
import { CreateAccountContent } from './_legacy_create-account';
import { ClientContext } from '../../context/main';
import { SignInPassword } from './pages/signin-password';
// const testMnemonic =
// 'futuristic big receptive caption saw hug odd spoon internal dime bike rake helpless left distribution gusty eyes beg enormous word influence trashy pets curl';
//
// const mnemonicToArray = (mnemonic: string): TMnemonicWords =>
// mnemonic
// .split(' ')
// .reduce((a, c: string, index) => [...a, { name: c, index: index + 1, disabled: false }], [] as TMnemonicWords);
export const Welcome = () => {
const [page, setPage] = useState<TPages>('welcome');
const [loginType, setLoginType] = useState<TLoginType>('mnemonic');
// const [mnemonicWords, setMnemonicWords] = useState<TMnemonicWords>();
const { isLoading } = useContext(ClientContext);
// useEffect(() => {
// const mnemonicArray = mnemonicToArray(testMnemonic)
// setMnemonicWords(mnemonicArray)
// }, [])
return (
<Box
sx={{
@@ -43,39 +50,28 @@ export const Welcome = () => {
<CircularProgress size={72} />
) : (
<Stack spacing={3} alignItems="center" sx={{ width: 1080 }}>
<NymWordmark width={75} />
<Step currentPage={page} totalSteps={3} />
<NymLogo width={75} />
<RenderPage page={page}>
<WelcomeContent
onUseExisting={() => setPage('existing account')}
onCreateAccount={() => setPage('create mnemonic')}
onCreateAccountComplete={() => setPage('legacy create account')}
page="welcome"
/>
<CreateMnemonic
onNext={() => setPage('verify mnemonic')}
onPrev={() => setPage('create password')}
page="create mnemonic"
/>
<VerifyMnemonic onNext={() => setPage('create password')} onPrev={() => {}} page="verify mnemonic" />
<CreatePassword
onSkip={() => {
setLoginType('mnemonic');
setPage('existing account');
}}
onNext={() => {
setLoginType('password');
setPage('existing account');
}}
page="create password"
/>
<ExistingAccount
onPrev={() => setPage('welcome')}
page="existing account"
loginType={loginType}
setLoginType={(loginType) => setLoginType(loginType)}
/>
<SignInMnemonic onPrev={() => setPage('welcome')} page="sign in with mnemonic" />
<SignInPassword onPrev={() => setPage('welcome')} page="sign in with password" />
<CreateAccountContent page="legacy create account" showSignIn={() => setPage('existing account')} />
{/* <MnemonicWords
mnemonicWords={mnemonicWords}
onNext={() => setPage('verify mnemonic')}
onPrev={() => setPage('welcome')}
page="create account"
/>
<VerifyMnemonic
mnemonicWords={mnemonicWords}
onComplete={() => setPage('create password')}
page="verify mnemonic"
/>
<CreatePassword page="create password" /> */}
<ExistingAccount onPrev={() => setPage('welcome')} page="existing account" />
</RenderPage>
</Stack>
)}
@@ -1,60 +0,0 @@
import React, { useContext, useEffect } from 'react';
import { Alert, Button, Grid, Stack, Typography } from '@mui/material';
import { Check, ContentCopySharp } from '@mui/icons-material';
import { useClipboard } from 'use-clipboard-copy';
import { WordTiles } from '../components';
import { TPages } from '../types';
import { SignInContext } from '../context';
export const CreateMnemonic = ({ onNext }: { page: TPages; onNext: () => void; onPrev: () => void }) => {
const { mnemonic, mnemonicWords, generateMnemonic, validateMnemonic, setMnemonic, setError } =
useContext(SignInContext);
useEffect(() => {
generateMnemonic();
}, []);
const { copy, copied } = useClipboard({ copiedTimeout: 5000 });
return (
<Stack alignItems="center" spacing={3}>
<Typography sx={{ color: 'common.white', fontWeight: 600 }} textAlign="center">
Write down your mnemonic
</Typography>
<Alert variant="outlined" severity="warning" sx={{ textAlign: 'center' }}>
<Typography>Below is your 24 word mnemonic, please store the mnemonic in a safe place.</Typography>
<Typography>This is the only way to access your wallet!</Typography>
</Alert>
<WordTiles mnemonicWords={mnemonicWords} showIndex />
<Button
color="inherit"
disableElevation
size="large"
onClick={() => {
copy(mnemonic);
}}
sx={{
width: 250,
}}
endIcon={!copied ? <ContentCopySharp /> : <Check color="success" />}
>
Copy mnemonic
</Button>
<Button
variant="contained"
color="primary"
disableElevation
size="large"
onClick={onNext}
sx={{ width: 250 }}
disabled={!copied}
>
I saved my mnemonic
</Button>
</Stack>
);
};
@@ -1,74 +1,60 @@
import React, { useContext, useState } from 'react';
import { Alert, Button, FormControl, Stack } from '@mui/material';
import { useSnackbar } from 'notistack';
import { TPages } from '../types';
import React, { useState } from 'react';
import { Button, FormControl, Grid, IconButton, Stack, TextField } from '@mui/material';
import { VisibilityOff, Visibility } from '@mui/icons-material';
import { Subtitle, Title, PasswordStrength } from '../components';
import { PasswordInput } from '../components/textfields';
import { SignInContext } from '../context';
import { createPassword } from '../../../requests';
export const CreatePassword = ({ onSkip, onNext }: { page: TPages; onNext: () => void; onSkip: () => void }) => {
const { password, setPassword } = useContext(SignInContext);
const [confirmedPassword, setConfirmedPassword] = useState<string>('');
const [isStrongPassword, setIsStrongPassword] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const { mnemonic } = useContext(SignInContext);
const handleSkip = () => {
setPassword('');
onSkip();
};
const { enqueueSnackbar } = useSnackbar();
const storePassword = async () => {
try {
setIsLoading(true);
await createPassword({ mnemonic, password });
enqueueSnackbar('Password successfully created', { variant: 'success' });
setPassword('');
onNext();
} catch (e) {
enqueueSnackbar(e as string, { variant: 'error' });
} finally {
setIsLoading(false);
}
};
export const CreatePassword = () => {
const [password, setPassword] = useState<string>('');
const [confirmedPassword, setConfirmedPassword] = useState<string>();
const [showPassword, setShowPassword] = useState(false);
const [showConfirmedPassword, setShowConfirmedPassword] = useState(false);
return (
<Stack spacing={3} alignItems="center" minWidth="50%">
<Title title="Create optional password" />
<Subtitle subtitle="Password should be min 8 characters, at least one number and one symbol" />
<FormControl fullWidth>
<Stack spacing={2}>
<>
<PasswordInput
password={password}
onUpdatePassword={(pswd) => setPassword(pswd)}
label="Password"
autoFocus
/>
<PasswordStrength password={password} onChange={(isStrong) => setIsStrongPassword(isStrong)} />
</>
<PasswordInput
password={confirmedPassword}
onUpdatePassword={(pswd) => setConfirmedPassword(pswd)}
label="Confirm password"
/>
<Button
size="large"
variant="contained"
disabled={password !== confirmedPassword || password.length === 0 || !isStrongPassword || isLoading}
onClick={storePassword}
>
Next
</Button>
<Button size="large" color="info" onClick={handleSkip}>
Skip and sign in with mnemonic
</Button>
</Stack>
</FormControl>
</Stack>
<>
<Title title="Create password" />
<Subtitle subtitle="Create a strong password. Min 8 characters, at least one capital letter, number and special symbol" />
<Grid container justifyContent="center">
<Grid item xs={6}>
<FormControl fullWidth>
<Stack spacing={2}>
<TextField
label="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
type={showPassword ? 'input' : 'password'}
InputProps={{
endAdornment: (
<IconButton onClick={() => setShowPassword((show) => !show)}>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
),
}}
/>
<PasswordStrength password={password} />
<TextField
label="Confirm password"
value={confirmedPassword}
onChange={(e) => setConfirmedPassword(e.target.value)}
type={showConfirmedPassword ? 'input' : 'password'}
InputProps={{
endAdornment: (
<IconButton onClick={() => setShowConfirmedPassword((show) => !show)}>
{showConfirmedPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
),
}}
/>
<Button
size="large"
variant="contained"
disabled={password !== confirmedPassword || password.length === 0}
>
Next
</Button>
</Stack>
</FormControl>
</Grid>
</Grid>
</>
);
};

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